def render_text(text, autocompletes=None, comment=None, unwrap_p=False): # Render comment text into HTML. import re # Put @-mentions in bold. if autocompletes: text, _ = match_autocompletes(text, autocompletes, lambda text: "**" + text + "**") # Rewrite attachment:### URLs. if comment is not None: def get_attachment_url(attachment_id): try: return Attachment.objects.get( id=attachment_id.group(1)).get_absolute_url() except: return "about:blank" text = re.sub("(?<=\()attachment:(\d+)(?=\))", get_attachment_url, text) # Render to HTML as if CommonMark. import CommonMark parsed = CommonMark.Parser().parse(text) text = CommonMark.HtmlRenderer({"safe": True}).render(parsed) if unwrap_p: # If it's a single paragraph, unwrap it. text = re.sub(r"^<p>(.*)</p>$", r"\1", text) return text
def to_json(self, user=None): parser = CommonMark.Parser() renderer = CommonMark.HtmlRenderer() ast = parser.parse(self.event.abstract) abstract = renderer.render(ast) data = { 'title': self.event.title, 'event_slug': self.event.slug, 'abstract': abstract, 'from': self.when.lower.isoformat(), 'to': self.when.upper.isoformat(), 'url': str(self.event.get_absolute_url()), 'id': self.id, 'speakers': [ { 'name': speaker.name , 'url': str(speaker.get_absolute_url()) } for speaker in self.event.speakers.all() ], 'bg-color': self.event.event_type.color, 'fg-color': '#fff' if self.event.event_type.light_text else '#000', 'event_type': self.event.event_type.slug, 'location': self.location.slug, 'location_icon': self.location.icon, 'timeslots': self.timeslots, } if user and user.is_authenticated: is_favorited = user.favorites.filter(event_instance=self).exists() data['is_favorited'] = is_favorited return data
def _render(self, text): '''Render CommonMark with ettings taken in account''' parser = CommonMark.Parser() ast = parser.parse(text) renderer = HtmlRenderer(self) html = renderer.render(ast) return html
def parse(self, src): self.src = src self.recipe = Recipe(title=None) parser = CommonMark.Parser() ast = parser.parse(src) self.current = ast.first_child self._parse_title() self._parse_description() self._parse_tags_and_yields() if self.current is not None and self.current.t == 'thematic_break': self._next_node() else: # TODO this divider is required, but we might just continue anyways? raise RuntimeError("Invalid, expected divider before ingredients") self._parse_ingredients() if self.current is not None: if self.current.t == 'thematic_break': self._next_node() else: # TODO this divider is required, but we might just continue anyways? raise RuntimeError("Invalid, expected divider after ingredients") self.recipe.instructions = self._get_source_until() return self.recipe
def markdown(text, escape=True): parser = CommonMark.Parser() ast = parser.parse(text) renderer = CommonMark.HtmlRenderer() html = renderer.render(ast) return html
def extract_last_changelog(path): with open(path, 'r') as fp: parser = CommonMark.Parser() content = fp.read() changelog = ast_to_changelog(parser.parse(content)) if len(changelog.releases) == 0: raise Exception('No changelog releases') if len(changelog.releases) > 1: current = changelog.releases[0] previous = changelog.releases[1] with open(path, 'r') as fp: content = fp.read() pattern = r'\#\# {}(.*\n)([\n\S\s]*)\#\# {}'.format(re.escape(current.name), re.escape(previous.name)) result = re.search(pattern, content, re.MULTILINE) return result.group(2).strip() elif len(changelog.releases) == 1: current = changelog.releases[0] with open(path, 'r') as fp: content = fp.read() pattern = r'\#\# {}(.*\n)([\n\S\s]*)'.format(re.escape(current.name)) result = re.search(pattern, content, re.MULTILINE) return result.group(2).strip()
def neomarkdown(markdown_content): parser = CommonMark.Parser() renderer = CommonMark.HtmlRenderer() ast = parser.parse(markdown_content) html = renderer.render(ast) # json = CommonMark.dumpJSON(ast) # CommonMark.dumpAST(ast) return html
def __init__(self, parser=None, style_name=None): if parser is None: parser = CommonMark.Parser() if style_name is None: style_name = 'native' self.parser = parser self.style_name = style_name self.list_level = -1 self.counters = {} self.footnotes = []
def test_smart_dashes(self): md = 'a - b -- c --- d ---- e ----- f' EM = '\u2014' EN = '\u2013' expected_html = ('<p>' + 'a - ' + 'b ' + EN + ' ' + 'c ' + EM + ' ' + 'd ' + EN + EN + ' ' + 'e ' + EM + EN + ' ' + 'f</p>\n') parser = CommonMark.Parser(options=dict(smart=True)) ast = parser.parse(md) renderer = CommonMark.HtmlRenderer() html = renderer.render(ast) self.assertEqual(html, expected_html)
def page(name='teemo'): exodus_dir = os.path.dirname(os.path.realpath(__file__)) page = 'pages/%s.md' % name path = os.path.join(exodus_dir, page) if path.startswith(exodus_dir) and os.path.exists(path): with open(str(path)) as p: c = p.read() parser = CommonMark.Parser() ast = parser.parse(unicode(c, 'utf-8')) renderer = CommonMark.HtmlRenderer() html = renderer.render(ast) return render_template('page.html', page=html) else: return render_template('404.html'), 404
def handle(self, *args, **options): markdown_txt = options['input_file'].read_text(encoding='utf-8') parser = CommonMark.Parser() ast = parser.parse(markdown_txt) expression_date = options['expression_date'].date() work_id = options.get('work_id', options['input_file'].stem) expression_id = options.get('expression_id', expression_date.isoformat()) work, _ = Work.objects.get_or_create( doc_type=options['doc_type'], doc_subtype=options['doc_subtype'], work_id=work_id, ) Expression.objects.filter(work=work, expression_id=expression_id).delete() expression = Expression.objects.create(work=work, expression_id=expression_id, date=expression_date, author=options['author']) cursor = None for ast_node, entering in ast.walker(): if entering: fn_name = f"enter_{ast_node.t}" else: fn_name = f"exit_{ast_node.t}" if hasattr(markdown_transforms, fn_name): cursor = getattr(markdown_transforms, fn_name)(cursor, ast_node) elif fn_name not in markdown_transforms.skips: logger.warning("Don't know how to parse %s", fn_name) cursor.renumber() for node in cursor.walk(): node.struct.expression = expression node.struct.save() for span in node.extra.get('spans', []): span.doc_struct = node.struct span.save() logger.info('Created %s DocStructs for %s/%s/%s/@%s', cursor.subtree_size(), work.doc_type, work.doc_subtype, work.work_id, expression.expression_id)
def markdown(value): # Renders the string using CommonMark in safe mode, which blocks # raw HTML in the input and also some links using a blacklist, # plus a second pass filtering using a whitelist for allowed # tags and URL schemes. import CommonMark ast = CommonMark.Parser().parse(force_unicode(value)) html = CommonMark.HtmlRenderer({'safe': True}).render(ast) import html5lib, urlparse def filter_url(url): try: urlp = urlparse.urlparse(url) except Exception as e: # invalid URL return None if urlp.scheme not in ("http", "https"): return None return url valid_tags = set( 'strong em a code p h1 h2 h3 h4 h5 h6 pre br hr img ul ol li span blockquote' .split()) valid_tags = set('{http://www.w3.org/1999/xhtml}' + tag for tag in valid_tags) dom = html5lib.HTMLParser().parseFragment(html) for node in dom.iter(): if node.tag not in valid_tags and node.tag != 'DOCUMENT_FRAGMENT': node.tag = '{http://www.w3.org/1999/xhtml}span' for name, val in node.attrib.items(): if name.lower() in ("href", "src"): val = filter_url(val) if val is None: node.attrib.pop(name) else: node.set(name, val) else: # No other attributes are permitted. node.attrib.pop(name) html = html5lib.serialize(dom, quote_attr_values="always", omit_optional_tags=False, alphabetical_attributes=True) return safestring.mark_safe(html)
def run_tests(): test_output = [] parser = CommonMark.Parser() for test in get_tests(): # Render as text and output. # Since we don't have reference output, we'll make our own and # just track if it changes. renderer = CommonMarkExtensions.plaintext.PlainTextRenderer() ast = parser.parse(test['markdown']) heading = "TEST (%s)" % test['section'] print(heading) print("=" * len(heading)) print() print("markdown:") print(test['markdown']) print("HTML:") print(test['html']) print("output:") try: output = renderer.render(ast) except CommonMarkExtensions.plaintext.RawHtmlNotAllowed: print("[raw HTML is not permitted in plain text output]\n") else: print(output) # Render as CommonMark, then re-parse it, render that to # HTML, and see if the HTML matches the reference HTML. cm = CommonMarkExtensions.plaintext.CommonMarkToCommonMarkRenderer( ).render(ast) ast = parser.parse(cm) html = CommonMark.render.html.HtmlRenderer().render(ast) if html != test['html']: # This is an error. Round-tripping didn't work. print("Round-tripping CommonMark failed. It generated:") print(cm)
def main(): parser = argparse.ArgumentParser( description="Process Markdown according to " "the CommonMark specification.") if sys.version_info < (3, 0): reload(sys) # noqa sys.setdefaultencoding('utf-8') parser.add_argument('infile', nargs="?", type=argparse.FileType('r'), default=sys.stdin, help="Input Markdown file to parse, defaults to STDIN") parser.add_argument('-o', nargs="?", type=argparse.FileType('w'), default=sys.stdout, help="Output HTML/JSON file, defaults to STDOUT") parser.add_argument('-a', action="store_true", help="Print formatted AST") parser.add_argument('-aj', action="store_true", help="Output JSON AST") args = parser.parse_args() parser = CommonMark.Parser() f = args.infile o = args.o lines = [] for line in f: lines.append(line) data = "".join(lines) ast = parser.parse(data) if not args.a and not args.aj: renderer = CommonMark.HtmlRenderer() o.write(renderer.render(ast)) exit() if args.a: # print ast CommonMark.dumpAST(ast) exit() # o.write(ast.to_JSON()) o.write(CommonMark.dumpJSON(ast)) exit()
def render_commonmark_template(template, context): # Render a CommonMark template to HTML. # Replace template tags with Unicode sentinels so that # the template tags do not impact CommonMark rendering. substitutions = [] import re def replace(m): # Record the substitution. index = len(substitutions) substitutions.append(m.group(0)) return "\uE000%d\uE001" % index # use Unicode private use area code points template = re.sub("{%.*?%}|{{.*?}}", replace, template) # Render the CommonMark. # Prevent CommonMark from mucking with our sentinels # in URLs though - it would otherwise add escaping. from CommonMark import inlines def urlencode_special(uri): import urllib.parse return "".join( urllib.parse.quote(c, safe="/@:+?=&()%#*,") # this is what CommonMark does if c not in "\uE000\uE001" else c # but keep our special codes for c in uri) inlines.normalize_uri = urlencode_special # Render. import CommonMark template = CommonMark.HtmlRenderer().render(CommonMark.Parser().parse(template)) # Put the template tags back that we removed prior to running # the CommonMark renderer. def replace(m): return substitutions[int(m.group(1))] template = re.sub("\uE000(\d+)\uE001", replace, template) # And finally render the Django template. return Template(template).render(Context(context))
def main(where): parser = CommonMark.Parser() for root, subdirs, files in os.walk(where): for i in files: filename = root + '/' + i if not filename.endswith('.md'): continue ast = parser.parse(codecs.open(filename, encoding="utf8").read()) for i, _ in ast.walker(): if i.__dict__['t'] == "link": url = i.__dict__['destination'] if url.startswith('http'): check_external_url(url) # local file elif not url.startswith('/'): raise Exception( "a link to a local page must start with / " + url + " in " + filename) else: linked_page = where + '/' + url.replace(".html", ".md") if not os.path.exists(linked_page): raise Exception("no such page " + linked_page)
def unsafecommonmark(value): """Returns HTML given some CommonMark Markdown. Cleans HTML from input using bleach, suitable for use with untrusted input.""" parser = CommonMark.Parser() renderer = CommonMark.HtmlRenderer() ast = parser.parse(bleach.clean(value)) return mark_safe(renderer.render(ast))
def commonmark(value): """Returns HTML given some CommonMark Markdown. Does not clean HTML, not for use with untrusted input.""" parser = CommonMark.Parser() renderer = CommonMark.HtmlRenderer() ast = parser.parse(value) return mark_safe(renderer.render(ast))
def main(): prolog = """ <meta charset="utf-8"> <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css"> <script src="http://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/highlight.min.js"></script> <script>hljs.initHighlightingOnLoad();</script> """ links = [('stylesheet', 'text/css', 'default.css')] scripts = [] known_types = { 'css': ('stylesheet', 'text/css'), 'js': ('script', 'text/javascript') } parser = argparse.ArgumentParser( description="CommonMark to HTML5 processor") parser.add_argument('infile', nargs="?", type=argparse.FileType('r'), default=sys.stdin, help="The Markdown file to parse (defaults to STDIN)") parser.add_argument( '-o', nargs="?", type=argparse.FileType('w'), default=sys.stdout, help="The output HTML file to produce (defaults to STDOUT)") parser.add_argument('--inline', action='store_true', default=False, help="Inline resources via data uris") parser.add_argument('--toc', action='store_true', default=False, help="Generate a table of contents") parser.add_argument('--autonumber-title', action='store_true', dest='autonumbertitle', default=False, help="Generate the title in the table of contents") parser.add_argument('--autonumber', action='store_true', dest='autonumber', default=False, help="Generate the title in the table of contents") parser.add_argument('--prolog', type=argparse.FileType('r'), dest='prologFile', nargs="?", help="A file to embed in the prolog (head).") parser.add_argument('--link', action='append', dest='links', help="A link to add to the prolog (head)") parser.add_argument( '--copy-style', action='append', dest='copyStyle', help="Copy the default style from the package to the local destination" ) parser.add_argument('--script', action='append', dest='scripts', help="A script to add to the prolog (head)") args = parser.parse_args() if args.copyStyle is not None: for source in args.copyStyle: this_dir, this_filename = os.path.split(__file__) path = os.path.join(this_dir, source) copyfile(path, source) f = args.infile o = args.o s = io.StringIO() for line in f: s.write(line) parser = CommonMark.Parser() ast = parser.parse(s.getvalue()) s.close() if args.prologFile is not None: s = io.StringIO() for line in args.prologFile: s.write(line) prolog = s.getvalue() s.close() if args.links is not None: links = [] for link in args.links: if link[0] == '(': links.append(literal_eval(link)) else: ext = link[link.rfind('.') + 1:] if ext in known_types: info = known_types[ext] links.append((info[0], info[1], link)) else: raise ValueError('Unknown extension: ' + ext) if args.scripts is not None: scripts = [] for script in args.scripts: if script[0] == '(': scripts.append(literal_eval(script)) else: ext = script[script.rfind('.') + 1:] if ext in known_types: info = known_types[ext] scripts.append((info[1], script)) else: raise ValueError('Unknown extension: ' + ext) headings = toc(ast, args.autonumber, args.autonumbertitle) if args.toc else None renderer = HTML5Renderer() title = findTitle(ast) o.write('<html>\n') o.write('<head>\n') o.write('<title>{0}</title>\n'.format(title)) o.write(prolog) for link in links: if args.inline: o.write('<link rel="{0}" type="{1}" href="{2}">\n'.format( link[0], link[1], dataURI(link[1], link[2]))) else: o.write('<link rel="{0}" type="{1}" href="{2}">\n'.format(*link)) for script in scripts: if args.inline: o.write('<script type="{0}" src="{1}"></script>\n'.format( link[0], dataURI(link[0], link[1]))) else: o.write('<script type="{0}" src="{1}"></script>\n'.format(*script)) o.write('</head>\n') o.write('<body>\n') o.write('<main>\n') o.write('<h1>{0}</h1>'.format(title)) if headings is not None: o.write('<header class="toc">\n') o.write('<h1>Table of Contents</h1>\n') writeTOC( o, headings if len(headings) > 1 or args.autonumbertitle else headings[0][2], args.autonumber) o.write('</header>\n') o.write('<article>\n') o.write(renderer.render(ast)) o.write('</article>\n') o.write('</main>\n') o.write('</body>\n') o.write('</html>\n')
def __init__(self, *args, **kwargs): super(CompileCommonMark, self).__init__(*args, **kwargs) if CommonMark is not None: self.parser = CommonMark.Parser() self.renderer = CommonMark.HtmlRenderer()
def setUp(self): self.parser = CommonMark.Parser() self.renderer = CommonMark.ReStructuredTextRenderer()
def commonmark(value): parser = CommonMark.Parser() renderer = CommonMark.HtmlRenderer() ast = parser.parse(force_text(value)) return mark_safe(force_text(renderer.render(ast)))
def trustedcommonmark(value): """Returns HTML given some CommonMark Markdown. Also allows real HTML, so do not use this with untrusted input.""" parser = CommonMark.Parser() renderer = CommonMark.HtmlRenderer() ast = parser.parse(value) return mark_safe(renderer.render(ast))
def __init__(self): self.path = os.path.join(settings.BASE_DIR, 'NEWS.md') self.parser = CommonMark.Parser() self.renderer = CommonMark.HtmlRenderer() self.cookie_name = 'news_current'
def debug(filename): print "\n".join( str(x) for x in list(CommonMark.Parser().parse( codecs.open(filename, encoding="utf8").read()).walker()))
def parse_changelog(path): with open(path, 'r') as fp: parser = CommonMark.Parser() ast = parser.parse(fp.read()) return ast_to_changelog(ast)
import CommonMark parser = CommonMark.Parser() with open("data/am_hang/gipfel.md", "rt") as f: t = f.read() ast = parser.parse(t) for node, b in ast.walker(): print(b, node.level, node) # print(node.pretty())
def create(self, options): self.parser = CommonMark.Parser() self.renderer = CommonMark.HtmlRenderer()
def run(): """ Looks through the docs/ dir and parses each markdown document, looking for sections to update from Python docstrings. Looks for section headers in the format: - ### `ClassName()` class - ##### `.method_name()` method - ##### `.attribute_name` attribute - ### `function_name()` function The markdown content following these section headers up until the next section header will be replaced by new markdown generated from the Python docstrings of the associated source files. By default maps docs/{name}.md to {modulename}/{name}.py. Allows for custom mapping via the MD_SOURCE_MAP variable. """ print('Updating API docs...') md_files = [] for root, _, filenames in os.walk(os.path.join(package_root, 'docs')): for filename in filenames: if not filename.endswith('.md'): continue md_files.append(os.path.join(root, filename)) parser = CommonMark.Parser() for md_file in md_files: md_file_relative = md_file[len(package_root) + 1:] if md_file_relative in MD_SOURCE_MAP: py_files = MD_SOURCE_MAP[md_file_relative] py_paths = [ os.path.join(package_root, py_file) for py_file in py_files ] else: py_files = [os.path.basename(md_file).replace('.md', '.py')] py_paths = [os.path.join(package_root, package_name, py_files[0])] if not os.path.exists(py_paths[0]): continue with open(md_file, 'rb') as f: markdown = f.read().decode('utf-8') original_markdown = markdown md_lines = list(markdown.splitlines()) md_ast = parser.parse(markdown) last_class = [] last = {} sections = OrderedDict() find_sections(md_ast, sections, last, last_class, markdown.count("\n") + 1) md_chunks = {} for index, py_file in enumerate(py_files): py_path = py_paths[index] with open(os.path.join(py_path), 'rb') as f: code = f.read().decode('utf-8') module_ast = ast.parse(code, filename=py_file) code_lines = list(code.splitlines()) for node in ast.iter_child_nodes(module_ast): walk_ast(node, code_lines, sections, md_chunks) added_lines = 0 def _replace_md(key, sections, md_chunk, md_lines, added_lines): start, end = sections[key] start -= 1 start += added_lines end += added_lines new_lines = md_chunk.split('\n') added_lines += len(new_lines) - (end - start) # Ensure a newline above each class header if start > 0 and md_lines[start][0:4] == '### ' and md_lines[ start - 1][0:1] == '>': added_lines += 1 new_lines.insert(0, '') md_lines[start:end] = new_lines return added_lines for key in sections: if key not in md_chunks: raise ValueError('No documentation found for %s' % key[1]) added_lines = _replace_md(key, sections, md_chunks[key], md_lines, added_lines) markdown = '\n'.join(md_lines).strip() + '\n' if original_markdown != markdown: with open(md_file, 'wb') as f: f.write(markdown.encode('utf-8'))
def show_question(request, task, answered, context, q, EncryptionProvider, set_ephemeral_encryption_cookies): # Always hide the fill-out-your-profile blurb on all question pages - it's # distracting from answering the question at hand. See task_view. request.suppress_prompt_banner = True # If this question cannot currently be answered (i.e. dependencies are unmet), # then redirect away from this page. If the user is allowed to use the authoring # tool, then allow seeing this question so they can edit all questions. authoring_tool_enabled = task.module.is_authoring_tool_enabled(request.user) is_answerable = (((q not in answered.unanswered) or (q in answered.can_answer)) and (q.key not in answered.was_imputed)) if not is_answerable and not authoring_tool_enabled: return HttpResponseRedirect(task.get_absolute_url()) # Is there a TaskAnswer for this yet? taskq = TaskAnswer.objects.filter(task=task, question=q).first() # Display requested question. # Is there an answer already? (If this question isn't answerable, i.e. if we're # only here because the user is using the authoring tool, then there is no # real answer to load.) answer = None if taskq and is_answerable: answer = taskq.get_current_answer() if answer and answer.cleared: # If the answer is cleared, treat as if it had not been answered. answer = None # For "module"-type questions, get the Module instance of the tasks that can # be an answer to this question, and get the existing Tasks that the user can # choose as an answer. answer_module = q.answer_type_module answer_tasks = [] if answer_module: # The user can choose from any Task instances they have read permission on # and that are of the correct Module type. answer_tasks = Task.get_all_tasks_readable_by(request.user, request.organization, recursive=True)\ .filter(module=answer_module) # Annotate the instances with whether the user also has write permission. for t in answer_tasks: t.can_write = t.has_write_priv(request.user) # Sort the instances: # first: the current answer, if any # then: tasks defined in the same project as this task # later: tasks defined in projects in the same folder as this task's project # last: everything else by reverse update date now = timezone.now() current_answer = answer.answered_by_task.first() if answer else None answer_tasks = sorted(answer_tasks, key = lambda t : ( not (t == current_answer), not (t.project == task.project), not (set(t.project.contained_in_folders.all()) & set(task.project.contained_in_folders.all())), now-t.updated, )) # Add instrumentation event. # How many times has this question been shown? i_prev_view = InstrumentationEvent.objects\ .filter(user=request.user, event_type="task-question-show", task=task, question=q)\ .order_by('-event_time')\ .first() # Save. InstrumentationEvent.objects.create( user=request.user, event_type="task-question-show", event_value=(i_prev_view.event_value+1) if i_prev_view else 1, module=task.module, question=q, project=task.project, task=task, answer=taskq, ) # Indicate for the InstrumentQuestionPageLoadTimes middleware that this is # a question page load. request._instrument_page_load = { "event_type": "task-question-request-duration", "module": task.module, "question": q, "project": task.project, "task": task, "answer": taskq, } # Construct the page. def render_markdown_field(field, output_format, **kwargs): template = q.spec.get(field) if not template: return None if not isinstance(template, str): raise ValueError("%s question %s %s is not a string" % (repr(q.module), q.key, field)) return module_logic.render_content({ "template": template, "format": "markdown", }, answered, output_format, "%s question %s %s" % (repr(q.module), q.key, field), **kwargs ) # Get any existing answer for this question. existing_answer = None if answer: existing_answer = answer.get_value(decryption_provider=EncryptionProvider()) # For longtext questions, because the WYSIWYG editor is initialized with HTML, # render the value as HTML. if existing_answer and q.spec["type"] == "longtext": import CommonMark existing_answer = CommonMark.HtmlRenderer().render(CommonMark.Parser().parse(existing_answer)) # What's the title/h1 of the page and the rest of the prompt? Render the # prompt field. If it starts with a paragraph, turn that paragraph into # the title. title = q.spec["title"] prompt = render_markdown_field("prompt", "html") m = re.match(r"^<p>([\w\W]*?)</p>\s*", prompt) if m: title = m.group(1) prompt = prompt[m.end():] # Get a default answer for this question. Render Jinja2 template, but don't turn # Markdown into HTML for plain text fields. For longtext fields, turn it into # HTML because the WYSIWYG editor is initialized with HTML. default_answer = render_markdown_field("default", "text" if q.spec["type"] != "longtext" else "html", demote_headings=False) context.update({ "header_col_active": "start" if (len(answered.as_dict()) == 0 and q.spec["type"] == "interstitial") else "questions", "q": q, "title": title, "prompt": prompt, "placeholder_answer": render_markdown_field("placeholder", "text") or "", # Render Jinja2 template but don't turn Markdown into HTML. "reference_text": render_markdown_field("reference_text", "html"), "history": taskq.get_history() if taskq else None, "answer_obj": answer, "answer": existing_answer, "default_answer": default_answer, "review_choices": [(0, 'Not Reviewed'), (1, 'Reviewed'), (2, 'Approved')], "discussion": Discussion.get_for(request.organization, taskq) if taskq else None, "show_discussion_members_count": True, "answer_module": answer_module, "answer_tasks": answer_tasks, "answer_tasks_show_user": len([ t for t in answer_tasks if t.editor != request.user ]) > 0, "context": module_logic.get_question_context(answered, q), # Helpers for showing date month, day, year dropdowns, with # localized strings and integer values. Default selections # are done in the template & client-side so that we can use # the client browser's timezone to determine the current date. "date_l8n": lambda : { "months": [ (timezone.now().replace(2016,m,1).strftime("%B"), m) for m in range(1, 12+1)], "days": [ d for d in range(1, 31+1)], "years": [ y for y in reversed(range(timezone.now().year-100, timezone.now().year+101))], }, "is_answerable": is_answerable, # only false if authoring tool is enabled, otherwise this page is not renderable "authoring_tool_enabled": authoring_tool_enabled, }) return render(request, "question.html", context)