Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions common/lib/xmodule/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@

XMODULES = [
"book = xmodule.backcompat_module:TranslateCustomTagDescriptor",
"customtag = xmodule.template_module:CustomTagDescriptor",
"discuss = xmodule.backcompat_module:TranslateCustomTagDescriptor",
"image = xmodule.backcompat_module:TranslateCustomTagDescriptor",
"poll_question = xmodule.poll_module:PollDescriptor",
"section = xmodule.backcompat_module:SemanticSectionDescriptor",
"slides = xmodule.backcompat_module:TranslateCustomTagDescriptor",
"videodev = xmodule.backcompat_module:TranslateCustomTagDescriptor",
Expand All @@ -21,6 +19,7 @@
"conditional = xmodule.conditional_module:ConditionalBlock",
"course = xmodule.course_module:CourseBlock",
"course_info = xmodule.html_module:CourseInfoBlock",
"customtag = xmodule.template_module:CustomTagBlock",
"error = xmodule.error_module:ErrorBlock",
"hidden = xmodule.hidden_module:HiddenDescriptor",
"html = xmodule.html_module:HtmlBlock",
Expand All @@ -29,6 +28,7 @@
"library_sourced = xmodule.library_sourced_block:LibrarySourcedBlock",
"lti = xmodule.lti_module:LTIBlock",
"nonstaff_error = xmodule.error_module:NonStaffErrorBlock",
"poll_question = xmodule.poll_module:PollBlock",
"problem = xmodule.capa_module:ProblemBlock",
"problemset = xmodule.seq_module:SequenceBlock",
"randomize = xmodule.randomize_module:RandomizeBlock",
Expand Down
78 changes: 56 additions & 22 deletions common/lib/xmodule/xmodule/poll_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,38 @@
from copy import deepcopy

from pkg_resources import resource_string
from web_fragments.fragment import Fragment

from lxml import etree
from openedx.core.djangolib.markup import Text, HTML
from xblock.fields import Boolean, Dict, List, Scope, String # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.mako_module import MakoModuleDescriptor
from xmodule.mako_module import MakoTemplateBlockBase
from xmodule.stringify import stringify_children
from xmodule.x_module import XModule
from xmodule.xml_module import XmlDescriptor
from xmodule.util.xmodule_django import add_webpack_to_fragment
from xmodule.x_module import (
HTMLSnippet,
ResourceTemplates,
shim_xmodule_js,
XModuleMixin,
XModuleDescriptorToXBlockMixin,
XModuleToXBlockMixin,
)
from xmodule.xml_module import XmlMixin

log = logging.getLogger(__name__)
_ = lambda text: text


class PollFields(object): # lint-amnesty, pylint: disable=missing-class-docstring
class PollBlock(
MakoTemplateBlockBase,
XmlMixin,
XModuleDescriptorToXBlockMixin,
XModuleToXBlockMixin,
HTMLSnippet,
ResourceTemplates,
XModuleMixin,
): # pylint: disable=abstract-method
"""Poll Module"""
# Name of poll to use in links to this poll
display_name = String(
help=_("The display name for this component."),
Expand Down Expand Up @@ -61,18 +80,33 @@ class PollFields(object): # lint-amnesty, pylint: disable=missing-class-docstri
default=''
)

resources_dir = None
uses_xmodule_styles_setup = True

class PollModule(PollFields, XModule):
"""Poll Module"""
js = {
preview_view_js = {
'js': [
resource_string(__name__, 'js/src/javascript_loader.js'),
resource_string(__name__, 'js/src/poll/poll.js'),
resource_string(__name__, 'js/src/poll/poll_main.js')
]
],
'xmodule_js': resource_string(__name__, 'js/src/xmodule.js'),
}
preview_view_css = {
'scss': [
resource_string(__name__, 'css/poll/display.scss')
],
}

# There is no studio_view() for this XBlock but this is needed to make the
# the static_content command happy.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be possible to fix this but I am assuming it is better to leave deprecated code broken.

studio_view_js = {
'js': [],
'xmodule_js': resource_string(__name__, 'js/src/xmodule.js')
}

studio_view_css = {
'scss': []
}
css = {'scss': [resource_string(__name__, 'css/poll/display.scss')]}
js_module_name = "Poll"

def handle_ajax(self, dispatch, data): # lint-amnesty, pylint: disable=unused-argument
"""Ajax handler.
Expand Down Expand Up @@ -103,7 +137,7 @@ def handle_ajax(self, dispatch, data): # lint-amnesty, pylint: disable=unused-a
'total': sum(self.poll_answers.values())
})
elif dispatch == 'reset_poll' and self.voted and \
self.descriptor.xml_attributes.get('reset', 'True').lower() != 'false':
self.xml_attributes.get('reset', 'True').lower() != 'false':
self.voted = False

# FIXME: fix this, when xblock will support mutable types.
Expand All @@ -117,16 +151,21 @@ def handle_ajax(self, dispatch, data): # lint-amnesty, pylint: disable=unused-a
else: # return error message
return json.dumps({'error': 'Unknown Command!'})

def get_html(self):
"""Renders parameters to template."""
def student_view(self, _context):
"""
Renders the student view.
"""
fragment = Fragment()
params = {
'element_id': self.location.html_id(),
'element_class': self.location.block_type,
'ajax_url': self.system.ajax_url,
'ajax_url': self.ajax_url,
'configuration_json': self.dump_poll(),
}
self.content = self.system.render_template('poll.html', params) # lint-amnesty, pylint: disable=attribute-defined-outside-init
return self.content
fragment.add_content(self.system.render_template('poll.html', params))
add_webpack_to_fragment(fragment, 'PollBlockPreview')
shim_xmodule_js(fragment, 'Poll')
return fragment

def dump_poll(self):
"""Dump poll information.
Expand Down Expand Up @@ -160,17 +199,12 @@ def dump_poll(self):
'poll_answer': self.poll_answer,
'poll_answers': self.poll_answers if self.voted else {},
'total': sum(self.poll_answers.values()) if self.voted else 0,
'reset': str(self.descriptor.xml_attributes.get('reset', 'true')).lower()
'reset': str(self.xml_attributes.get('reset', 'true')).lower()
})


class PollDescriptor(PollFields, MakoModuleDescriptor, XmlDescriptor): # lint-amnesty, pylint: disable=missing-class-docstring
_tag_name = 'poll_question'
_child_tag_name = 'answer'

module_class = PollModule
resources_dir = None

@classmethod
def definition_from_xml(cls, xml_object, system):
"""Pull out the data into dictionary.
Expand Down
4 changes: 4 additions & 0 deletions common/lib/xmodule/xmodule/static_content.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@
from xmodule.html_module import AboutBlock, CourseInfoBlock, HtmlBlock, StaticTabBlock
from xmodule.library_content_module import LibraryContentBlock
from xmodule.lti_module import LTIBlock
from xmodule.poll_module import PollBlock
from xmodule.seq_module import SequenceBlock
from xmodule.split_test_module import SplitTestBlock
from xmodule.template_module import CustomTagBlock
from xmodule.word_cloud_module import WordCloudBlock
from xmodule.x_module import XModuleDescriptor, HTMLSnippet

Expand Down Expand Up @@ -74,9 +76,11 @@ class VideoBlock(HTMLSnippet): # lint-amnesty, pylint: disable=abstract-method
AnnotatableBlock,
ConditionalBlock,
CourseInfoBlock,
CustomTagBlock,
HtmlBlock,
LibraryContentBlock,
LTIBlock,
PollBlock,
ProblemBlock,
SequenceBlock,
SplitTestBlock,
Expand Down
73 changes: 60 additions & 13 deletions common/lib/xmodule/xmodule/template_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,32 @@
from string import Template

from lxml import etree
from xmodule.raw_module import RawDescriptor
from xmodule.x_module import XModule # lint-amnesty, pylint: disable=unused-import


class CustomTagModule(XModule):
from pkg_resources import resource_string
from web_fragments.fragment import Fragment
from xmodule.editing_module import EditingMixin
from xmodule.raw_module import RawMixin
from xmodule.util.xmodule_django import add_webpack_to_fragment
from xmodule.x_module import (
HTMLSnippet,
ResourceTemplates,
shim_xmodule_js,
XModuleMixin,
XModuleDescriptorToXBlockMixin,
XModuleToXBlockMixin,
)
from xmodule.xml_module import XmlMixin


class CustomTagBlock(
RawMixin,
XmlMixin,
EditingMixin,
XModuleDescriptorToXBlockMixin,
XModuleToXBlockMixin,
HTMLSnippet,
ResourceTemplates,
XModuleMixin,
): # pylint: disable=abstract-method
"""
This module supports tags of the form
<customtag option="val" option2="val2" impl="tagname"/>
Expand All @@ -31,17 +52,35 @@ class CustomTagModule(XModule):
Renders to::
More information given in <a href="/book/234">the text</a>
"""

def get_html(self):
return self.descriptor.rendered_html


class CustomTagDescriptor(RawDescriptor):
""" Descriptor for custom tags. Loads the template when created."""
module_class = CustomTagModule
resources_dir = None
template_dir_name = 'customtag'

preview_view_js = {
'js': [],
'xmodule_js': resource_string(__name__, 'js/src/xmodule.js'),
}
preview_view_css = {
'scss': [],
}
studio_view_js = {
'js': [resource_string(__name__, 'js/src/raw/edit/xml.js')],
'xmodule_js': resource_string(__name__, 'js/src/xmodule.js'),
}
studio_view_css = {
'scss': [resource_string(__name__, 'css/codemirror/codemirror.scss')],
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copied over from XMLEditingDescriptor.


def studio_view(self, _context):
"""
Return the studio view.
"""
fragment = Fragment(
self.system.render_template(self.mako_template, self.get_context())
)
add_webpack_to_fragment(fragment, 'CustomTagBlockStudio')
shim_xmodule_js(fragment, 'XMLEditingDescriptor')
return fragment

def render_template(self, system, xml_data):
'''Render the template, given the definition xml_data'''
xmltree = etree.fromstring(xml_data)
Expand Down Expand Up @@ -71,6 +110,14 @@ def render_template(self, system, xml_data):
def rendered_html(self):
return self.render_template(self.system, self.data)

def student_view(self, _context):
"""
Renders the student view.
"""
fragment = Fragment()
fragment.add_content(self.rendered_html)
return fragment

def export_to_file(self):
"""
Custom tags are special: since they're already pointers, we don't want
Expand Down
23 changes: 0 additions & 23 deletions common/lib/xmodule/xmodule/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,29 +191,6 @@ def test_load_class(self):
assert str(vc) == vc_str


class LogicTest(unittest.TestCase):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class is no longer being used.

"""Base class for testing xmodule logic."""
descriptor_class = None
raw_field_data = {}

def setUp(self):
super(LogicTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
self.system = get_test_system()
self.descriptor = Mock(name="descriptor", url_name='', category='test')

self.xmodule_class = self.descriptor_class.module_class
usage_key = self.system.course_id.make_usage_key(self.descriptor.category, 'test_loc')
# ScopeIds has 4 fields: user_id, block_type, def_id, usage_id
scope_ids = ScopeIds(1, self.descriptor.category, usage_key, usage_key)
self.xmodule = self.xmodule_class(
self.descriptor, self.system, DictFieldData(self.raw_field_data), scope_ids
)

def ajax_request(self, dispatch, data):
"""Call Xmodule.handle_ajax."""
return json.loads(self.xmodule.handle_ajax(dispatch, data))


def map_references(value, field, actual_course_key):
"""
Map the references in value to actual_course_key and return value
Expand Down
28 changes: 23 additions & 5 deletions common/lib/xmodule/xmodule/tests/test_poll.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,42 @@
# -*- coding: utf-8 -*-
"""Test for Poll Xmodule functional logic."""

import json
import unittest

from mock import Mock

from xmodule.poll_module import PollDescriptor
from xblock.field_data import DictFieldData
from xblock.fields import ScopeIds
from xmodule.poll_module import PollBlock

from . import LogicTest
from . import get_test_system
from .test_import import DummySystem


class PollModuleTest(LogicTest):
class PollBlockTest(unittest.TestCase):
"""Logic tests for Poll Xmodule."""
descriptor_class = PollDescriptor

raw_field_data = {
'poll_answers': {'Yes': 1, 'Dont_know': 0, 'No': 0},
'voted': False,
'poll_answer': ''
}

def setUp(self):
super().setUp()
self.system = get_test_system()
usage_key = self.system.course_id.make_usage_key(PollBlock.category, 'test_loc')
# ScopeIds has 4 fields: user_id, block_type, def_id, usage_id
scope_ids = ScopeIds(1, PollBlock.category, usage_key, usage_key)
self.xmodule = PollBlock(
self.system, DictFieldData(self.raw_field_data), scope_ids
)

def ajax_request(self, dispatch, data):
"""Call Xmodule.handle_ajax."""
return json.loads(self.xmodule.handle_ajax(dispatch, data))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two methods have been moved over with changes for XBlocks (instead of XModules) from LogicTest.

def test_bad_ajax_request(self):
# Make sure that answer for incorrect request is error json.
response = self.ajax_request('bad_answer', {})
Expand Down Expand Up @@ -52,7 +70,7 @@ def test_poll_export_with_unescaped_characters_xml(self):
</poll_question>
'''

output = PollDescriptor.from_xml(sample_poll_xml, module_system, id_generator)
output = PollBlock.from_xml(sample_poll_xml, module_system, id_generator)
# Update the answer with invalid character.
invalid_characters_poll_answer = output.answers[0]
# Invalid less-than character.
Expand Down
6 changes: 3 additions & 3 deletions common/lib/xmodule/xmodule/tests/test_xblock_wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
from xmodule.conditional_module import ConditionalBlock
from xmodule.course_module import CourseBlock
from xmodule.html_module import HtmlBlock
from xmodule.poll_module import PollDescriptor
from xmodule.poll_module import PollBlock
from xmodule.randomize_module import RandomizeBlock
from xmodule.seq_module import SequenceBlock
from xmodule.tests import get_test_descriptor_system, get_test_system
Expand All @@ -57,7 +57,7 @@
LEAF_XMODULES = {
AnnotatableBlock: [{}],
HtmlBlock: [{}],
PollDescriptor: [{'display_name': 'Poll Display Name'}],
PollBlock: [{'display_name': 'Poll Display Name'}],
WordCloudBlock: [{}],
}

Expand All @@ -76,7 +76,7 @@

# These modules are not editable in studio yet
NOT_STUDIO_EDITABLE = (
PollDescriptor,
PollBlock,
)


Expand Down