Commit 2a250fde by David Ormsbee

Merge pull request #452 from MITx/feature/bridger/new_wiki

Feature/bridger/new wiki
parents cf20839b c4512faf
import logging
from django.conf import settings
from django.template.base import TemplateDoesNotExist
from django.template.loader import make_origin, get_template_from_string
from django.template.loaders.filesystem import Loader as FilesystemLoader
from django.template.loaders.app_directories import Loader as AppDirectoriesLoader
from mitxmako.template import Template
import mitxmako.middleware
log = logging.getLogger(__name__)
class MakoLoader(object):
"""
......@@ -19,19 +25,28 @@ class MakoLoader(object):
# base_loader is an instance of a BaseLoader subclass
self.base_loader = base_loader
module_directory = getattr(settings, 'MAKO_MODULE_DIR', None)
if module_directory is None:
log.warning("For more caching of mako templates, set the MAKO_MODULE_DIR in settings!")
module_directory = tempfile.mkdtemp()
self.module_directory = module_directory
def __call__(self, template_name, template_dirs=None):
return self.load_template(template_name, template_dirs)
def load_template(self, template_name, template_dirs=None):
source, display_name = self.load_template_source(template_name, template_dirs)
source, file_path = self.load_template_source(template_name, template_dirs)
if source.startswith("## mako\n"):
# This is a mako template
template = Template(text=source, uri=template_name)
template = Template(filename=file_path, module_directory=self.module_directory, uri=template_name)
return template, None
else:
# This is a regular template
origin = make_origin(display_name, self.load_template_source, template_name, template_dirs)
origin = make_origin(file_path, self.load_template_source, template_name, template_dirs)
try:
template = get_template_from_string(source, origin, template_name)
return template, None
......@@ -40,7 +55,7 @@ class MakoLoader(object):
# returning the source and display name for the template we were asked to load.
# This allows for correct identification (later) of the actual template that does
# not exist.
return source, display_name
return source, file_path
def load_template_source(self, template_name, template_dirs=None):
# Just having this makes the template load as an instance, instead of a class.
......
......@@ -17,8 +17,7 @@ from mako.template import Template as MakoTemplate
from mitxmako import middleware
django_variables = ['lookup', 'output_encoding',
'module_directory', 'encoding_errors']
django_variables = ['lookup', 'output_encoding', 'encoding_errors']
# TODO: We should make this a Django Template subclass that simply has the MakoTemplate inside of it? (Intead of inheriting from MakoTemplate)
class Template(MakoTemplate):
......
CodeMirror.defineMode("xml", function(config, parserConfig) {
var indentUnit = config.indentUnit;
var Kludges = parserConfig.htmlMode ? {
autoSelfClosers: {"br": true, "img": true, "hr": true, "link": true, "input": true,
"meta": true, "col": true, "frame": true, "base": true, "area": true},
autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
'track': true, 'wbr': true},
implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
'th': true, 'tr': true},
contextGrabbers: {
'dd': {'dd': true, 'dt': true},
'dt': {'dd': true, 'dt': true},
'li': {'li': true},
'option': {'option': true, 'optgroup': true},
'optgroup': {'optgroup': true},
'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
'rp': {'rp': true, 'rt': true},
'rt': {'rp': true, 'rt': true},
'tbody': {'tbody': true, 'tfoot': true},
'td': {'td': true, 'th': true},
'tfoot': {'tbody': true},
'th': {'td': true, 'th': true},
'thead': {'tbody': true, 'tfoot': true},
'tr': {'tr': true}
},
doNotIndent: {"pre": true},
allowUnquoted: true,
allowMissing: false
} : {autoSelfClosers: {}, doNotIndent: {}, allowUnquoted: false, allowMissing: false};
} : {
autoSelfClosers: {},
implicitlyClosed: {},
contextGrabbers: {},
doNotIndent: {},
allowUnquoted: false,
allowMissing: false
};
var alignCDATA = parserConfig.alignCDATA;
// Return variables for tokenizers
......@@ -162,7 +194,12 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
} else if (type == "closeTag") {
var err = false;
if (curState.context) {
err = curState.context.tagName != tagName;
if (curState.context.tagName != tagName) {
if (Kludges.implicitlyClosed.hasOwnProperty(curState.context.tagName.toLowerCase())) {
popContext();
}
err = !curState.context || curState.context.tagName != tagName;
}
} else {
err = true;
}
......@@ -174,9 +211,15 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
function endtag(startOfLine) {
return function(type) {
if (type == "selfcloseTag" ||
(type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(curState.tagName.toLowerCase())))
(type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(curState.tagName.toLowerCase()))) {
maybePopContext(curState.tagName.toLowerCase());
return cont();
if (type == "endTag") {pushContext(curState.tagName, startOfLine); return cont();}
}
if (type == "endTag") {
maybePopContext(curState.tagName.toLowerCase());
pushContext(curState.tagName, startOfLine);
return cont();
}
return cont();
};
}
......@@ -188,6 +231,20 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
return cont(arguments.callee);
}
}
function maybePopContext(nextTagName) {
var parentTagName;
while (true) {
if (!curState.context) {
return;
}
parentTagName = curState.context.tagName.toLowerCase();
if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) ||
!Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
return;
}
popContext();
}
}
function attributes(type) {
if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);}
......@@ -255,7 +312,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
if (a.indented != b.indented || a.tokenize != b.tokenize) return false;
for (var ca = a.context, cb = b.context; ; ca = ca.prev, cb = cb.prev) {
if (!ca || !cb) return ca == cb;
if (ca.tagName != cb.tagName) return false;
if (ca.tagName != cb.tagName || ca.indent != cb.indent) return false;
}
},
......@@ -263,5 +320,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
};
});
CodeMirror.defineMIME("text/xml", "xml");
CodeMirror.defineMIME("application/xml", "xml");
CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
##
## mako
## File: templates/mathjax_include.html
##
## Advanced mathjax using 2.0-latest CDN for Dynamic Math
##
## This enables ASCIIMathJAX, and is used by js_textbox
%if mathjax_mode is not Undefined and mathjax_mode == 'wiki':
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
tex2jax: {inlineMath: [ ['$','$'], ["\\(","\\)"]],
displayMath: [ ['$$','$$'], ["\\[","\\]"]]}
});
</script>
%else:
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
tex2jax: {
......@@ -19,6 +28,7 @@
}
});
</script>
%endif
<!-- This must appear after all mathjax-config blocks, so it is after the imports from the other templates.
It can't be run through static.url because MathJax uses crazy url introspection to do lazy loading of
......
from django import forms
from django.forms.util import flatatt
from django.utils.encoding import force_unicode
from django.utils.html import conditional_escape
from django.utils.safestring import mark_safe
from wiki.editors.base import BaseEditor
from wiki.editors.markitup import MarkItUpAdminWidget
class CodeMirrorWidget(forms.Widget):
def __init__(self, attrs=None):
# The 'rows' and 'cols' attributes are required for HTML correctness.
default_attrs = {'class': 'markItUp',
'rows': '10', 'cols': '40',}
if attrs:
default_attrs.update(attrs)
super(CodeMirrorWidget, self).__init__(default_attrs)
def render(self, name, value, attrs=None):
if value is None: value = ''
final_attrs = self.build_attrs(attrs, name=name)
return mark_safe(u'<div><textarea%s>%s</textarea></div>' % (flatatt(final_attrs),
conditional_escape(force_unicode(value))))
class CodeMirror(BaseEditor):
editor_id = 'codemirror'
def get_admin_widget(self, instance=None):
return MarkItUpAdminWidget()
def get_widget(self, instance=None):
return CodeMirrorWidget()
class AdminMedia:
css = {
'all': ("wiki/markitup/skins/simple/style.css",
"wiki/markitup/sets/admin/style.css",)
}
js = ("wiki/markitup/admin.init.js",
"wiki/markitup/jquery.markitup.js",
"wiki/markitup/sets/admin/set.js",
)
class Media:
css = {
'all': ("js/vendor/CodeMirror/codemirror.css",)
}
js = ("js/vendor/CodeMirror/codemirror.js",
"js/vendor/CodeMirror/xml.js",
"js/vendor/CodeMirror/mitx_markdown.js",
"js/wiki/CodeMirror.init.js",
)
# Make sure wiki_plugin.py gets run.
from course_wiki.plugins.markdownedx.wiki_plugin import ExtendMarkdownPlugin
\ No newline at end of file
#!/usr/bin/env python
'''
Image Circuit Extension for Python-Markdown
======================================
Any single line beginning with circuit-schematic: and followed by data (which should be json data, but this
is not enforced at this level) will be displayed as a circuit schematic. This is simply an input element with
the value set to the data. It is left to javascript on the page to render that input as a circuit schematic.
ex:
circuit-schematic:[["r",[128,48,0],{"r":"1","_json_":0},["2","1"]],["view",0,0,2,null,null,null,null,null,null,null],["dc",{"0":0,"1":1,"I(_3)":-1}]]
(This is a schematic with a single one-ohm resistor. Note that this data is not meant to be user-editable.)
'''
import markdown
import re
from django.utils.html import escape
try:
# Markdown 2.1.0 changed from 2.0.3. We try importing the new version first,
# but import the 2.0.3 version if it fails
from markdown.util import etree
except:
from markdown import etree
class CircuitExtension(markdown.Extension):
def __init__(self, configs):
for key, value in configs:
self.setConfig(key, value)
def extendMarkdown(self, md, md_globals):
## Because Markdown treats contigous lines as one block of text, it is hard to match
## a regex that must occupy the whole line (like the circuit regex). This is why we have
## a preprocessor that inspects the lines and replaces the matched lines with text that is
## easier to match
md.preprocessors.add('circuit', CircuitPreprocessor(md), "_begin")
pattern = CircuitLink(r'processed-schematic:(?P<data>.*?)processed-schematic-end')
pattern.md = md
pattern.ext = self
md.inlinePatterns.add('circuit', pattern, "<reference")
class CircuitPreprocessor(markdown.preprocessors.Preprocessor):
preRegex = re.compile(r'^circuit-schematic:(?P<data>.*)$')
def run(self, lines):
print "running circuit preprocessor"
def convertLine(line):
m = self.preRegex.match(line)
if m:
return 'processed-schematic:{0}processed-schematic-end'.format(m.group('data'))
else:
return line
return [convertLine(line) for line in lines]
class CircuitLink(markdown.inlinepatterns.Pattern):
def handleMatch(self, m):
data = m.group('data')
data = escape(data)
return etree.fromstring("<div align='center'><input type='hidden' parts='' value='" + data + "' analyses='' class='schematic ctrls' width='400' height='220'/></div>")
def makeExtension(configs=None):
to_return = CircuitExtension(configs=configs)
print "circuit returning ", to_return
return to_return
#!/usr/bin/env python
'''
Image Embedding Extension for Python-Markdown
======================================
Converts lone links to embedded images, provided the file extension is allowed.
Ex:
http://www.ericfehse.net/media/img/ef/blog/django-pony.jpg
becomes
<img src="http://www.ericfehse.net/media/img/ef/blog/django-pony.jpg">
mypic.jpg becomes <img src="/MEDIA_PATH/mypic.jpg">
Requires Python-Markdown 1.6+
'''
import simplewiki.settings as settings
import markdown
try:
# Markdown 2.1.0 changed from 2.0.3. We try importing the new version first,
# but import the 2.0.3 version if it fails
from markdown.util import etree
except:
from markdown import etree
class ImageExtension(markdown.Extension):
def __init__(self, configs):
for key, value in configs:
self.setConfig(key, value)
def add_inline(self, md, name, klass, re):
pattern = klass(re)
pattern.md = md
pattern.ext = self
md.inlinePatterns.add(name, pattern, "<reference")
def extendMarkdown(self, md, md_globals):
self.add_inline(md, 'image', ImageLink,
r'^(?P<proto>([^:/?#])+://)?(?P<domain>([^/?#]*)/)?(?P<path>[^?#]*\.(?P<ext>[^?#]{3,4}))(?:\?([^#]*))?(?:#(.*))?$')
class ImageLink(markdown.inlinepatterns.Pattern):
def handleMatch(self, m):
img = etree.Element('img')
proto = m.group('proto') or "http://"
domain = m.group('domain')
path = m.group('path')
ext = m.group('ext')
# A fixer upper
if ext.lower() in settings.WIKI_IMAGE_EXTENSIONS:
if domain:
src = proto + domain + path
elif path:
# We need a nice way to source local attachments...
src = "/wiki/media/" + path + ".upload"
else:
src = ''
img.set('src', src)
return img
def makeExtension(configs=None):
return ImageExtension(configs=configs)
if __name__ == "__main__":
import doctest
doctest.testmod()
# Source: https://github.com/mayoff/python-markdown-mathjax
import markdown
try:
# Markdown 2.1.0 changed from 2.0.3. We try importing the new version first,
# but import the 2.0.3 version if it fails
from markdown.util import etree, AtomicString
except:
from markdown import etree, AtomicString
class MathJaxPattern(markdown.inlinepatterns.Pattern):
def __init__(self):
markdown.inlinepatterns.Pattern.__init__(self, r'(?<!\\)(\$\$?)(.+?)\2')
def handleMatch(self, m):
el = etree.Element('span')
el.text = AtomicString(m.group(2) + m.group(3) + m.group(2))
return el
class MathJaxExtension(markdown.Extension):
def extendMarkdown(self, md, md_globals):
# Needs to come before escape matching because \ is pretty important in LaTeX
md.inlinePatterns.add('mathjax', MathJaxPattern(), '<escape')
def makeExtension(configs=None):
return MathJaxExtension(configs)
#!/usr/bin/env python
'''
Wikipath Extension for Python-Markdown
======================================
Converts [Link Name](wiki:ArticleName) to relative links pointing to article. Requires Python-Markdown 2.0+
Basic usage:
>>> import markdown
>>> text = "Some text with a [Link Name](wiki:ArticleName)."
>>> html = markdown.markdown(text, ['wikipath(base_url="/wiki/view/")'])
>>> html
u'<p>Some text with a <a class="wikipath" href="/wiki/view/ArticleName/">Link Name</a>.</p>'
Dependencies:
* [Python 2.3+](http://python.org)
* [Markdown 2.0+](http://www.freewisdom.org/projects/python-markdown/)
'''
import markdown
try:
# Markdown 2.1.0 changed from 2.0.3. We try importing the new version first,
# but import the 2.0.3 version if it fails
from markdown.util import etree
except:
from markdown import etree
class WikiPathExtension(markdown.Extension):
def __init__(self, configs):
# set extension defaults
self.config = {
'base_url' : ['/', 'String to append to beginning of URL.'],
'html_class' : ['wikipath', 'CSS hook. Leave blank for none.']
}
# Override defaults with user settings
for key, value in configs :
# self.config[key][0] = value
self.setConfig(key, value)
def extendMarkdown(self, md, md_globals):
self.md = md
# append to end of inline patterns
WIKI_RE = r'\[(?P<linkTitle>.+?)\]\(wiki:(?P<wikiTitle>[a-zA-Z\d/_-]*)\)'
wikiPathPattern = WikiPath(WIKI_RE, self.config)
wikiPathPattern.md = md
md.inlinePatterns.add('wikipath', wikiPathPattern, "<reference")
class WikiPath(markdown.inlinepatterns.Pattern):
def __init__(self, pattern, config):
markdown.inlinepatterns.Pattern.__init__(self, pattern)
self.config = config
def handleMatch(self, m) :
article_title = m.group('wikiTitle')
if article_title.startswith("/"):
article_title = article_title[1:]
url = self.config['base_url'][0] + article_title
label = m.group('linkTitle')
a = etree.Element('a')
a.set('href', url)
a.text = label
if self.config['html_class'][0]:
a.set('class', self.config['html_class'][0])
return a
def _getMeta(self):
""" Return meta data or config data. """
base_url = self.config['base_url'][0]
html_class = self.config['html_class'][0]
if hasattr(self.md, 'Meta'):
if self.md.Meta.has_key('wiki_base_url'):
base_url = self.md.Meta['wiki_base_url'][0]
if self.md.Meta.has_key('wiki_html_class'):
html_class = self.md.Meta['wiki_html_class'][0]
return base_url, html_class
def makeExtension(configs=None) :
return WikiPathExtension(configs=configs)
if __name__ == "__main__":
import doctest
doctest.testmod()
# -*- coding: utf-8 -*-
from django.core.urlresolvers import reverse_lazy
from wiki.core import plugins_registry, baseplugin
import wiki
from course_wiki.plugins.markdownedx import mdx_circuit, mdx_wikipath, mdx_mathjax, mdx_video
class ExtendMarkdownPlugin(baseplugin.BasePlugin):
"""
This plugin simply loads all of the markdown extensions we use in edX.
"""
wiki_base_url = reverse_lazy("wiki:get", kwargs={'path' : ""})
markdown_extensions = [mdx_circuit.CircuitExtension(configs={}),
#mdx_image.ImageExtension() , #This one doesn't work. Tries to import simplewiki.settings
mdx_wikipath.WikiPathExtension(configs={'base_url' : wiki_base_url}.iteritems() ) ,
mdx_mathjax.MathJaxExtension(configs={}) ,
mdx_video.VideoExtension(configs={})]
plugins_registry.register(ExtendMarkdownPlugin)
......@@ -120,6 +120,9 @@ MAKO_TEMPLATES['main'] = [PROJECT_ROOT / 'templates',
# still left lying around.
TEMPLATE_DIRS = (
PROJECT_ROOT / "templates",
COMMON_ROOT / 'templates',
COMMON_ROOT / 'lib' / 'capa' / 'capa' / 'templates',
COMMON_ROOT / 'djangoapps' / 'pipeline_mako' / 'templates',
)
TEMPLATE_CONTEXT_PROCESSORS = (
......@@ -297,6 +300,7 @@ SIMPLE_WIKI_REQUIRE_LOGIN_VIEW = False
################################# WIKI ###################################
WIKI_ACCOUNT_HANDLING = False
WIKI_EDITOR = 'course_wiki.editors.CodeMirror'
################################# Jasmine ###################################
JASMINE_TEST_DIRECTORY = PROJECT_ROOT + '/static/coffee'
......@@ -554,12 +558,13 @@ INSTALLED_APPS = (
#For the wiki
'wiki', # The new django-wiki from benjaoming
'course_wiki', # Our customizations
'django_notify',
'course_wiki', # Our customizations
'mptt',
'sekizai',
'wiki.plugins.attachments',
'wiki.plugins.notifications',
'course_wiki.plugins.markdownedx',
# For testing
'django_jasmine',
......
/* ==========================================================
* bootstrap-alert.js v2.0.4
* http://twitter.github.com/bootstrap/javascript.html#alerts
* ==========================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================== */
!function ($) {
"use strict"; // jshint ;_;
/* ALERT CLASS DEFINITION
* ====================== */
var dismiss = '[data-dismiss="alert"]'
, Alert = function (el) {
$(el).on('click', dismiss, this.close)
}
Alert.prototype.close = function (e) {
var $this = $(this)
, selector = $this.attr('data-target')
, $parent
if (!selector) {
selector = $this.attr('href')
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
}
$parent = $(selector)
e && e.preventDefault()
$parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent())
$parent.trigger(e = $.Event('close'))
if (e.isDefaultPrevented()) return
$parent.removeClass('in')
function removeElement() {
$parent
.trigger('closed')
.remove()
}
$.support.transition && $parent.hasClass('fade') ?
$parent.on($.support.transition.end, removeElement) :
removeElement()
}
/* ALERT PLUGIN DEFINITION
* ======================= */
$.fn.alert = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('alert')
if (!data) $this.data('alert', (data = new Alert(this)))
if (typeof option == 'string') data[option].call($this)
})
}
$.fn.alert.Constructor = Alert
/* ALERT DATA-API
* ============== */
$(function () {
$('body').on('click.alert.data-api', dismiss, Alert.prototype.close)
})
}(window.jQuery);
\ No newline at end of file
/* =============================================================
* bootstrap-collapse.js v2.0.4
* http://twitter.github.com/bootstrap/javascript.html#collapse
* =============================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============================================================ */
!function ($) {
"use strict"; // jshint ;_;
/* COLLAPSE PUBLIC CLASS DEFINITION
* ================================ */
var Collapse = function (element, options) {
this.$element = $(element)
this.options = $.extend({}, $.fn.collapse.defaults, options)
if (this.options.parent) {
this.$parent = $(this.options.parent)
}
this.options.toggle && this.toggle()
}
Collapse.prototype = {
constructor: Collapse
, dimension: function () {
var hasWidth = this.$element.hasClass('width')
return hasWidth ? 'width' : 'height'
}
, show: function () {
var dimension
, scroll
, actives
, hasData
if (this.transitioning) return
dimension = this.dimension()
scroll = $.camelCase(['scroll', dimension].join('-'))
actives = this.$parent && this.$parent.find('> .accordion-group > .in')
if (actives && actives.length) {
hasData = actives.data('collapse')
if (hasData && hasData.transitioning) return
actives.collapse('hide')
hasData || actives.data('collapse', null)
}
this.$element[dimension](0)
this.transition('addClass', $.Event('show'), 'shown')
this.$element[dimension](this.$element[0][scroll])
}
, hide: function () {
var dimension
if (this.transitioning) return
dimension = this.dimension()
this.reset(this.$element[dimension]())
this.transition('removeClass', $.Event('hide'), 'hidden')
this.$element[dimension](0)
}
, reset: function (size) {
var dimension = this.dimension()
this.$element
.removeClass('collapse')
[dimension](size || 'auto')
[0].offsetWidth
this.$element[size !== null ? 'addClass' : 'removeClass']('collapse')
return this
}
, transition: function (method, startEvent, completeEvent) {
var that = this
, complete = function () {
if (startEvent.type == 'show') that.reset()
that.transitioning = 0
that.$element.trigger(completeEvent)
}
this.$element.trigger(startEvent)
if (startEvent.isDefaultPrevented()) return
this.transitioning = 1
this.$element[method]('in')
$.support.transition && this.$element.hasClass('collapse') ?
this.$element.one($.support.transition.end, complete) :
complete()
}
, toggle: function () {
this[this.$element.hasClass('in') ? 'hide' : 'show']()
}
}
/* COLLAPSIBLE PLUGIN DEFINITION
* ============================== */
$.fn.collapse = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('collapse')
, options = typeof option == 'object' && option
if (!data) $this.data('collapse', (data = new Collapse(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.collapse.defaults = {
toggle: true
}
$.fn.collapse.Constructor = Collapse
/* COLLAPSIBLE DATA-API
* ==================== */
$(function () {
$('body').on('click.collapse.data-api', '[data-toggle=collapse]', function ( e ) {
var $this = $(this), href
, target = $this.attr('data-target')
|| e.preventDefault()
|| (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
, option = $(target).data('collapse') ? 'toggle' : $this.data()
$(target).collapse(option)
})
})
}(window.jQuery);
\ No newline at end of file
/* ===================================================
* bootstrap-transition.js v2.0.4
* http://twitter.github.com/bootstrap/javascript.html#transitions
* ===================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================== */
!function ($) {
$(function () {
"use strict"; // jshint ;_;
/* CSS TRANSITION SUPPORT (http://www.modernizr.com/)
* ======================================================= */
$.support.transition = (function () {
var transitionEnd = (function () {
var el = document.createElement('bootstrap')
, transEndEventNames = {
'WebkitTransition' : 'webkitTransitionEnd'
, 'MozTransition' : 'transitionend'
, 'OTransition' : 'oTransitionEnd'
, 'msTransition' : 'MSTransitionEnd'
, 'transition' : 'transitionend'
}
, name
for (name in transEndEventNames){
if (el.style[name] !== undefined) {
return transEndEventNames[name]
}
}
}())
return transitionEnd && {
end: transitionEnd
}
})()
})
}(window.jQuery);
\ No newline at end of file
$(document).ready(function() {
var editor = CodeMirror.fromTextArea(document.getElementById("id_content"), {
mode: 'mitx_markdown',
matchBrackets: true,
theme: "default",
lineWrapping: true,
});
//Store the inital contents so we can compare for unsaved changes
var initial_contents = editor.getValue();
window.onbeforeunload = function askConfirm() { //Warn the user before they navigate away
if ( editor.getValue() != initial_contents ) {
return "You have made changes to the article that have not been saved yet.";
}
};
$(".btn-primary").click(function() {
initial_contents = editor.getValue();
});
});
\ No newline at end of file
......@@ -10,6 +10,7 @@ $fg-min-width: 810px;
$sans-serif: 'Open Sans', $verdana;
$body-font-family: $sans-serif;
$serif: $georgia;
$monospace: Monaco, 'Bitstream Vera Sans Mono', 'Lucida Console', monospace;
$body-font-size: em(14);
$body-line-height: golden-ratio(.875em, 1);
......
section.wiki {
padding-top: 25px;
header {
> header {
height: 33px;
margin-bottom: 36px;
padding-bottom: 26px;
......@@ -77,6 +77,10 @@ section.wiki {
padding: 7px 15px !important;
font-size: 0.72em;
font-weight: 600;
&:hover {
text-decoration: none;
}
}
.search-wiki {
......@@ -127,12 +131,14 @@ section.wiki {
width: flex-grid(9);
margin-left: flex-gutter();
color: $base-font-color;
}
&.view .main-article {
h2 {
padding-bottom: 8px;
margin-bottom: 22px;
border-bottom: 1px solid $light-gray;
font-size: 1.33em;
font-size: 1.6em;
font-weight: bold;
color: $base-font-color;
text-transform: none;
......@@ -143,19 +149,29 @@ section.wiki {
margin-top: 40px;
margin-bottom: 20px;
font-weight: bold;
font-size: 1.1em;
font-size: 1.25em;
}
h4 {
margin: 30px 0 10px;
font-size: 1em;
color: #999;
font-weight: bold;
}
h5 {
margin: 20px 0 10px;
font-size: .8em;
font-weight: bold;
text-transform: uppercase;
}
h6 {
margin: 20px 0 10px;
font-size: .8em;
font-weight: bold;
color: #999;
text-transform: uppercase;
}
ul {
......@@ -166,6 +182,16 @@ section.wiki {
li {
margin-bottom: 15px;
line-height: 1.6em;
}
pre {
padding: 10px;
border: 1px solid #ddd;
background: #f8f8f8;
border-radius: 4px;
font-size: 0.9em;
font-family: Monaco, monospace;
}
}
......@@ -283,6 +309,439 @@ section.wiki {
/*-----------------
Edit
-----------------*/
label {
font-family: $sans-serif;
font-size: 0.9em;
font-weight: bold;
font-style: normal;
text-transform: uppercase;
color: #aaa;
}
input {
font-family: $sans-serif;
font-style: normal;
font-weight: normal;
}
#id_title,
#id_content {
width: 100%;
}
#id_content,
.CodeMirror {
font-family: $monospace;
font-size: 0.8em;
line-height: 1.4em;
}
.CodeMirror {
background: #fafafa;
border: 1px solid #c8c8c8;
@include border-radius(3px);
@include box-shadow(0 1px 0 0 rgba(255, 255, 255, 0.6), inset 0 0 3px 0 rgba(0, 0, 0, 0.1));
}
.CodeMirror-scroll {
padding: 7px;
}
.schematic_container {
position: relative;
canvas {
@include box-shadow(0 0 1px 1px rgba(0, 0, 0, .1), 0 1px 6px rgba(0, 0, 0, .2));
}
&:before {
content: 'click to edit schematic';
position: absolute;
top: 10px;
left: 7px;
z-index: 9999;
font-family: $sans-serif;
font-size: 0.75em;
color: #aaa;
pointer-events: none;
}
}
.markItUpContainer {
margin-right: 0;
}
.control-group {
margin-bottom: 20px;
}
#hint_id_summary {
display: inline-block;
font-size: 0.9em;
line-height: 32px;
margin-left: 15px;
}
.asteriskField {
display: none;
}
button {
font-family: $sans-serif;
}
.btn {
@include button(simple, #eee);
font-size: 0.8em;
margin-right: 5px;
line-height: 1.2em;
text-transform: none !important;
letter-spacing: 0 !important;
&:hover {
color: $base-font-color;
text-decoration: none;
}
&.btn-primary {
@include button;
font-size: 0.8em;
}
&.btn-danger {
@include button(simple, $pink);
font-size: 0.8em;
}
&.btn-info {
@include button(simple, #ccc);
font-size: 0.8em;
}
}
a.btn {
padding: 7px 18px 8px !important;
}
.modal {
width: 960px;
z-index: 9999;
min-height: 500px;
margin-left: -480px;
top: 150px;
&.upload-modal,
&.search-file-modal {
width: 400px;
min-height: 0;
margin-left: -200px;
h4 {
margin-bottom: 20px;
font-weight: bold;
}
.help-block {
font-size: 0.8em;
}
}
&.search-file-modal {
width: 500px;
margin-left: -250px;
p {
font-size: 0.8em;
line-height: 1.4em;
}
.form-search {
margin: 30px 0 15px;
input {
width: 350px;
}
button {
height: 35px;
}
}
}
.modal-header {
h1, p {
color: #fff;
}
h1 {
margin: 3px 12px 8px;
font-size: 1.1em;
}
p {
font-size: 0.9em;
margin: 5px 12px 20px;
line-height: 1em;
}
}
.modal-body {
padding-bottom: 8px;
}
iframe {
width: 100%;
min-height: 450px;
border: 0;
}
.modal-footer {
margin: 12px;
}
.modal-footer .btn {
margin-right: 10px;
}
}
.modal-inner-wrapper {
background: #fff;
padding: 20px;
}
#previewWindow body {
background: #f00 !important;
}
/*-----------------
Changes
-----------------*/
&.history {
.accordion {
margin-bottom: 15px;
padding: 15px;
border: 1px solid $light-gray;
background: #f9f9f9;
border-radius: 5px;
a:hover {
text-decoration: none;
}
}
}
.accordion small {
font-size: 0.8em;
color: #aaa;
}
.accordion-toggle div {
margin-top: 8px;
}
.collapse {
display: none;
&.in {
display: block;
}
}
.diff-container {
overflow-x: scroll;
table {
min-width: 100%;
}
th {
font-family: $sans-serif;
font-size: 0.7em;
}
td {
font-family: $monospace;
}
.linenumber,
.data {
font-size: 0.75em;
}
}
/*-----------------
Settings
-----------------*/
#settings_form {
.well {
margin-bottom: 15px;
@include clearfix;
}
.control-group {
float: left;
margin-bottom: 0;
clear: both;
}
label {
margin-left: 15px;
}
.controls {
padding-top: 4px;
}
label,
.controls {
float: right;
}
}
/*-----------------
New
-----------------*/
.new-article {
margin: auto;
float: none;
.add-on {
line-height: 34px;
margin-right: 6px;
}
}
#hint_id_slug {
display: inline-block;
font-size: 0.9em;
line-height: 1.3em;
margin-top: 9px;
}
/*-----------------
Attachments
-----------------*/
.attachment-options {
height: 40px;
margin: 40px 0 30px;
}
.attachment-list {
ul {
list-style: none;
padding: 0;
}
li {
margin-bottom: 15px;
border: 1px solid #DDD;
background: #F9F9F9;
@include border-radius(5px);
}
header,
.attachment-details {
padding: 12px 15px;
}
.attachment-details {
background: #eee;
@include border-radius(0 0 5px 5px);
}
h3 {
a {
font-weight: bold;
font-size: 0.9em;
}
.badge {
float: right;
font-size: 0.6em;
line-height: 20px;
color: #aaa;
}
}
.attachment-description {
font-size: 0.8em;
}
table {
width: 100%;
font-size: 0.8em;
}
.attachment-actions .btn {
float: right;
}
}
/*-----------------
Delete
-----------------*/
#div_id_confirm {
position: relative;
height: 30px;
margin: 40px 0;
#id_confirm {
position: absolute;
top: 6px;
}
label {
position: absolute;
left: 25px;
font-size: 1.4em;
}
}
/*-----------------
Alerts
......@@ -291,7 +750,7 @@ section.wiki {
.alert {
position: relative;
top: -35px;
top: -15px;
margin-bottom: 24px;
padding: 8px 12px;
border: 1px solid #EBE8BF;
......@@ -309,3 +768,80 @@ section.wiki {
}
}
}
.modal-backdrop {
position: fixed;
top: 0;
left: 0;
z-index: 999;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, .4);
}
.modal-preview {
min-width: 0;
}
.modal-preview .container {
padding: 50px;
}
.modal-preview .main-article {
width: 100% !important;
margin-left: 0;
}
#circuit_editor_modal.modal {
width: 648px;
z-index: 9999;
margin-left: -325px;
top: 150px;
.modal-header {
h1, p {
color: #fff;
}
h1 {
margin: 3px 12px 8px;
font-size: 1.1em;
}
p {
font-size: 0.9em;
margin: 5px 12px 20px;
line-height: 1em;
}
}
.modal-body {
padding-bottom: 8px;
}
.modal-footer {
margin: 12px;
}
.modal-footer .btn {
@include button(simple, #eee);
font-size: 0.8em;
margin-right: 5px;
line-height: 1.2em;
text-transform: none !important;
letter-spacing: 0 !important;
&:hover {
color: $base-font-color;
text-decoration: none;
}
&.btn-primary {
@include button;
font-size: 0.8em;
}
margin-right: 10px;
}
}
<!DOCTYPE html>
{% load compressed %}{% load sekizai_tags i18n %}{% load url from future %}
{% load compressed %}{% load sekizai_tags i18n %}{% load url from future %}{% load staticfiles %}
<html>
<head>
{% block title %}<title>edX</title>{% endblock %}
<link rel="icon" type="image/x-icon" href="${static.url('images/favicon.ico')}" />
<link rel="icon" type="image/x-icon" href="{% static "images/favicon.ico" %}" />
{% compressed_css 'application' %}
{% compressed_js 'main_vendor' %}
......
......@@ -13,7 +13,10 @@
<div class="article-wrapper">
<article class="main-article">
{% if selected_tab != "edit" %}
<h1>{{ article.current_revision.title }}</h1>
{% endif %}
{% block wiki_contents_tab %}
{% wiki_render article %}
{% endblock %}
......
{% extends "main_django.html" %}
{% load compressed %}{% load sekizai_tags i18n %}{% load url from future %}
{% load compressed %}{% load sekizai_tags i18n %}{% load url from future %}{% load staticfiles %}
{% block title %}<title>{% block pagetitle %}{% endblock %} | edX Wiki</title>{% endblock %}
{% block headextra %}
{% compressed_css 'course' %}
<script src="{{ STATIC_URL }}js/bootstrap-modal.js"></script>
<script type="text/javascript">
function ajaxError(){}
$.ajaxSetup({
timeout: 7000,
cache: false,
error: function(e, xhr, settings, exception) {
ajaxError();
}
});
function jsonWrapper(url, callback) {
$.getJSON(url, function(data) {
if (data == null) {
ajaxError();
} else {
callback(data);
}
});
}
</script>
{% addtoblock 'js' %}
{% comment %} These scripts load at the bottom of the body {% endcomment %}
<script src="{% static 'js/bootstrap-alert.js' %}"></script>
<script src="{% static 'js/bootstrap-collapse.js' %}"></script>
<script src="{% static 'js/bootstrap-modal.js' %}"></script>
{% with mathjax_mode='wiki' %}
{% include "mathjax_include.html" %}
{% endwith %}
{% endaddtoblock %}
{% endblock %}
......@@ -14,7 +50,7 @@
{% include "course_navigation.html" with active_page_context="wiki" %}
{% endif %}
<section class="container wiki">
<section class="container wiki {{ selected_tab }}">
{% block wiki_body %}
......
{% extends "wiki/base.html" %}
{% load wiki_tags i18n sekizai_tags %}
{% load url from future %}
{% block pagetitle %}{% trans "Add new article" %}{% endblock %}
{% block wiki_contents %}
{% addtoblock "js" %}
<script type="text/javascript" src="{{ STATIC_URL }}admin/js/urlify.js "></script>
<script type="text/javascript">
//<![CDATA[
(function($) {
$(document).ready(function (){
$("#id_title").keyup(function () {
var e = $("#id_slug")[0];
if(!e._changed) {
e.value = URLify(this.value, 64);
}
});
});
})(jQuery);
//]]>
</script>
{% endaddtoblock %}
<article class="main-article new-article">
{% include "wiki/includes/editormedia.html" %}
<h1 class="page-header">{% trans "Add new article" %}</h1>
<form method="POST" class="form-horizontal">
{% wiki_form create_form %}
<div class="form-actions">
<a href="{% url 'wiki:get' path=parent_urlpath.path %}" class="btn btn-large">
<span class="icon-circle-arrow-left"></span>
{% trans "Go back" %}
</a>
<button type="submit" name="save_changes" class="btn btn-primary btn-large">
<span class="icon-plus"></span>
{% trans "Create article" %}
</button>
</div>
</form>
</article>
{% endblock %}
{% extends "wiki/base.html" %}
{% load wiki_tags i18n sekizai_tags %}
{% load url from future %}
{% block pagetitle %}{% trans "Delete article" %}{% endblock %}
{% block wiki_contents %}
<h1 class="page-header">{% trans "Delete" %} "{{ article.current_revision.title }}"</h1>
{% if cannot_delete_root %}
<p class="lead">{% trans "You cannot delete a root article." %}</p>
<p><a href="{% url 'wiki:get' path=urlpath.path article_id=article.id %}">{% trans "Go back" %}</a></p>
{% else %}
{% if cannot_delete_children %}
<p class="alert alert-error"><strong>{% trans "You cannot delete this article because you do not have permission to delete articles with children. Try to remove the children manually one-by-one." %}</strong></p>
{% endif %}
{% if delete_children %}
<p class="lead">{% trans "You are deleting an article. This means that its children will be deleted as well. If you choose to purge, children will also be purged!" %}</p>
<h2>{% trans "Articles that will be deleted" %}</h2>
<ul>
{% for child in delete_children %}
<li><a href="{% url 'wiki:get' article_id=child.article.id %}" target="_blank">{{ child.article }}</a></li>
{% if delete_children_more %}
<li><em>{% trans "...and more!" %}</em></li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
{% if not cannot_delete_children %}
<p class="lead">{% trans "You are deleting an article. Please confirm." %}</p>
<form method="POST" class="form-horizontal">
{% wiki_form delete_form %}
<script type="text/javascript">
$('#id_revision').val('{{ article.current_revision.id }}');
</script>
<div class="form-actions">
<a href="{% url 'wiki:get' path=urlpath.path article_id=article.id %}" class="btn btn-large">
<span class="icon-circle-arrow-left"></span>
{% trans "Go back" %}
</a>
<button type="submit" name="save_changes" class="btn btn-danger btn-large">
<span class="icon-plus"></span>
{% trans "Delete article" %}
</button>
</div>
</form>
{% endif %}
{% endif %}
{% endblock %}
{% extends "wiki/article.html" %}
{% load wiki_tags i18n %}
{% load url from future %}
{% block pagetitle %}{% trans "Edit" %}: {{ article.current_revision.title }}{% endblock %}
{% block wiki_contents_tab %}
<form method="POST" class="form-horizontal" id="article_edit_form" enctype="multipart/form-data">
{% include "wiki/includes/editor.html" %}
<div class="form-actions">
<button type="submit" name="preview" value="1" class="btn btn-large" onclick="$('#previewModal').modal('show'); this.form.target = 'previewWindow'; this.form.action = '{% url 'wiki:preview' path=urlpath.path article_id=article.id %}';">
<span class="icon-eye-open"></span>
{% trans "Preview" %}
</button>
<button type="submit" name="save" value="1" class="btn btn-large btn-primary" onclick="this.form.target=''; this.form.action='{% url 'wiki:edit' path=urlpath.path article_id=article.id %}'">
<span class="icon-ok"></span>
{% trans "Save changes" %}
</button>
<a href="{% url 'wiki:delete' path=urlpath.path article_id=article.id %}" class="pull-right btn btn-danger">
<span class="icon-trash"></span>
{% trans "Delete article" %}
</a>
</div>
<div class="modal hide fade" id="previewModal">
<div class="modal-body">
<iframe name="previewWindow" frameborder="0"></iframe>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-large" data-dismiss="modal">
<span class="icon-circle-arrow-left"></span>
{% trans "Back to editor" %}
</a>
<button type="submit" name="save" value="1" class="btn btn-large btn-primary" onclick="this.form.target=''; this.form.action='{% url 'wiki:edit' path=urlpath.path article_id=article.id %}'">
<span class="icon-ok"></span>
{% trans "Save changes" %}
</button>
</div>
</div>
</form>
{% endblock %}
{% extends "wiki/article.html" %}
{% load wiki_tags i18n sekizai_tags %}
{% load url from future %}
{% block pagetitle %}{% trans "History" %}: {{ article.current_revision.title }}{% endblock %}
{% block wiki_contents_tab %}
{% addtoblock "js" %}
<script type="text/javascript" src="{{ STATIC_URL }}wiki/js/diffview.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}wiki/js/diff.js"></script>
<script type="text/javascript">
$(document).ready(
function() {
$('.accordion input[disabled!="disabled"][type="radio"]').first().attr('checked', 'true');
// Fix modal heights
// $('.modal-body').css('height', $(window).height()*0.70 + 'px');
// $('.modal').css('max-height', $(window).height() + 'px');
});
</script>
{% endaddtoblock %}
{% addtoblock "css" %}
<style type="text/css">
td.linenumber {
width: 20px;
}
tr.insert td {
background-color: #DFC;
}
tr.delete td {
background-color: #FDC;
}
tr.equal td {
background-color: #F2F2F2;
}
.diff-container td {
white-space: pre; font-family: monospace;
}
.diff-container td,
.diff-container th {
padding: 2px 7px;
border-right: 1px solid #DDD;
}
.diff-container td:last-child,
.diff-container th:last-child {
border-right: none;
}
.diff-container table {
border-top: 1px solid #DDD;
}
</style>
{% endaddtoblock %}
<form method="GET">
<div class="tab-content" style="overflow: visible;">
{% for revision in revisions %}
<div class="accordion" id="accordion{{ revision.revision_number }}">
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" style="float: left;" href="#collapse{{ revision.revision_number }}" onclick="get_diff_json('{% url 'wiki:diff' revision.id %}', $('#collapse{{ revision.revision_number }}'))">
{{ revision.created }} (#{{ revision.revision_number }}) by {% if revision.user %}{{ revision.user }}{% else %}{% if user|is_moderator %}{{ revision.ip_address|default:"anonymous (IP not logged)" }}{% else %}{% trans "anonymous (IP logged)" %}{% endif %}{% endif %}
{% if revision == article.current_revision %}
<strong>*</strong>
{% endif %}
{% if revision.deleted %}
<span class="badge badge-important">{% trans "deleted" %}</span>
{% endif %}
{% if revision.previous_revision.deleted and not revision.deleted %}
<span class="badge badge-success">{% trans "restored" %}</span>
{% endif %}
<div style="color: #CCC;">
<small>
{% if revision.user_message %}
{{ revision.user_message }}
{% else %}
({% trans "no log message" %})
{% endif %}
</small>
</div>
</a>
<div class="progress progress-striped active" style="display: none; width: 40px; float: left; margin-top: 7px; margin-bottom: -7px;">
<div class="bar" style="width: 100%;"></div>
</div>
<div class="pull-right" style="vertical-align: middle; margin: 8px 3px;">
{% if revision == article.current_revision %}
<a href="#" class="btn disabled">
<span class="icon-lock"></span>
{% trans "Preview this version" %}
</a>
{% else %}
<button type="submit" class="btn" onclick="$('#previewModal').modal('show'); this.form.target='previewWindow'; this.form.r.value='{{ revision.id }}'; this.form.action='{% url 'wiki:preview_revision' article.id %}'; $('#previewModal .switch-to-revision').attr('href', '{% url 'wiki:change_revision' path=urlpath.path article_id=article.id revision_id=revision.id %}')">
<span class="icon-eye-open"></span>
{% trans "Preview this version" %}
</button>
{% endif %}
<a class="btn btn-info" href="#collapse{{ revision.revision_number }}" onclick="get_diff_json('{% url 'wiki:diff' revision_id=revision.id %}', $('#collapse{{ revision.revision_number }}'))">
<span class="icon-list-alt"></span>
{% trans "Show changes" %}
</a>
{% if article|can_write:user %}
<input type="radio"{% if revision == article.current_revision %} disabled="true"{% endif %} style="margin: 0 10px;" value="{{ revision.id }}" name="revision_id" switch-button-href="{% url 'wiki:change_revision' path=urlpath.path revision_id=revision.id %}" merge-button-href="{% url 'wiki:merge_revision_preview' article_id=article.id revision_id=revision.id %}" merge-button-commit-href="{% url 'wiki:merge_revision' path=urlpath.path article_id=article.id revision_id=revision.id %}" />
{% endif %}
</div>
<div style="clear: both"></div>
</div>
<div id="collapse{{ revision.revision_number }}" class="accordion-body collapse">
<div class="accordion-inner diff-container" style="padding: 0;">
<dl class="dl-horizontal">
<dt>{% trans "Auto log:" %}</dt>
<dd>{{ revision.automatic_log|default:"-"|linebreaksbr }}</dd>
</dl>
<table class="table table-condensed" style="margin: 0; border-collapse: collapse;">
<thead>
<tr>
<th class="linenumber">{% if revision.previous_revision %}#{{revision.previous_revision.revision_number}}{% endif %}</th>
<th class="linenumber">#{{revision.revision_number}}</th>
<th>{% trans "Change" %}</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
{% endfor %}
{% include "wiki/includes/pagination.html" %}
{% if revisions.count > 1 %}
<div class="form-actions">
<div class="pull-right">
{% if article|can_write:user %}
<button type="submit" name="preview" value="1" class="btn btn-large" onclick="$('#mergeModal').modal('show'); this.form.target='mergeWindow'; this.form.action=$('input[type=radio]:checked').attr('merge-button-href'); $('.merge-revision-commit').attr('href', $('input[type=radio]:checked').attr('merge-button-commit-href'))">
<span class="icon-random"></span>
{% trans "Merge selected with current..." %}
</button>
{% else %}
<button type="submit" disabled="true" name="preview" value="1" class="btn btn-large">
<span class="icon-lock"></span>
{% trans "Merge selected with current..." %}
</button>
{% endif %}
<button type="submit" name="save" value="1" class="btn btn-large btn-primary" onclick="$('#previewModal').modal('show'); this.form.target='previewWindow'; this.form.action=$('input[type=radio]:checked').attr('switch-button-href')">
<span class="icon-flag"></span>
{% trans "Switch to selected version" %}
</button>
</div>
</div>
{% endif %}
</div>
<input type="hidden" name="r" value="" />
<div class="modal hide fade" id="previewModal">
<div class="modal-body">
<iframe name="previewWindow" frameborder="0"></iframe>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-large" data-dismiss="modal">
<span class="icon-circle-arrow-left"></span>
{% trans "Back to history view" %}
</a>
{% if article|can_write:user %}
<a href="#" class="btn btn-large btn-primary switch-to-revision">
<span class="icon-flag"></span>
{% trans "Switch to this version" %}
</a>
{% else %}
<a href="#" class="btn btn-large btn-primary disabled">
<span class="icon-lock"></span>
{% trans "Switch to this version" %}
</a>
{% endif %}
</div>
</div>
<div class="modal hide fade" id="mergeModal">
<div class="modal-header">
<h1>{% trans "Merge with current" %}</h1>
<p class="lead"><span class="icon-info-sign"></span> {% trans "When you merge a revision with the current, all data will be retained from both versions and merged at its approximate location from each revision." %} <strong>{% trans "After this, it's important to do a manual review." %}</strong></p>
</div>
<div class="modal-body">
<iframe name="mergeWindow" frameborder="0"></iframe>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-large" data-dismiss="modal">
<span class="icon-circle-arrow-left"></span>
{% trans "Back to history view" %}
</a>
{% if article|can_write:user %}
<a href="#" class="btn btn-large btn-primary merge-revision-commit">
<span class="icon-file"></span>
{% trans "Create new merged version" %}
</a>
{% else %}
<a href="#" class="btn btn-large btn-primary disabled">
<span class="icon-lock"></span>
{% trans "Create new merged version" %}
</a>
{% endif %}
</div>
</div>
</form>
{% endblock %}
{% load i18n %}
{% load url from future %}
<em>
{% url 'wiki:signup' as signup_url %}
{% url 'wiki:login' as login_url %}
{% if login_url and signup_url %}
{% blocktrans %}
You need to <a href="{{ login_url }}">log in</a> or <a href="{{ signup_url }}">sign up</a> to use this function.
{% endblocktrans %}
{% else %}
{% trans "You need to log in or sign up to use this function." %}
{% endif %}
</em>
{% load i18n wiki_tags %}{% load url from future %}
## mako
<%! from django.core.urlresolvers import reverse %>
{% with selected_tab as selected %}
<li class="{% if selected == "view" %} active{% endif %}">
<a href="{% url 'wiki:get' article_id=article.id path=urlpath.path %}">
<span class="icon icon-view"></span>
{% trans "View" %}
<li class="${"active" if selected_tab == "view" else ""}">
<a href="${reverse('wiki:get', kwargs={'article_id' : article.id, 'path' : urlpath.path})}">
<span class="icon-home"></span>
View
</a>
</li>
<li class="{% if selected == "edit" %} active{% endif %}">
<a href="{% url 'wiki:edit' article_id=article.id path=urlpath.path %}">
<span class="icon icon-edit"></span>
{% trans "Edit" %}
%if article.can_write(user):
<li class="${"active" if selected_tab == "edit" else ""}">
<a href="${reverse('wiki:edit', kwargs={'article_id' : article.id, 'path' : urlpath.path})}">
<span class="icon-edit"></span>
Edit
</a>
</li>
<li class="{% if selected == "history" %} active{% endif %}">
<a href="{% url 'wiki:history' article_id=article.id path=urlpath.path %}">
<span class="icon icon-changes"></span>
{% trans "Changes" %}
</li>
%endif
<li class="${"active" if selected_tab == "history" else ""}">
<a href="${reverse('wiki:history', kwargs={'article_id' : article.id, 'path' : urlpath.path})}">
<span class="icon-time"></span>
Changes
</a>
</li>
{% for plugin in article_tabs %}
<li class="{% if selected == plugin.slug %} active{% endif %}">
<a href="{% url 'wiki:plugin' slug=plugin.slug article_id=article.id path=urlpath.path %}">
<span class="icon icon-attachments {{ plugin.article_tab.1 }}"></span>
{{ plugin.article_tab.0 }}
%for plugin in article_tabs:
%if hasattr(plugin, "article_tab"):
<li class="${"active" if selected_tab == plugin.slug else ""}">
<a href="${reverse('wiki:plugin', kwargs={'slug' : plugin.slug, 'article_id' : article.id, 'path' : urlpath.path}) }">
<span class="${plugin.article_tab[1]}"></span>
${plugin.article_tab[0]}
</a>
</li>
{% endfor %}
<li class="{% if selected == "settings" %} active{% endif %}">
{% if not user.is_anonymous %}
<a href="{% url 'wiki:settings' article_id=article.id path=urlpath.path %}">
<span class="icon icon-settings"></span>
{% trans "Settings" %}
%endif
%endfor
%if not user.is_anonymous():
<li class="${"active" if selected_tab == "settings" else ""}">
<a href="${reverse('wiki:settings', kwargs={'article_id' : article.id, 'path' : urlpath.path})}">
<span class="icon-wrench"></span>
Settings
</a>
{% endif %}
</li>
%endif
{% endwith %}
{% load wiki_tags i18n %}
{% include "wiki/includes/editormedia.html" %}
<div>
{% wiki_form edit_form %}
<script language="javascript">
$(document).ready(function() {
$("#id_revision").val('{{ article.current_revision.id }}');
});
</script>
</div>
<div>
<div>
{% for plugin in sidebar %}
<div class="accordion" id="accordion_{{ plugin.slug }}">
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" href="#collapse_{{ plugin.slug }}" data-toggle="collapse">
<h2>{{ plugin.sidebar.headline }} <span class="{{ plugin.sidebar.icon_class }}"></span></h2>
</a>
</div>
<div id="collapse_{{ plugin.slug }}" class="accordion-body collapse{% if form_images.errors %} in{% endif %}">
<div class="accordion-inner form-vertical">
{% if plugin.sidebar.template %}
{% with form_images as form and plugin as plugin %}
{% include plugin.sidebar.template %}
{% endwith %}
{% endif %}
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% csrf_token %}
{% if form.non_field_errors %}
{% if form_error_title %}<h4 class="alert-heading">{{ form_error_title }}</h4>{% endif %}
{% for error_message in form.non_field_errors %}
<div class="alert alert-block alert-error">
{{ error_message }}
</div>
{% endfor %}
{% endif %}
{% for field in form %}
{% if field.is_hidden %}
{{ field }}
{% else %}
<div id="div_{{ field.auto_id }}" class="clearfix control-group{% if field.errors %} error{% endif %}">
{% if field.label %}
<label for="{{ field.id_for_label }}" class="control-label {% if field.field.required %}requiredField{% endif %}">
{{ field.label|safe }}
</label>
{% endif %}
<div class="controls">
{{ field }} {% if field.field.required %}<span class="asteriskField">*</span>{% endif %}
{% if field.errors %}
{% for error in field.errors %}
<div id="error_{{ forloop.counter }}_{{ field.auto_id }}" class="help-block"><strong>{{ error }}</strong></div>
{% endfor %}
{% endif %}
{% if field.help_text %}
<p id="hint_{{ field.auto_id }}" class="help-block">{{ field.help_text|safe }}</p>
{% endif %}
</div>
</div>
{% endif %}
{% endfor %}
{% extends "wiki/article.html" %}
{% load wiki_tags i18n humanize %}
{% load url from future %}
{% block pagetitle %}{% trans "Attachments" %}: {{ article.current_revision.title }}{% endblock %}
{% block wiki_contents_tab %}
<div class="row-fluid">
{% if article|can_write:user %}
<div class="attachment-options">
<a class="btn" href="#" id="upload-file-btn">
<span class="icon-upload"></span>{% trans "Upload new file" %}
</a>
<a class="btn" href="#" id="search-for-file-btn">
<span class="icon-plus-sign"></span>{% trans "Search and add file" %}
</a>
</div>
<div class="modal upload-modal hide fade" id="upload-modal">
<div class="modal-inner-wrapper">
<h4>Upload File</h4>
<form method="POST" class="form-vertical" id="attachment_form" enctype="multipart/form-data">
{% wiki_form form %}
<button type="submit" name="save" value="1" class="btn btn-primary">
{% trans "Upload file" %}
</button>
</form>
</div>
</div>
<div class="modal search-file-modal hide fade" id="search-file-modal">
<div class="modal-inner-wrapper">
<h4>Search files and articles</h4>
<p>{% trans "You can reuse files from other articles. These files are subject to updates on other articles which may or may not be a good thing." %}</p>
<form method="GET" action="{% url 'wiki:attachments_search' path=urlpath.path article_id=article.id %}" class="form-search">
{{ search_form.query }}
<button class="btn btn-primary">
{% trans "Search" %}
</button>
</form>
</div>
</div>
<script type="text/javascript">
$('#upload-file-btn').bind('click', function(e) {
{% if anonymous_disallowed %}
console.log('you cannot do that!');
{% else %}
$('#upload-modal').modal('show');
{% endif %}
});
$('#search-for-file-btn').bind('click', function(e) {
$('#search-file-modal').modal('show');
});
</script>
{% endif %}
<div class="attachment-list">
<!--<p class="lead">{% trans "The following files are available for this article. Copy the markdown tag to directly refer to a file from the article text." %}</p>-->
<ul>
{% for attachment in attachments %}
<li>
<header>
<h3>
<a href="{% url 'wiki:attachments_download' path=urlpath.path article_id=article.id attachment_id=attachment.id %}">{{ attachment.current_revision.get_filename }}</a>
<span class="badge">{{ attachment.current_revision.created|naturaltime }}</span>
{% if attachment.current_revision.deleted %}
<span class="badge badge-important">{% trans "deleted" %}</span>
{% endif %}
</h3>
<p class="attachment-description">
{{ attachment.current_revision.description }}
</p>
</header>
<div class="attachment-details">
<table>
<tr>
<th>{% trans "Markdown tag" %}</th>
<th>{% trans "Uploaded by" %}</th>
<th>{% trans "Size" %}</th>
<th>{% trans "File History" %}</th>
<td class="attachment-actions">
{% if attachment|can_write:user %}
{% if not attachment.current_revision.deleted %}
{% if attachment.article = article %}
<a href="{% url 'wiki:attachments_delete' path=urlpath.path article_id=article.id attachment_id=attachment.id %}" class="btn btn-danger">{% trans "Delete" %}</a>
{% else %}
<a href="{% url 'wiki:attachments_delete' path=urlpath.path article_id=article.id attachment_id=attachment.id %}" class="btn">{% trans "Detach" %}</a>
{% endif %}
<a href="{% url 'wiki:attachments_replace' path=urlpath.path article_id=article.id attachment_id=attachment.id %}" class="btn">{% trans "Replace" %}</a>
{% else %}
{% if attachment.current_revision.previous_revision.id %}
<form method="POST" action="{% url 'wiki:attachments_revision_change' path=urlpath.path article_id=article.id attachment_id=attachment.id revision_id=attachment.current_revision.previous_revision.id %}">
{% csrf_token %}
<button class="btn">
{% trans "Restore" %}
</button>
</form>
{% endif %}
{% endif %}
{% endif %}
</td>
</tr>
<tr>
<td><code>[attachment:{{ attachment.id }}]</code></td>
<td>
{% if attachment.current_revision.user %}{{ attachment.current_revision.user }}{% else %}{% if user|is_moderator %}{{ attachment.current_revision.ip_address|default:"anonymous (IP not logged)" }}{% else %}{% trans "anonymous (IP logged)" %}{% endif %}{% endif %}
</td>
<td>{{ attachment.current_revision.get_size|filesizeformat }}</td>
<td>{{ attachment.attachmentrevision_set.all.count }} {% trans "revisions" %}</td>
</tr>
</table>
</div>
</li>
{% empty %}
<p style="margin-bottom: 20px;"><em>{% trans "There are no attachments for this article." %}</em></p>
{% endfor %}
</ul>
</div>
</div>
{% endblock %}
......@@ -3,9 +3,11 @@
<html>
<head>
{% compressed_css 'course' %}
{% compressed_js 'main_vendor' %}
</head>
<body>
<section class="content-wrapper">
<body class="modal-preview">
<section class="container wiki view">
<div class="main-article">
{% if revision %}
<div class="alert alert-info">
<strong>{% trans "Previewing revision" %}:</strong> {{ revision.created }} (#{{ revision.revision_number }}) by {% if revision.user %}{{ revision.user }}{% else %}{% if user|is_moderator %}{{ revision.ip_address|default:"anonymous (IP not logged)" }}{% else %}{% trans "anonymous (IP logged)" %}{% endif %}{% endif %}
......@@ -24,7 +26,16 @@
<h1 class="page-header">{{ title }}</h1>
{% wiki_render article content %}
</div>
</section>
{% compressed_js 'application' %}
{% compressed_js 'module-js' %}
{% with mathjax_mode='wiki' %}
{% include "mathjax_include.html" %}
{% endwith %}
</body>
</html>
......
{% extends "wiki/base.html" %}
{% load wiki_tags i18n %}
{% load url from future %}
{% block pagetitle %}{% trans "Settings" %}: {{ article.current_revision.title }}{% endblock %}
{% block wiki_breadcrumbs %}
{% include "wiki/includes/breadcrumbs.html" %}
{% endblock %}
{% block wiki_contents %}
<div class="article-wrapper">
<article class="main-article">
{% if selected_tab != "edit" %}
<h1>{{ article.current_revision.title }}</h1>
{% endif %}
{% for form in forms %}
<form method="POST" class="form-horizontal" id="settings_form" action="?f={{form.action}}">
<h2>{{ form.settings_form_headline }}</h2>
<div class="well">
{% wiki_form form %}
</div>
<div class="form-actions">
<button type="submit" name="save" value="1" class="btn btn-large btn-primary">
<span class="icon-ok"></span>
{% trans "Save changes" %}
</button>
</div>
</form>
{% endfor %}
</article>
<div class="article-functions">
<div class="timestamp">
<span class="label">{% trans "Last modified:" %}</span><br />
<span class="date">{{ article.current_revision.modified }}</span>
</div>
<ul class="nav nav-tabs">
{% with "settings" as selected %}
{% include "wiki/includes/article_menu.html" %}
{% endwith %}
</ul>
</div>
</div>
{% endblock %}
......@@ -44,5 +44,5 @@ django-ses
django-storages
django-threaded-multihost
django-sekizai<0.7
git+git://github.com/benjaoming/django-wiki.git@484ff1ce49
git+git://github.com/benjaoming/django-wiki.git@97f8413
-r repo-requirements.txt
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment