diff --git a/lms/templates/poll.html b/lms/templates/poll.html
deleted file mode 100644
index 8a8c6dc1ca23..000000000000
--- a/lms/templates/poll.html
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
${configuration_json}
-
\ No newline at end of file
diff --git a/openedx/envs/common.py b/openedx/envs/common.py
index 31b0015d34d9..56afc21a1d50 100644
--- a/openedx/envs/common.py
+++ b/openedx/envs/common.py
@@ -2077,14 +2077,6 @@ def add_optional_apps(optional_apps, installed_apps):
# .. toggle_target_removal_date: 2026-04-10
USE_EXTRACTED_ANNOTATABLE_BLOCK = True
-# .. toggle_name: USE_EXTRACTED_POLL_QUESTION_BLOCK
-# .. toggle_default: True
-# .. toggle_implementation: DjangoSetting
-# .. toggle_description: Enables the use of the extracted poll question XBlock, which has been shifted to the 'openedx/xblocks-contrib' repo.
-# .. toggle_use_cases: temporary
-# .. toggle_creation_date: 2024-11-10
-# .. toggle_target_removal_date: 2026-04-10
-USE_EXTRACTED_POLL_QUESTION_BLOCK = True
# .. toggle_name: USE_EXTRACTED_LTI_BLOCK
# .. toggle_default: True
diff --git a/pyproject.toml b/pyproject.toml
index 44488938ec95..1b4ed2d7685a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -34,7 +34,7 @@ image = "xmodule.template_block:TranslateCustomTagBlock"
library = "xmodule.library_root_xblock:LibraryRoot"
library_content = "xmodule.library_content_block:LegacyLibraryContentBlock"
lti = "xmodule.lti_block:LTIBlock"
-poll_question = "xmodule.poll_block:PollBlock"
+poll_question = "xblocks_contrib:PollBlock"
problem = "xmodule.capa_block:ProblemBlock"
randomize = "xmodule.randomize_block:RandomizeBlock"
sequential = "xmodule.seq_block:SequenceBlock"
diff --git a/webpack.builtinblocks.config.js b/webpack.builtinblocks.config.js
index 20d8790fc5b5..d5964343df90 100644
--- a/webpack.builtinblocks.config.js
+++ b/webpack.builtinblocks.config.js
@@ -50,13 +50,6 @@ module.exports = {
'./xmodule/js/src/xmodule.js',
'./xmodule/js/src/raw/edit/metadata-only.js'
],
- PollBlockDisplay: [
- './xmodule/js/src/xmodule.js',
- './xmodule/js/src/javascript_loader.js',
- './xmodule/js/src/poll/poll.js',
- './xmodule/js/src/poll/poll_main.js'
- ],
- PollBlockEditor: './xmodule/js/src/xmodule.js',
ProblemBlockDisplay: [
'./xmodule/js/src/xmodule.js',
'./xmodule/js/src/javascript_loader.js',
diff --git a/webpack.common.config.js b/webpack.common.config.js
index cd9d0f53af59..b2b9571dea3a 100644
--- a/webpack.common.config.js
+++ b/webpack.common.config.js
@@ -421,24 +421,7 @@ module.exports = Merge.merge({
}
]
},
- {
- test: /xmodule\/js\/src\/poll\/poll.js/,
- use: [
- {
- loader: 'imports-loader',
- options: 'this=>window'
- }
- ]
- },
- {
- test: /xmodule\/js\/src\/poll\/poll_main.js/,
- use: [
- {
- loader: 'imports-loader',
- options: 'this=>window'
- }
- ]
- },
+
{
test: /xmodule\/js\/src\/problem\/edit.js/,
use: [
diff --git a/xmodule/js/src/poll/.gitignore b/xmodule/js/src/poll/.gitignore
deleted file mode 100644
index d4aa116a26c7..000000000000
--- a/xmodule/js/src/poll/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-!*.js
diff --git a/xmodule/js/src/poll/poll.js b/xmodule/js/src/poll/poll.js
deleted file mode 100644
index 018dbaf708a2..000000000000
--- a/xmodule/js/src/poll/poll.js
+++ /dev/null
@@ -1,11 +0,0 @@
-define(['poll/poll_main.js'], function(PollMain) {
- 'use strict';
-
- function Poll(el) {
- return new PollMain(el);
- }
-
- window.Poll = Poll;
-
- return Poll;
-});
diff --git a/xmodule/js/src/poll/poll_main.js b/xmodule/js/src/poll/poll_main.js
deleted file mode 100644
index 49eb10446a53..000000000000
--- a/xmodule/js/src/poll/poll_main.js
+++ /dev/null
@@ -1,326 +0,0 @@
-(function(requirejs, require, define) {
- define('PollMain', ['edx-ui-toolkit/js/utils/html-utils'], function(HtmlUtils) {
- PollMain.prototype = {
-
- showAnswerGraph: function(poll_answers, total) {
- var _this, totalValue;
-
- totalValue = parseFloat(total);
- if (isFinite(totalValue) === false) {
- return;
- }
-
- _this = this;
-
- $.each(poll_answers, function(index, value) {
- var numValue, percentValue;
-
- numValue = parseFloat(value);
- if (isFinite(numValue) === false) {
- return;
- }
-
- percentValue = (numValue / totalValue) * 100.0;
-
- _this.answersObj[index].statsEl.show();
- // eslint-disable-next-line max-len
- _this.answersObj[index].numberEl.html(HtmlUtils.HTML('' + value + ' (' + percentValue.toFixed(1) + '%)').toString());
- _this.answersObj[index].percentEl.css({
- width: '' + percentValue.toFixed(1) + '%'
- });
- });
- },
-
- submitAnswer: function(answer, answerObj) {
- var _this;
-
- // Make sure that the user can answer a question only once.
- if (this.questionAnswered === true) {
- return;
- }
- this.questionAnswered = true;
-
- _this = this;
-
- console.log('submit answer');
-
- answerObj.buttonEl.addClass('answered');
-
- // Send the data to the server as an AJAX request. Attach a callback that will
- // be fired on server's response.
- $.postWithPrefix(
- _this.ajax_url + '/' + answer, {},
- function(response) {
- console.log('success! response = ');
- console.log(response);
-
- _this.showAnswerGraph(response.poll_answers, response.total);
-
- if (_this.canReset === true) {
- _this.resetButton.show();
- }
-
- // Initialize Conditional constructors.
- if (_this.wrapperSectionEl !== null) {
- $(_this.wrapperSectionEl).find('.xmodule_ConditionalModule').each(function(index, value) {
- // eslint-disable-next-line no-new
- new window.Conditional(value, _this.runtime, _this.id.replace(/^poll_/, ''));
- });
- }
- }
- );
- }, // End-of: 'submitAnswer': function (answer, answerEl) {
-
- submitReset: function() {
- var _this;
-
- _this = this;
-
- console.log('submit reset');
-
- // Send the data to the server as an AJAX request. Attach a callback that will
- // be fired on server's response.
- $.postWithPrefix(
- // eslint-disable-next-line no-useless-concat
- this.ajax_url + '/' + 'reset_poll',
- {},
- function(response) {
- console.log('success! response = ');
- console.log(response);
-
- if (
- (response.hasOwnProperty('status') !== true)
- || (typeof response.status !== 'string')
- || (response.status.toLowerCase() !== 'success')) {
- return;
- }
-
- _this.questionAnswered = false;
- _this.questionEl.find('.button.answered').removeClass('answered');
- _this.questionEl.find('.stats').hide();
- _this.resetButton.hide();
-
- // Initialize Conditional constructors. We will specify the third parameter as 'true'
- // notifying the constructor that this is a reset operation.
- if (_this.wrapperSectionEl !== null) {
- $(_this.wrapperSectionEl).find('.xmodule_ConditionalModule').each(function(index, value) {
- // eslint-disable-next-line no-new
- new window.Conditional(value, _this.runtime, _this.id.replace(/^poll_/, ''));
- });
- }
- }
- );
- }, // End-of: 'submitAnswer': function (answer, answerEl) {
-
- postInit: function() {
- var _this;
-
- // Access this object inside inner functions.
- _this = this;
-
- if (
- (this.jsonConfig.poll_answer.length > 0)
- && (this.jsonConfig.answers.hasOwnProperty(this.jsonConfig.poll_answer) === false)
- ) {
- HtmlUtils.append(this.questionEl, HtmlUtils.joinHtml(
- HtmlUtils.HTML('Error!
'),
- HtmlUtils.HTML(
- 'XML data format changed. List of answers was modified, but poll data was not updated.
'
- )
- ));
-
- return;
- }
-
- // Get the DOM id of the question.
- this.id = this.questionEl.attr('id');
-
- // Get the URL to which we will post the users answer to the question.
- this.ajax_url = this.questionEl.data('ajax-url');
-
- this.questionHtmlMarkup = $('').html(HtmlUtils.HTML(this.jsonConfig.question).toString()).text();
- this.questionEl.append(HtmlUtils.HTML(this.questionHtmlMarkup).toString());
-
- // When the user selects and answer, we will set this flag to true.
- this.questionAnswered = false;
-
- this.answersObj = {};
- this.shortVersion = true;
-
- $.each(this.jsonConfig.answers, function(index, value) {
- if (value.length >= 18) {
- _this.shortVersion = false;
- }
- });
-
- $.each(this.jsonConfig.answers, function(index, value) {
- var answer;
-
- answer = {};
-
- _this.answersObj[index] = answer;
-
- answer.el = $('');
-
- answer.questionEl = $('');
- answer.buttonEl = $('');
- answer.textEl = $('');
- answer.questionEl.append(HtmlUtils.HTML(answer.buttonEl).toString());
- answer.questionEl.append(HtmlUtils.HTML(answer.textEl).toString());
-
- answer.el.append(HtmlUtils.HTML(answer.questionEl).toString());
-
- answer.statsEl = $('');
- answer.barEl = $('');
- answer.percentEl = $('');
- answer.barEl.append(HtmlUtils.HTML(answer.percentEl).toString());
- answer.numberEl = $('');
- answer.statsEl.append(HtmlUtils.HTML(answer.barEl).toString());
- answer.statsEl.append(HtmlUtils.HTML(answer.numberEl).toString());
-
- answer.statsEl.hide();
-
- answer.el.append(HtmlUtils.HTML(answer.statsEl).toString());
-
- answer.textEl.html(HtmlUtils.HTML(value).toString());
-
- if (_this.shortVersion === true) {
- // eslint-disable-next-line no-shadow
- $.each(answer, function(index, value) {
- if (value instanceof jQuery) {
- value.addClass('short');
- }
- });
- }
-
- answer.el.appendTo(_this.questionEl);
-
- answer.textEl.on('click', function() {
- _this.submitAnswer(index, answer);
- });
-
- answer.buttonEl.on('click', function() {
- _this.submitAnswer(index, answer);
- });
-
- if (index === _this.jsonConfig.poll_answer) {
- answer.buttonEl.addClass('answered');
- _this.questionAnswered = true;
- }
- });
-
- console.log(this.jsonConfig.reset);
-
- if ((typeof this.jsonConfig.reset === 'string') && (this.jsonConfig.reset.toLowerCase() === 'true')) {
- this.canReset = true;
-
- this.resetButton = $('Change your vote
');
-
- if (this.questionAnswered === false) {
- this.resetButton.hide();
- }
-
- HtmlUtils.append(this.questionEl, this.resetButton);
- this.resetButton.on('click', function() {
- _this.submitReset();
- });
- } else {
- this.canReset = false;
- }
-
- // If it turns out that the user already answered the question, show the answers graph.
- if (this.questionAnswered === true) {
- this.showAnswerGraph(this.jsonConfig.poll_answers, this.jsonConfig.total);
- }
- } // End-of: 'postInit': function () {
- }; // End-of: PollMain.prototype = {
-
- return PollMain;
-
- function PollMain(el, runtime) {
- var _this;
-
- this.runtime = runtime;
- this.questionEl = $(el).find('.poll_question');
- if (this.questionEl.length !== 1) {
- // We require one question DOM element.
- console.log('ERROR: PollMain constructor requires one question DOM element.');
-
- return;
- }
-
- // Just a safety precussion. If we run this code more than once, multiple 'click' callback handlers will be
- // attached to the same DOM elements. We don't want this to happen.
- if (this.questionEl.attr('poll_main_processed') === 'true') {
- console.log(
- 'ERROR: PolMain JS constructor was called on a DOM element that has already been processed once.'
- );
-
- return;
- }
-
- // This element was not processed earlier.
- // Make sure that next time we will not process this element a second time.
- this.questionEl.attr('poll_main_processed', 'true');
-
- // Access this object inside inner functions.
- _this = this;
-
- // DOM element which contains the current poll along with any conditionals. By default we assume that such
- // element is not present. We will try to find it.
- this.wrapperSectionEl = null;
-
- (function(tempEl, c1) {
- while (tempEl.tagName.toLowerCase() !== 'body') {
- tempEl = $(tempEl).parent()[0];
- c1 += 1;
-
- if (
- (tempEl.tagName.toLowerCase() === 'div')
- && ($(tempEl).data('block-type') === 'wrapper')
- ) {
- _this.wrapperSectionEl = tempEl;
-
- break;
- } else if (c1 > 50) {
- // In case something breaks, and we enter an endless loop, a sane
- // limit for loop iterations.
-
- break;
- }
- }
- }($(el)[0], 0));
-
- try {
- this.jsonConfig = JSON.parse(this.questionEl.children('.poll_question_div').html());
-
- $.postWithPrefix(
- // eslint-disable-next-line no-useless-concat
- '' + this.questionEl.data('ajax-url') + '/' + 'get_state', {},
- function(response) {
- _this.jsonConfig.poll_answer = response.poll_answer;
- _this.jsonConfig.total = response.total;
-
- $.each(response.poll_answers, function(index, value) {
- _this.jsonConfig.poll_answers[index] = value;
- });
-
- // xss-lint: disable=javascript-jquery-html
- _this.questionEl.children('.poll_question_div').html(JSON.stringify(_this.jsonConfig));
-
- _this.postInit();
- }
- );
-
- return;
- } catch (err) {
- console.log(
- 'ERROR: Invalid JSON config for poll ID "' + this.id + '".',
- 'Error messsage: "' + err.message + '".'
- );
- }
- } // End-of: function PollMain(el) {
- }); // End-of: define('PollMain', [], function () {
-
-// End-of: (function (requirejs, require, define) {
-}(RequireJS.requirejs, RequireJS.require, RequireJS.define));
diff --git a/xmodule/poll_block.py b/xmodule/poll_block.py
deleted file mode 100644
index 8a25fd476fe4..000000000000
--- a/xmodule/poll_block.py
+++ /dev/null
@@ -1,278 +0,0 @@
-"""Poll block is ungraded xmodule used by students to
-to do set of polls.
-
-On the client side we show:
-If student does not yet anwered - Question with set of choices.
-If student have answered - Question with statistics for each answers.
-"""
-
-import html
-import json
-import logging
-import warnings
-from collections import OrderedDict
-from copy import deepcopy
-
-from django.conf import settings
-from lxml import etree
-from web_fragments.fragment import Fragment
-from xblock.core import XBlock
-from xblock.fields import Boolean, Dict, List, Scope, String # pylint: disable=wrong-import-order
-from xblocks_contrib.poll import PollBlock as _ExtractedPollBlock
-
-from openedx.core.djangolib.markup import HTML, Text
-from xmodule.mako_block import MakoTemplateBlockBase
-from xmodule.stringify import stringify_children
-from xmodule.util.builtin_assets import add_css_to_fragment, add_webpack_js_to_fragment
-from xmodule.x_module import ResourceTemplates, XModuleMixin, XModuleToXBlockMixin, shim_xmodule_js
-from xmodule.xml_block import XmlMixin
-
-log = logging.getLogger(__name__)
-_ = lambda text: text
-
-
-@XBlock.needs('mako')
-class _BuiltInPollBlock(
- MakoTemplateBlockBase,
- XmlMixin,
- XModuleToXBlockMixin,
- ResourceTemplates,
- XModuleMixin,
-): # pylint: disable=abstract-method
- """
- Poll Block.
-
- .. deprecated:: 2026-03
- This built-in poll block is deprecated. Please use the extracted ``PollBlock``
- from ``xblocks_contrib.poll`` instead.
- """
-
- is_extracted = False
-
- # Name of poll to use in links to this poll
- display_name = String(
- help=_("The display name for this component."),
- scope=Scope.settings
- )
-
- voted = Boolean(
- help=_("Whether this student has voted on the poll"),
- scope=Scope.user_state,
- default=False
- )
- poll_answer = String(
- help=_("Student answer"),
- scope=Scope.user_state,
- default=''
- )
- poll_answers = Dict(
- help=_("Poll answers from all students"),
- scope=Scope.user_state_summary
- )
-
- # List of answers, in the form {'id': 'some id', 'text': 'the answer text'}
- answers = List(
- help=_("Poll answers from xml"),
- scope=Scope.content,
- default=[]
- )
-
- question = String(
- help=_("Poll question"),
- scope=Scope.content,
- default=''
- )
-
- resources_dir = None
- uses_xmodule_styles_setup = True
-
- def handle_ajax(self, dispatch, data): # pylint: disable=unused-argument
- """Ajax handler.
-
- Args:
- dispatch: string request slug
- data: dict request data parameters
-
- Returns:
- json string
- """
- if dispatch in self.poll_answers and not self.voted:
- # FIXME: fix this, when xblock will support mutable types.
- # Now we use this hack.
- temp_poll_answers = self.poll_answers
- temp_poll_answers[dispatch] += 1
- self.poll_answers = temp_poll_answers
-
- self.voted = True
- self.poll_answer = dispatch
- return json.dumps({'poll_answers': self.poll_answers,
- 'total': sum(self.poll_answers.values()),
- 'callback': {'objectName': 'Conditional'}
- })
- elif dispatch == 'get_state':
- return json.dumps({'poll_answer': self.poll_answer,
- 'poll_answers': self.poll_answers,
- 'total': sum(self.poll_answers.values())
- })
- elif dispatch == 'reset_poll' and self.voted and \
- self.xml_attributes.get('reset', 'True').lower() != 'false':
- self.voted = False
-
- # FIXME: fix this, when xblock will support mutable types.
- # Now we use this hack.
- temp_poll_answers = self.poll_answers
- temp_poll_answers[self.poll_answer] -= 1
- self.poll_answers = temp_poll_answers
-
- self.poll_answer = ''
- return json.dumps({'status': 'success'})
- else: # return error message
- return json.dumps({'error': 'Unknown Command!'})
-
- 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.ajax_url,
- 'configuration_json': self.dump_poll(),
- }
- fragment.add_content(self.runtime.service(self, 'mako').render_lms_template('poll.html', params))
- add_css_to_fragment(fragment, 'PollBlockDisplay.css')
- add_webpack_js_to_fragment(fragment, 'PollBlockDisplay')
- shim_xmodule_js(fragment, 'Poll')
- return fragment
-
- def dump_poll(self):
- """Dump poll information.
-
- Returns:
- string - Serialize json.
- """
- # FIXME: hack for resolving caching `default={}` during definition
- # poll_answers field
- if self.poll_answers is None:
- self.poll_answers = {}
-
- answers_to_json = OrderedDict()
-
- # FIXME: fix this, when xblock support mutable types.
- # Now we use this hack.
- temp_poll_answers = self.poll_answers
-
- # Fill self.poll_answers, prepare data for template context.
- for answer in self.answers:
- # Set default count for answer = 0.
- if answer['id'] not in temp_poll_answers:
- temp_poll_answers[answer['id']] = 0
- answers_to_json[answer['id']] = html.escape(answer['text'], quote=False)
- self.poll_answers = temp_poll_answers
-
- return json.dumps({
- 'answers': answers_to_json,
- 'question': html.escape(self.question, quote=False),
- # to show answered poll after reload:
- '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.xml_attributes.get('reset', 'true')).lower()
- })
-
- _tag_name = 'poll_question'
- _child_tag_name = 'answer'
-
- @classmethod
- def definition_from_xml(cls, xml_object, system):
- """Pull out the data into dictionary.
-
- Args:
- xml_object: xml from file.
- system: `system` object.
-
- Returns:
- (definition, children) - tuple
- definition - dict:
- {
- 'answers': ,
- 'question':
- }
- """
- # Check for presense of required tags in xml.
- if len(xml_object.xpath(cls._child_tag_name)) == 0:
- raise ValueError("Poll_question definition must include \
- at least one 'answer' tag")
-
- xml_object_copy = deepcopy(xml_object)
- answers = []
- for element_answer in xml_object_copy.findall(cls._child_tag_name):
- answer_id = element_answer.get('id', None)
- if answer_id:
- answers.append({
- 'id': answer_id,
- 'text': stringify_children(element_answer)
- })
- xml_object_copy.remove(element_answer)
-
- definition = {
- 'answers': answers,
- 'question': stringify_children(xml_object_copy)
- }
- children = []
-
- return (definition, children)
-
- def definition_to_xml(self, resource_fs):
- """Return an xml element representing to this definition."""
-
- poll_str = HTML('<{tag_name}>{text}{tag_name}>').format(
- tag_name=self._tag_name, text=self.question)
- xml_object = etree.fromstring(poll_str)
- xml_object.set('display_name', self.display_name)
-
- def add_child(xml_obj, answer): # pylint: disable=unused-argument
- # Escape answer text before adding to xml tree.
- answer_text = str(answer['text'])
- child_str = Text('{tag_begin}{text}{tag_end}').format(
- tag_begin=HTML('<{tag_name} id="{id}">').format(
- tag_name=self._child_tag_name,
- id=answer['id']
- ),
- text=answer_text,
- tag_end=HTML('{tag_name}>').format(tag_name=self._child_tag_name)
- )
- child_node = etree.fromstring(child_str)
- xml_object.append(child_node)
-
- for answer in self.answers:
- add_child(xml_object, answer)
- return xml_object
-
-
-PollBlock = None
-
-
-def reset_class():
- """Reset class as per django settings flag"""
- global PollBlock
- PollBlock = (
- _ExtractedPollBlock if settings.USE_EXTRACTED_POLL_QUESTION_BLOCK
- else _BuiltInPollBlock
- )
- return PollBlock
-
-reset_class()
-PollBlock.__name__ = "PollBlock"
-
-if not settings.USE_EXTRACTED_POLL_QUESTION_BLOCK:
- warnings.warn(
- "The built-in `xmodule.poll_block` PollBlock implementation is deprecated. "
- "To fix this warning, enable `USE_EXTRACTED_POLL_QUESTION_BLOCK` (set it to True) to use "
- "`xblocks_contrib.poll.PollBlock` instead. "
- "Support for the built-in implementation, and the `USE_EXTRACTED_POLL_QUESTION_BLOCK` setting, "
- "will be removed in Willow.",
- DeprecationWarning,
- stacklevel=2,
- )
diff --git a/xmodule/static/css-builtin-blocks/PollBlockDisplay.css b/xmodule/static/css-builtin-blocks/PollBlockDisplay.css
deleted file mode 100644
index 0615ecf123d5..000000000000
--- a/xmodule/static/css-builtin-blocks/PollBlockDisplay.css
+++ /dev/null
@@ -1,217 +0,0 @@
-@import url("https://fonts.googleapis.com/css?family=Open+Sans:300,400,400i,600,700");
-
-@media print {
- .xmodule_display.xmodule_PollBlock div.poll_question {
- display: block;
- width: auto;
- padding: 0;
- }
-
- .xmodule_display.xmodule_PollBlock div.poll_question canvas,
- .xmodule_display.xmodule_PollBlock div.poll_question img {
- page-break-inside: avoid;
- }
-}
-
-.xmodule_display.xmodule_PollBlock div.poll_question .inline {
- display: inline;
-}
-
-.xmodule_display.xmodule_PollBlock div.poll_question h3 {
- margin-top: 0;
- margin-bottom: calc((var(--baseline, 20px) * 0.75));
- color: #fe57a1;
- font-size: 1.9em;
-}
-
-.xmodule_display.xmodule_PollBlock div.poll_question h3.problem-header div.staff {
- margin-top: calc((var(--baseline, 20px) * 1.5));
- font-size: 80%;
-}
-
-@media print {
- .xmodule_display.xmodule_PollBlock div.poll_question h3 {
- display: block;
- width: auto;
- border-right: 0;
- }
-}
-
-.xmodule_display.xmodule_PollBlock div.poll_question p {
- text-align: justify;
- font-weight: bold;
-}
-
-.xmodule_display.xmodule_PollBlock div.poll_question .poll_answer {
- margin-bottom: var(--baseline, 20px);
-}
-
-.xmodule_display.xmodule_PollBlock div.poll_question .poll_answer.short {
- clear: both;
-}
-
-.xmodule_display.xmodule_PollBlock div.poll_question .poll_answer .question {
- height: auto;
- clear: both;
- min-height: 30px;
-}
-
-.xmodule_display.xmodule_PollBlock div.poll_question .poll_answer .question.short {
- clear: none;
- width: 30%;
- display: inline;
- float: left;
-}
-
-.xmodule_display.xmodule_PollBlock div.poll_question .poll_answer .question .button {
- -webkit-appearance: none;
- -webkit-background-clip: padding-box;
- -webkit-border-image: none;
- -webkit-box-align: center;
- -webkit-box-shadow: white 0px 1px 0px 0px inset;
- -webkit-font-smoothing: antialiased;
- -webkit-rtl-ordering: logical;
- -webkit-user-select: text;
- -webkit-writing-mode: horizontal-tb;
- background-clip: padding-box;
- background-color: #eeeeee;
- background-image: -webkit-linear-gradient(top, #eeeeee, #d2d2d2);
- border-bottom-color: #cacaca;
- border-bottom-left-radius: 3px;
- border-bottom-right-radius: 3px;
- border-bottom-style: solid;
- border-bottom-width: 1px;
- border-left-color: #cacaca;
- border-left-style: solid;
- border-left-width: 1px;
- border-right-color: #cacaca;
- border-right-style: solid;
- border-right-width: 1px;
- border-top-color: #cacaca;
- border-top-left-radius: 3px;
- border-top-right-radius: 3px;
- border-top-style: solid;
- border-top-width: 1px;
- box-shadow: white 0px 1px 0px 0px inset;
- box-sizing: border-box;
- color: #333333;
- /* display: inline-block; */
- display: inline;
- float: left;
- font-family: 'Open Sans', Verdana, Geneva, sans-serif;
- font-size: 13px;
- font-style: normal;
- font-variant: normal;
- font-weight: bold;
- letter-spacing: normal;
- line-height: 25.59375px;
- margin-bottom: calc((var(--baseline, 20px) * 0.75));
- margin: 0;
- padding: 0px;
- text-align: center;
- text-decoration: none;
- text-indent: 0px;
- text-shadow: #f8f8f8 0px 1px 0px;
- text-transform: none;
- vertical-align: top;
- white-space: pre-line;
- width: 25px;
- height: 25px;
- word-spacing: 0px;
- writing-mode: lr-tb;
-}
-
-.xmodule_display.xmodule_PollBlock div.poll_question .poll_answer .question .button.answered {
- -webkit-box-shadow: #61b8e1 0px 1px 0px 0px inset;
- background-color: #1d9dd9;
- background-image: -webkit-linear-gradient(top, #1d9dd9, #0e7cb0);
- border-bottom-color: #0d72a2;
- border-left-color: #0d72a2;
- border-right-color: #0d72a2;
- border-top-color: #0d72a2;
- box-shadow: #61b8e1 0px 1px 0px 0px inset;
- color: white;
- text-shadow: #076794 0px 1px 0px;
- background-image: none;
-}
-
-.xmodule_display.xmodule_PollBlock div.poll_question .poll_answer .question .text {
- display: inline;
- float: left;
- width: 80%;
- text-align: left;
- min-height: 30px;
- margin-left: var(--baseline, 20px);
- height: auto;
- margin-bottom: var(--baseline, 20px);
-}
-
-.xmodule_display.xmodule_PollBlock div.poll_question .poll_answer .question .text.short {
- width: 100px;
-}
-
-.xmodule_display.xmodule_PollBlock div.poll_question .poll_answer .stats {
- min-height: 40px;
- margin-top: var(--baseline, 20px);
- clear: both;
-}
-
-.xmodule_display.xmodule_PollBlock div.poll_question .poll_answer .stats.short {
- margin-top: 0;
- clear: none;
- display: inline;
- float: right;
- width: 70%;
-}
-
-.xmodule_display.xmodule_PollBlock div.poll_question .poll_answer .stats .bar {
- width: 75%;
- height: 20px;
- border: 1px solid black;
- display: inline;
- float: left;
- margin-right: calc((var(--baseline, 20px) / 2));
-}
-
-.xmodule_display.xmodule_PollBlock div.poll_question .poll_answer .stats .bar.short {
- width: 65%;
- height: 20px;
- margin-top: 3px;
-}
-
-.xmodule_display.xmodule_PollBlock div.poll_question .poll_answer .stats .bar .percent {
- background-color: gray;
- width: 0;
- height: 20px;
-}
-
-.xmodule_display.xmodule_PollBlock div.poll_question .poll_answer .stats .number {
- width: 80px;
- display: inline;
- float: right;
- height: 28px;
- text-align: right;
-}
-
-.xmodule_display.xmodule_PollBlock div.poll_question .poll_answer .stats .number.short {
- width: 120px;
- height: auto;
-}
-
-.xmodule_display.xmodule_PollBlock div.poll_question .poll_answer.answered {
- -webkit-box-shadow: #61b8e1 0 1px 0 0 inset;
- background-color: #1d9dd9;
- background-image: -webkit-linear-gradient(top, #1d9dd9, #0e7cb0);
- border-bottom-color: #0d72a2;
- border-left-color: #0d72a2;
- border-right-color: #0d72a2;
- border-top-color: #0d72a2;
- box-shadow: #61b8e1 0 1px 0 0 inset;
- color: white;
- text-shadow: #076794 0 1px 0;
-}
-
-.xmodule_display.xmodule_PollBlock div.poll_question .button.reset-button {
- clear: both;
- float: right;
-}
diff --git a/xmodule/tests/test_poll.py b/xmodule/tests/test_poll.py
deleted file mode 100644
index 2604aad0ced5..000000000000
--- a/xmodule/tests/test_poll.py
+++ /dev/null
@@ -1,102 +0,0 @@
-"""Test for Poll Xmodule functional logic."""
-
-import json
-
-from django.test import TestCase, override_settings
-from opaque_keys.edx.keys import CourseKey
-from xblock.field_data import DictFieldData
-from xblock.fields import ScopeIds
-
-from openedx.core.lib.safe_lxml import etree
-from xmodule import poll_block
-
-from . import get_test_system
-from .test_import import DummyModuleStoreRuntime
-
-
-class _PollBlockTestBase(TestCase):
- """Logic tests for Poll Xmodule."""
- __test__ = False
-
- @classmethod
- def setUpClass(cls):
- super().setUpClass()
- cls.poll_block_class = poll_block.reset_class()
-
- raw_field_data = {
- 'poll_answers': {'Yes': 1, 'Dont_know': 0, 'No': 0},
- 'voted': False,
- 'poll_answer': ''
- }
-
- def setUp(self):
- super().setUp()
- course_key = CourseKey.from_string('org/course/run')
- self.system = get_test_system(course_key)
- usage_key = course_key.make_usage_key('poll_question', 'test_loc')
- # ScopeIds has 4 fields: user_id, block_type, def_id, usage_id
- self.scope_ids = ScopeIds(1, 'poll_question', usage_key, usage_key)
- self.xblock = self.poll_block_class(
- self.system, DictFieldData(self.raw_field_data), self.scope_ids
- )
-
- def ajax_request(self, dispatch, data):
- """Call Xmodule.handle_ajax."""
- return json.loads(self.xblock.handle_ajax(dispatch, data))
-
- def test_bad_ajax_request(self):
- # Make sure that answer for incorrect request is error json.
- response = self.ajax_request('bad_answer', {})
- self.assertDictEqual(response, {'error': 'Unknown Command!'}) # noqa: PT009
-
- def test_good_ajax_request(self):
- # Make sure that ajax request works correctly.
- response = self.ajax_request('No', {})
-
- poll_answers = response['poll_answers']
- total = response['total']
- callback = response['callback']
-
- self.assertDictEqual(poll_answers, {'Yes': 1, 'Dont_know': 0, 'No': 1}) # noqa: PT009
- assert total == 2
- self.assertDictEqual(callback, {'objectName': 'Conditional'}) # noqa: PT009
- assert self.xblock.poll_answer == 'No'
-
- def test_poll_export_with_unescaped_characters_xml(self):
- """
- Make sure that poll_block will export fine if its xml contains
- unescaped characters.
- """
- module_system = DummyModuleStoreRuntime(load_error_blocks=True)
- module_system.id_generator.target_course_id = self.xblock.context_key
- sample_poll_xml = '''
-
- How old are you?
- 18
-
- '''
- node = etree.fromstring(sample_poll_xml)
-
- output = self.poll_block_class.parse_xml(node, module_system, self.scope_ids)
- # Update the answer with invalid character.
- invalid_characters_poll_answer = output.answers[0]
- # Invalid less-than character.
- invalid_characters_poll_answer['text'] = '< 18'
- output.answers[0] = invalid_characters_poll_answer
- output.save()
-
- xml = output.definition_to_xml(None)
- # Extract texts of all children.
- child_texts = xml.xpath('//text()')
- # Last index of child_texts contains text of answer tag.
- assert child_texts[(- 1)] == '< 18'
-
-
-@override_settings(USE_EXTRACTED_POLL_QUESTION_BLOCK=True)
-class PollBlockTestExtracted(_PollBlockTestBase):
- __test__ = True
-
-
-@override_settings(USE_EXTRACTED_POLL_QUESTION_BLOCK=False)
-class PollBlockTestBuiltIn(_PollBlockTestBase):
- __test__ = True