diff --git a/runestone/external/__init__.py b/runestone/external/__init__.py new file mode 100644 index 000000000..ff94a4bc0 --- /dev/null +++ b/runestone/external/__init__.py @@ -0,0 +1 @@ +from .external import * diff --git a/runestone/external/css/external.css b/runestone/external/css/external.css new file mode 100644 index 000000000..8768f5ec3 --- /dev/null +++ b/runestone/external/css/external.css @@ -0,0 +1,109 @@ +.modal-profile { + display:none; + min-height: 300px; + overflow: hidden; + width: 700px; + padding:25px; + border:1px solid #fff; + box-shadow: 0px 2px 7px #292929; + -moz-box-shadow: 0px 2px 7px #292929; + -webkit-box-shadow: 0px 2px 7px #292929; + border-radius:10px; + -moz-border-radius:10px; + -webkit-border-radius:10px; + background: #f2f2f2; + z-index:50; +} + +.modal-lightsout { + display:none; + position:absolute; + top:0; + left:0; + width:100%; + z-index:25; + background:#000 ; +} + +.modal-close-profile { + display:none; + position:absolute; + height: 43px; + width: 43px; + background-image: url('close.png'); + top:1px; + right:0.5px; +} + + +.ac_actions{ + text-align: center; +} + +.ac_sep { + background: #000; + display: inline-block; + margin: -15px 10px; + width: 1px; + height: 35px; + padding: 0; + border: 0; +} +.ac_section{ + position: relative; + clear:both; +} +.ac_section>* { + max-width: 500pt; + margin-left: auto; + margin-right: auto; + position:relative; +} +.ac_section .clearfix{ + position: initial; +} +.ac_output{ + display:none; + max-width: 450px; +} + +.ac_caption { + text-align: center; + font-weight: bold; +} + +.ac_caption_text { + font-weight: normal; +} +.ac_caption:before { + content: "ActiveCode: " counter(activecode) " "; + counter-increment: activecode; +} + +.active_out { + background-color:#dcdcdc; + border-radius: 6px; + min-width: 20em; + max-height: 300px; + overflow: auto; +} + +.visible-ac-canvas { + border: 2px solid black; +} + +.ac_section>.col-md-12 { + max-width: 100% !important; +} + +.full_width ol { + max-width: 100% !important; +} + +.ac-disabled { + pointer-events: none; +} + +.ac-feedback { + border: 1px solid black; +} diff --git a/runestone/external/external.py b/runestone/external/external.py new file mode 100644 index 000000000..2706ded9e --- /dev/null +++ b/runestone/external/external.py @@ -0,0 +1,110 @@ +# Copyright (C) 2011 Bradley N. Miller +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +from docutils import nodes +from docutils.parsers.rst import directives +from runestone.common.runestonedirective import RunestoneDirective +from docutils.parsers.rst import Directive +from sqlalchemy import create_engine, Table, MetaData, select, delete +from runestone.server import get_dburl +from runestone.server.componentdb import addQuestionToDB, addHTMLToDB +from runestone.common.runestonedirective import RunestoneDirective + +try: + from html import escape # py3 +except ImportError: + from cgi import escape # py2 + +__author__ = 'jczetta' +# Code template is directly from question.py at the moment, which is (c) Bradley N. Miller. +#This is intended as the basis for a potential new gradeable directive class, still potential TODO. + + +def setup(app): + app.add_directive('external', ExternalDirective) + + app.add_node(ExternalNode, html=(visit_external_node, depart_external_node)) + + +class ExternalNode(nodes.General, nodes.Element): + def __init__(self, content): + super(ExternalNode, self).__init__() + self.external_options = content + + +def visit_external_node(self, node): + # Set options and format templates accordingly + # env = node.document.settings.env + + res = TEMPLATE_START % node.external_options + self.body.append(res) + + addHTMLToDB(node.external_options['divid'], + node.external_options['basecourse'], + "".join("")) + + # self.body.remove(node.delimiter) + + +def depart_external_node(self, node): + # Set options and format templates accordingly + res = TEMPLATE_END % node.external_options + delimiter = "_start__{}_".format(node.external_options['divid']) + + self.body.append(res) + + +# Templates to be formatted by node options +TEMPLATE_START = ''' +
+
  • + + ''' +TEMPLATE_END = ''' +
  • +
    + ''' + + +class ExternalDirective(RunestoneDirective): + """ +.. external:: identifier + + Content Everything here is part of the activity + Content Can include links... + """ + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + has_content = True + option_spec = RunestoneDirective.option_spec.copy() + option_spec.update({'number': directives.positive_int}) + + def run(self): + addQuestionToDB(self) + + self.assert_has_content() # make sure activity has something in it + self.options['divid'] = self.arguments[0] + self.options['basecourse'] = self.state.document.settings.env.config.html_context.get('basecourse', "unknown") + + self.options['name'] = self.arguments[0].strip() + + external_node = ExternalNode(self.options) + self.add_name(external_node) + + self.state.nested_parse(self.content, self.content_offset, external_node) + + return [external_node]