diff --git a/cms/djangoapps/contentstore/views/component.py b/cms/djangoapps/contentstore/views/component.py index 34c1f465c566..beea9827d12b 100644 --- a/cms/djangoapps/contentstore/views/component.py +++ b/cms/djangoapps/contentstore/views/component.py @@ -56,6 +56,7 @@ 'html', 'video', 'problem', + 'game', 'itembank', 'library_v2', # Not an XBlock 'library', diff --git a/lms/templates/game.html b/lms/templates/game.html new file mode 100644 index 000000000000..21a27a0e08db --- /dev/null +++ b/lms/templates/game.html @@ -0,0 +1,12 @@ +<%page expression_filter="h"/> + +
+ % if game_type: +
+ ${game_type.capitalize()} +
+ % endif +
+ ${data | n} +
+
diff --git a/setup.py b/setup.py index 5b9f020ac3c5..de897e2ea783 100644 --- a/setup.py +++ b/setup.py @@ -17,6 +17,7 @@ "discuss = xmodule.template_block:TranslateCustomTagBlock", "discussion = xmodule.discussion_block:DiscussionXBlock", "error = xmodule.error_block:ErrorBlock", + "game = xmodule.game_block:GameBlock", "hidden = xmodule.hidden_block:HiddenBlock", "html = xmodule.html_block:HtmlBlock", "itembank = xmodule.item_bank_block:ItemBankBlock", diff --git a/xmodule/game_block.py b/xmodule/game_block.py new file mode 100644 index 000000000000..3f1895f1b24f --- /dev/null +++ b/xmodule/game_block.py @@ -0,0 +1,75 @@ +# lint-amnesty, pylint: disable=missing-module-docstring + +import logging + +from web_fragments.fragment import Fragment +from xblock.core import XBlock +from xblock.fields import Scope, String + +from xmodule.x_module import ( + XModuleMixin, + XModuleToXBlockMixin, +) +from xmodule.xml_block import XmlMixin + +log = logging.getLogger(__name__) + +# Make '_' a no-op so we can scrape strings +_ = lambda text: text + + +@XBlock.needs("mako") +class GameBlock( # lint-amnesty, pylint: disable=abstract-method + XmlMixin, + XModuleToXBlockMixin, + XModuleMixin, + XBlock, +): + """ + Game XBlock for displaying interactive game content in courses. + This is a read-only component for students to view game content. + """ + + display_name = String( + display_name=_("Display Name"), + help=_("The display name for this component."), + scope=Scope.settings, + default=_("Game") + ) + + game_type = String( + help=_("Type of game"), + display_name=_("Game Type"), + default="", + scope=Scope.settings + ) + + data = String( + help=_("Game content to display for this block"), + default="", + scope=Scope.content + ) + + @XBlock.supports("multi_device") + def student_view(self, _context): + """ + Return a fragment that contains the html for the student view + """ + from xmodule.util.builtin_assets import add_css_to_fragment + + context = { + 'game_type': self.game_type, + 'data': self.data if self.data else "" + } + fragment = Fragment( + self.runtime.service(self, 'mako').render_lms_template('game.html', context) + ) + add_css_to_fragment(fragment, 'GameBlockDisplay.css') + return fragment + + @XBlock.supports("multi_device") + def public_view(self, context): + """ + Returns a fragment that contains the html for the preview view + """ + return self.student_view(context) diff --git a/xmodule/static/css-builtin-blocks/GameBlockDisplay.css b/xmodule/static/css-builtin-blocks/GameBlockDisplay.css new file mode 100644 index 000000000000..6fb7500f17d7 --- /dev/null +++ b/xmodule/static/css-builtin-blocks/GameBlockDisplay.css @@ -0,0 +1,21 @@ +/* Game XBlock Display Styles */ + +.game-xblock { + margin: 20px 0; +} + +.game-xblock .game-type-header { + margin-bottom: 10px; + padding-bottom: 10px; + border-bottom: 1px solid var(--gray-l3, #c8c8c8); +} + +.game-xblock .game-type-header strong { + font-size: 16px; + color: var(--body-color, #313131); +} + +.game-xblock .game-content { + font-size: 14px; + color: var(--body-color, #313131); +}