Bibliography Xblock
authorJason Skomorowski <jason@indexdata.com>
Tue, 19 Aug 2014 21:13:19 +0000 (17:13 -0400)
committerJason Skomorowski <jason@indexdata.com>
Tue, 19 Aug 2014 21:13:19 +0000 (17:13 -0400)
16 files changed:
.gitignore
mkwsbiblio/mkwsbiblio/.gitignore [new file with mode: 0644]
mkwsbiblio/mkwsbiblio/__init__.py [new file with mode: 0644]
mkwsbiblio/mkwsbiblio/mkwsbiblio.py [new file with mode: 0644]
mkwsbiblio/mkwsbiblio/settings.js [new file with mode: 0644]
mkwsbiblio/mkwsbiblio/static/html/.student.html.swo [new file with mode: 0644]
mkwsbiblio/mkwsbiblio/static/html/settings.html [new file with mode: 0644]
mkwsbiblio/mkwsbiblio/static/html/student.html [new file with mode: 0644]
mkwsbiblio/mkwsbiblio/static/js/settings.js [new file with mode: 0644]
mkwsbiblio/mkwsbiblio/static/js/src/EdxChooser.handlebars [new file with mode: 0644]
mkwsbiblio/mkwsbiblio/static/js/src/Summary.handlebars [new file with mode: 0644]
mkwsbiblio/mkwsbiblio/static/js/src/buildsetting.sh [new file with mode: 0644]
mkwsbiblio/mkwsbiblio/static/js/src/settings.js [new file with mode: 0644]
mkwsbiblio/mkwsbiblio/static/js/src/student.js [new file with mode: 0644]
mkwsbiblio/requirements.txt [new file with mode: 0644]
mkwsbiblio/setup.py [new file with mode: 0644]

index 665e2d7..02581fe 100644 (file)
@@ -1,3 +1,4 @@
 *.pyc
 *.swp
+*.swo
 mkwsxb_xblock.egg-info/
diff --git a/mkwsbiblio/mkwsbiblio/.gitignore b/mkwsbiblio/mkwsbiblio/.gitignore
new file mode 100644 (file)
index 0000000..2854ec4
--- /dev/null
@@ -0,0 +1 @@
+settings/static/settings.js
diff --git a/mkwsbiblio/mkwsbiblio/__init__.py b/mkwsbiblio/mkwsbiblio/__init__.py
new file mode 100644 (file)
index 0000000..6536d93
--- /dev/null
@@ -0,0 +1 @@
+from .mkwsbiblio import MKWSBiblio
diff --git a/mkwsbiblio/mkwsbiblio/mkwsbiblio.py b/mkwsbiblio/mkwsbiblio/mkwsbiblio.py
new file mode 100644 (file)
index 0000000..83bc12a
--- /dev/null
@@ -0,0 +1,75 @@
+"""Embed bibliographic widget from MKWS, the MasterKey Widget Set"""
+
+import pkg_resources
+import random
+
+from xblock.core import XBlock
+from xblock.fields import Integer, Scope, String, Any, Boolean, Dict
+from xblock.fragment import Fragment
+
+class MKWSBiblio(XBlock):
+    """Embed bibliographic widget from MKWS, the MasterKey Widget Set"""
+
+    # Fields
+    query = String(
+      help="Search query",
+      default="water",
+      scope=Scope.content
+    )
+    display_name = String(
+      default="MKWS bibliographic details",
+      scope=Scope.settings
+    )
+
+    def resource_string(self, path):
+        """Helper for accessing resources."""
+        data = pkg_resources.resource_string(__name__, path)
+        return data.decode("utf8")
+
+    def student_view(self, context=None):
+        """The primary view of the MKWS XBlock, shown to students when viewing courses."""
+        html = self.resource_string("static/html/student.html")
+        frag = Fragment(html.format(query=self.query, team=random.randint(0, 100000)))
+        # student.js uses require.js as it cannot guarantee mkws-complete.js has loaded 
+        # in studio without it. We'll need to add it if we're in the LMS:
+        frag.add_javascript_url("/static/js/vendor/require.js");
+        # frag.add_resource_url("//mkws.indexdata.com/mkws-complete", "text/javascript", "head");
+        # frag.add_resource('<script src="//mkws.indexdata.com/mkws-complete.js"></script>', "text/html", "head");
+        frag.add_javascript(self.resource_string("static/js/src/student.js"))
+        frag.initialize_js('MKWSBiblio')
+        return frag;
+
+    def author_view(self, context=None):
+        """The primary view of the MKWS XBlock, shown when authoring courses."""
+        # This should closely mirror the student_view. Here all we do is not include
+        # require.js as it's already in Studio and the lms path won't work.
+        html = self.resource_string("static/html/student.html")
+        frag = Fragment(html.format(query=self.query, team=random.randint(0, 100000)))
+        frag.add_javascript(self.resource_string("static/js/src/student.js"))
+        frag.initialize_js('MKWSBiblio')
+        return frag;
+
+    def studio_view(self, context=None):
+        """Studio configuration view."""
+        html = self.resource_string("static/html/settings.html")
+        frag = Fragment(html.format(query=self.query))
+        frag.add_javascript(self.resource_string("static/js/settings.js"))
+        frag.initialize_js('MKWSBiblioSettings')
+        return frag
+
+    @XBlock.json_handler
+    def update_settings(self, data, suffix=''):
+        """Studio configuration callback."""
+        self.query = data['query']
+        return {"result": "success"}
+
+    @staticmethod
+    def workbench_scenarios():
+        """A canned scenario for display in the workbench."""
+        return [
+            ("MKWSBiblio",
+             """<vertical_demo>
+                <mkwsbiblio/>
+                </vertical_demo>
+             """),
+        ]
diff --git a/mkwsbiblio/mkwsbiblio/settings.js b/mkwsbiblio/mkwsbiblio/settings.js
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/mkwsbiblio/mkwsbiblio/static/html/.student.html.swo b/mkwsbiblio/mkwsbiblio/static/html/.student.html.swo
new file mode 100644 (file)
index 0000000..cc8c071
Binary files /dev/null and b/mkwsbiblio/mkwsbiblio/static/html/.student.html.swo differ
diff --git a/mkwsbiblio/mkwsbiblio/static/html/settings.html b/mkwsbiblio/mkwsbiblio/static/html/settings.html
new file mode 100644 (file)
index 0000000..6e0ab63
--- /dev/null
@@ -0,0 +1,28 @@
+<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" id="mkwsCurrentRecordContainer">
+        <h3>Current selection:</h3>
+        <div class="mkwsRecords mkwsTeam_UNIQUE" id="mkwsCurrentRecord" autosearch="{query}" perpage="1">Loading record...</div>
+      </div>
+    </li>
+    <li class="field comp-setting-entry is-set">
+      <div class="wrapper-comp-setting">
+        <h3>Choose a record:</h3>
+        <div class="mkwsSearch mkwsTeam_BlockConfig"></div>
+        <!-- NB: double braces needed to escape edx templateing -->
+        <div class="mkwsRecords mkwsTeam_BlockConfig" data-mkws-config='{{ "template": "EdxChooser", "perpage": 5 }}'></div>
+      </div>
+    </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>
diff --git a/mkwsbiblio/mkwsbiblio/static/html/student.html b/mkwsbiblio/mkwsbiblio/static/html/student.html
new file mode 100644 (file)
index 0000000..6df95ba
--- /dev/null
@@ -0,0 +1,3 @@
+<div class="mkwsbiblio_block">
+  <div class="mkwsRecords mkwsTeam_{team}" autosearch="{query}" perpage="1">Loading record...</div>
+</div>
diff --git a/mkwsbiblio/mkwsbiblio/static/js/settings.js b/mkwsbiblio/mkwsbiblio/static/js/settings.js
new file mode 100644 (file)
index 0000000..f238e6c
--- /dev/null
@@ -0,0 +1,94 @@
+(function() {
+  var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
+templates['EdxChooser'] = template(function (Handlebars,depth0,helpers,partials,data) {
+  this.compilerInfo = [4,'>= 1.0.0'];
+helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
+  var buffer = "", stack1, functionType="function", escapeExpression=this.escapeExpression, self=this;
+
+function program1(depth0,data) {
+  
+  var buffer = "", stack1, helper;
+  buffer += "\n  <div class=\"";
+  if (helper = helpers.containerClass) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0.containerClass); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "\">\n    <a href=\"#\" id=\"";
+  if (helper = helpers.detailLinkId) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0.detailLinkId); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "\" onclick=\"$('#mkwsCurrentRecord').attr('autosearch', '";
+  if (helper = helpers['md-title']) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0['md-title']); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "'); mkws.init('Click select', $('#mkwsCurrentRecordContainer'));\">\n      <b>";
+  if (helper = helpers['md-title']) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0['md-title']); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "</b>\n    </a>\n    ";
+  stack1 = helpers['if'].call(depth0, (depth0 && depth0['md-title-remainder']), {hash:{},inverse:self.noop,fn:self.program(2, program2, data),data:data});
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n    ";
+  stack1 = helpers['if'].call(depth0, (depth0 && depth0['md-title-responsibility']), {hash:{},inverse:self.noop,fn:self.program(4, program4, data),data:data});
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n    ";
+  stack1 = helpers['if'].call(depth0, (depth0 && depth0.renderedDetails), {hash:{},inverse:self.noop,fn:self.program(6, program6, data),data:data});
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n  </div>\n";
+  return buffer;
+  }
+function program2(depth0,data) {
+  
+  var buffer = "", stack1, helper;
+  buffer += "\n      <span>";
+  if (helper = helpers['md-title-remainder']) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0['md-title-remainder']); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "</span>\n    ";
+  return buffer;
+  }
+
+function program4(depth0,data) {
+  
+  var buffer = "", stack1, helper;
+  buffer += "\n      <span><i>";
+  if (helper = helpers['md-title-responsibility']) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0['md-title-responsibility']); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "</i></span>\n    ";
+  return buffer;
+  }
+
+function program6(depth0,data) {
+  
+  var buffer = "", stack1, helper;
+  buffer += "\n      ";
+  if (helper = helpers.renderedDetails) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0.renderedDetails); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n    ";
+  return buffer;
+  }
+
+  buffer += "\n";
+  stack1 = helpers.each.call(depth0, (depth0 && depth0.hits), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n";
+  return buffer;
+  });
+})();
+function MKWSBiblioSettings(runtime, element) {
+  mkws.init("XBlock settings pane", "#settings-tab");
+  $(element).find('.save-button').bind('click', function() {
+    var handlerUrl = runtime.handlerUrl(element, 'update_settings');
+    var data = {
+      query: $(element).find('#mkwsCurrentRecord').attr('autosearch')
+    };
+    $.post(handlerUrl, JSON.stringify(data)).done(function(response) {
+      window.location.reload(false);
+    });
+  });
+
+  $(element).find('.cancel-button').bind('click', function() {
+    runtime.notify('cancel', {});
+  });
+};
diff --git a/mkwsbiblio/mkwsbiblio/static/js/src/EdxChooser.handlebars b/mkwsbiblio/mkwsbiblio/static/js/src/EdxChooser.handlebars
new file mode 100644 (file)
index 0000000..e3f8656
--- /dev/null
@@ -0,0 +1,26 @@
+{{!
+Records from a search.
+
+hits:
+  containerClass - class  attribute for same
+  detailLinkId - id for the element triggering detail display
+  detailClick - a click event handler for details
+  renderedDetails - active record details rendered from the Record template
+  md-* - metadata fields passed through from backend
+}}
+{{#each hits}}
+  <div class="{{containerClass}}">
+    <a href="#" id="{{detailLinkId}}" onclick="$('#mkwsCurrentRecord').attr('autosearch', '{{md-title}}'); mkws.init('Click select', $('#mkwsCurrentRecordContainer'));">
+      <b>{{md-title}}</b>
+    </a>
+    {{#if md-title-remainder}}
+      <span>{{md-title-remainder}}</span>
+    {{/if}}
+    {{#if md-title-responsibility}}
+      <span><i>{{md-title-responsibility}}</i></span>
+    {{/if}}
+    {{#if renderedDetails}}
+      {{{renderedDetails}}}
+    {{/if}}
+  </div>
+{{/each}}
diff --git a/mkwsbiblio/mkwsbiblio/static/js/src/Summary.handlebars b/mkwsbiblio/mkwsbiblio/static/js/src/Summary.handlebars
new file mode 100644 (file)
index 0000000..bd5f893
--- /dev/null
@@ -0,0 +1,6 @@
+      {{#if md-author}}
+        <span>{{md-author}}</span>
+      {{/if}}
+      <a href="#" id="{{_id}}" onclick="{{_onclick}}">
+        <b>WOO {{md-title}}</b>
+      </a>
diff --git a/mkwsbiblio/mkwsbiblio/static/js/src/buildsetting.sh b/mkwsbiblio/mkwsbiblio/static/js/src/buildsetting.sh
new file mode 100644 (file)
index 0000000..43ebb64
--- /dev/null
@@ -0,0 +1 @@
+handlebars Records.handlebars | cat - settings.js > ../settings.js
diff --git a/mkwsbiblio/mkwsbiblio/static/js/src/settings.js b/mkwsbiblio/mkwsbiblio/static/js/src/settings.js
new file mode 100644 (file)
index 0000000..3d8e2d7
--- /dev/null
@@ -0,0 +1,16 @@
+function MKWSBiblioSettings(runtime, element) {
+  mkws.init("XBlock settings pane", "#settings-tab");
+  $(element).find('.save-button').bind('click', function() {
+    var handlerUrl = runtime.handlerUrl(element, 'update_settings');
+    var data = {
+      query: $(element).find('#mkwsCurrentRecord').attr('autosearch')
+    };
+    $.post(handlerUrl, JSON.stringify(data)).done(function(response) {
+      window.location.reload(false);
+    });
+  });
+
+  $(element).find('.cancel-button').bind('click', function() {
+    runtime.notify('cancel', {});
+  });
+};
diff --git a/mkwsbiblio/mkwsbiblio/static/js/src/student.js b/mkwsbiblio/mkwsbiblio/static/js/src/student.js
new file mode 100644 (file)
index 0000000..406407c
--- /dev/null
@@ -0,0 +1,18 @@
+window.mkws_noready = true;
+require.config({
+  paths: {
+    mkws: "//mkws.local/mkws-complete",
+  },
+  shim: {
+    mkws: {
+      exports: "mkws"
+    },
+  }
+});
+function MKWSBiblio(runtime, element) {
+  require(['mkws'], function() { 
+    console.log(mkws);
+    mkws.init("XBlock initialised.", element);
+    //mkws.init("XBlock initialised.");
+  } );
+}
diff --git a/mkwsbiblio/requirements.txt b/mkwsbiblio/requirements.txt
new file mode 100644 (file)
index 0000000..d6e1198
--- /dev/null
@@ -0,0 +1 @@
+-e .
diff --git a/mkwsbiblio/setup.py b/mkwsbiblio/setup.py
new file mode 100644 (file)
index 0000000..bf89297
--- /dev/null
@@ -0,0 +1,37 @@
+"""Setup for mkwsbiblio 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='mkwsbiblio',
+    version='0.1',
+    description='XBlock to embed a bibliographic record via MKWS',
+    packages=[
+        'mkwsbiblio',
+    ],
+    install_requires=[
+        'XBlock',
+    ],
+    entry_points={
+        'xblock.v1': [
+            'mkwsbiblio = mkwsbiblio:MKWSBiblio',
+        ]
+    },
+    package_data=package_data("mkwsbiblio", ["static", "public"]),
+)