Commit 28007b28 by bridger

Merge pull request #168 from MITx/multicourse_wiki

Multicourse wiki
parents bc21cdfa ae955ec4
......@@ -29,6 +29,10 @@ class CourseDescriptor(SequenceDescriptor):
@property
def instructors(self):
return self.get_about_section("instructors").split("\n")
@property
def wiki_namespace(self):
return self.location.course
def get_about_section(self, section_key):
"""
......
......@@ -14,6 +14,7 @@ _FIELDS = ['number', # 6.002x
'path', # /some/absolute/filepath/6.002x --> course.xml is in here.
'instructors', # ['Anant Agarwal']
'institution', # "MIT"
'wiki_namespace',
'grader', # a courseware.graders.CourseGrader object
#'start', # These should be datetime fields
......
......@@ -34,7 +34,7 @@ class ArticleAdminForm(forms.ModelForm):
model = Article
class ArticleAdmin(admin.ModelAdmin):
list_display = ('created_by', 'slug', 'modified_on', 'parent')
list_display = ('created_by', 'slug', 'modified_on', 'namespace')
search_fields = ('slug',)
prepopulated_fields = {'slug': ('title',) }
inlines = [RevisionInline]
......
......@@ -17,8 +17,6 @@ circuit-schematic:[["r",[128,48,0],{"r":"1","_json_":0},["2","1"]],["view",0,0,2
import markdown
import re
import simplewiki.settings as settings
from django.utils.html import escape
try:
......@@ -68,5 +66,7 @@ class CircuitLink(markdown.inlinepatterns.Pattern):
return etree.fromstring("<div align='center'><input type='hidden' parts='' value='" + data + "' analyses='' class='schematic ctrls' width='640' height='480'/></div>")
def makeExtension(configs=None) :
return CircuitExtension(configs=configs)
def makeExtension(configs=None):
to_return = CircuitExtension(configs=configs)
print "circuit returning " , to_return
return to_return
......@@ -33,7 +33,7 @@ class WikiPathExtension(markdown.Extension):
def __init__(self, configs):
# set extension defaults
self.config = {
'base_url' : ['/', 'String to append to beginning or URL.'],
'default_namespace' : ['edX', 'Default namespace for when one isn\'t specified.'],
'html_class' : ['wikipath', 'CSS hook. Leave blank for none.']
}
......@@ -62,7 +62,10 @@ class WikiPath(markdown.inlinepatterns.Pattern):
if article_title.startswith("/"):
article_title = article_title[1:]
url = self.config['base_url'][0] + article_title
if not "/" in article_title:
article_title = self.config['default_namespace'][0] + "/" + article_title
url = "../" + article_title
label = m.group('linkTitle')
a = etree.Element('a')
a.set('href', url)
......
......@@ -16,9 +16,23 @@ from util.cache import cache
class ShouldHaveExactlyOneRootSlug(Exception):
pass
class Namespace(models.Model):
name = models.CharField(max_length=30, db_index=True, unique=True, verbose_name=_('namespace'))
# TODO: We may want to add permissions, etc later
@classmethod
def ensure_namespace(cls, name):
try:
namespace = Namespace.objects.get(name__exact = name)
except Namespace.DoesNotExist:
new_namespace = Namespace(name=name)
new_namespace.save()
class Article(models.Model):
"""Wiki article referring to Revision model for actual content.
'slug' and 'parent' field should be maintained centrally, since users
'slug' and 'title' field should be maintained centrally, since users
aren't allowed to change them, anyways.
"""
......@@ -27,12 +41,10 @@ class Article(models.Model):
slug = models.SlugField(max_length=100, verbose_name=_('slug'),
help_text=_('Letters, numbers, underscore and hyphen.'),
blank=True)
namespace = models.ForeignKey(Namespace, verbose_name=_('Namespace'))
created_by = models.ForeignKey(User, verbose_name=_('Created by'), blank=True, null=True)
created_on = models.DateTimeField(auto_now_add = 1)
modified_on = models.DateTimeField(auto_now_add = 1)
parent = models.ForeignKey('self', verbose_name=_('Parent article slug'),
help_text=_('Affects URL structure and possibly inherits permissions'),
null=True, blank=True)
locked = models.BooleanField(default=False, verbose_name=_('Locked for editing'))
permissions = models.ForeignKey('Permission', verbose_name=_('Permissions'),
blank=True, null=True,
......@@ -45,53 +57,44 @@ class Article(models.Model):
def attachments(self):
return ArticleAttachment.objects.filter(article__exact = self)
def get_path(self):
return self.namespace.name + "/" + self.slug
@classmethod
def get_article(cls, article_path):
"""
Given an article_path like namespace/slug, this returns the article. It may raise
a Article.DoesNotExist if no matching article is found or ValueError if the
article_path is not constructed properly.
"""
#TODO: Verify the path, throw a meaningful error?
namespace, slug = article_path.split("/")
return Article.objects.get( slug__exact = slug, namespace__name__exact = namespace)
@classmethod
def get_root(cls):
def get_root(cls, namespace):
"""Return the root article, which should ALWAYS exist..
except the very first time the wiki is loaded, in which
case the user is prompted to create this article."""
try:
return Article.objects.filter(slug__exact = "")[0]
return Article.objects.filter(slug__exact = "", namespace__name__exact = namespace)[0]
except:
raise ShouldHaveExactlyOneRootSlug()
def get_url(self):
"""Return the Wiki URL for an article"""
url = self.slug + "/"
if self.parent_id:
parent_url = cache.get("wiki_url-" + str(self.parent_id))
if parent_url is None:
parent_url = self.parent.get_url()
url = parent_url + url
cache.set("wiki_url-" + str(self.id), url, 60*60)
return url
def get_abs_url(self):
"""Return the absolute path for an article. This is necessary in cases
where the template system isn't used for generating URLs..."""
# TODO: Remove and create a reverse() lookup.
return WIKI_BASE + self.get_url()
@models.permalink
def get_absolute_url(self):
return ('wiki_view', [self.get_url()])
@classmethod
def get_url_reverse(cls, path, article, return_list=[]):
"""Lookup a URL and return the corresponding set of articles
in the path."""
if path == []:
return return_list + [article]
# Lookup next child in path
try:
a = Article.objects.get(parent__exact = article, slug__exact=str(path[0]))
return cls.get_url_reverse(path[1:], a, return_list+[article])
except Exception, e:
return None
# @classmethod
# def get_url_reverse(cls, path, article, return_list=[]):
# """Lookup a URL and return the corresponding set of articles
# in the path."""
# if path == []:
# return return_list + [article]
# # Lookup next child in path
# try:
# a = Article.objects.get(parent__exact = article, slug__exact=str(path[0]))
# return cls.get_url_reverse(path[1:], a, return_list+[article])
# except Exception, e:
# return None
def can_read(self, user):
""" Check read permissions and return True/False."""
......@@ -101,7 +104,8 @@ class Article(models.Model):
perms = self.permissions.can_read.all()
return perms.count() == 0 or (user in perms)
else:
return self.parent.can_read(user) if self.parent else True
# TODO: We can inherit namespace permissions here
return True
def can_write(self, user):
""" Check write permissions and return True/False."""
......@@ -111,7 +115,8 @@ class Article(models.Model):
perms = self.permissions.can_write.all()
return perms.count() == 0 or (user in perms)
else:
return self.parent.can_write(user) if self.parent else True
# TODO: We can inherit namespace permissions here
return True
def can_write_l(self, user):
"""Check write permissions and locked status"""
......@@ -123,13 +128,13 @@ class Article(models.Model):
return self.can_write_l(user) and (WIKI_ALLOW_ANON_ATTACHMENTS or not user.is_anonymous())
def __unicode__(self):
if self.slug == '' and not self.parent:
if self.slug == '':
return unicode(_('Root article'))
else:
return self.get_url()
return self.slug
class Meta:
unique_together = (('slug', 'parent'),)
unique_together = (('slug', 'namespace'),)
verbose_name = _('Article')
verbose_name_plural = _('Articles')
......@@ -275,7 +280,7 @@ class Revision(models.Model):
# Create pre-parsed contents - no need to parse on-the-fly
ext = WIKI_MARKDOWN_EXTENSIONS
ext += ["wikipath(base_url=%s)" % reverse('wiki_view', args=('/',))]
ext += ["wikipath(default_namespace=%s)" % self.article.namespace.name ]
self.contents_parsed = markdown(self.contents,
extensions=ext,
safe_mode='escape',)
......
from django.conf.urls.defaults import *
from django.conf.urls.defaults import patterns, url
namespace_regex = r"[a-zA-Z\d._-]+"
article_slug = r'/(?P<article_path>' + namespace_regex + r'/[a-zA-Z\d_-]*)'
namespace = r'/(?P<namespace>' + namespace_regex + r')'
urlpatterns = patterns('',
url(r'^$', 'simplewiki.views.root_redirect', name='wiki_root'),
url(r'^view(/[a-zA-Z\d/_-]*)/?$', 'simplewiki.views.view', name='wiki_view'),
url(r'^view_revision/([0-9]*)(/[a-zA-Z\d/_-]*)/?$', 'simplewiki.views.view_revision', name='wiki_view_revision'),
url(r'^edit(/[a-zA-Z\d/_-]*)/?$', 'simplewiki.views.edit', name='wiki_edit'),
url(r'^create(/[a-zA-Z\d/_-]*)/?$', 'simplewiki.views.create', name='wiki_create'),
url(r'^history(/[a-zA-Z\d/_-]*)/([0-9]*)/?$', 'simplewiki.views.history', name='wiki_history'),
url(r'^search_related(/[a-zA-Z\d/_-]*)/?$', 'simplewiki.views.search_add_related', name='search_related'),
url(r'^random/?$', 'simplewiki.views.random_article', name='wiki_random'),
url(r'^revision_feed/([0-9]*)/?$', 'simplewiki.views.revision_feed', name='wiki_revision_feed'),
url(r'^search/?$', 'simplewiki.views.search_articles', name='wiki_search_articles'),
url(r'^list/?$', 'simplewiki.views.search_articles', name='wiki_list_articles'), #Just an alias for the search, but you usually don't submit a search term
# url(r'^/?([a-zA-Z\d/_-]*)/_related/add/$', 'simplewiki.views.add_related', name='add_related'),
# url(r'^/?([a-zA-Z\d/_-]*)/_related/remove/(\d+)$', 'simplewiki.views.remove_related', name='wiki_remove_relation'),
# url(r'^/?([a-zA-Z\d/_-]*)/_add_attachment/$', 'simplewiki.views_attachments.add_attachment', name='add_attachment'),
# url(r'^/?([a-zA-Z\d/_-]*)/_view_attachment/(.+)?$', 'simplewiki.views_attachments.view_attachment', name='wiki_view_attachment'),
# url(r'^(.*)$', 'simplewiki.views.encode_err', name='wiki_encode_err')
url(r'^$', 'simplewiki.views.root_redirect', name='wiki_root'),
url(r'^view' + article_slug, 'simplewiki.views.view', name='wiki_view'),
url(r'^view_revision/(?P<revision_number>[0-9]+)' + article_slug, 'simplewiki.views.view_revision', name='wiki_view_revision'),
url(r'^edit' + article_slug, 'simplewiki.views.edit', name='wiki_edit'),
url(r'^create' + article_slug, 'simplewiki.views.create', name='wiki_create'),
url(r'^history' + article_slug + r'(?:/(?P<page>[0-9]+))?$', 'simplewiki.views.history', name='wiki_history'),
url(r'^search_related' + article_slug, 'simplewiki.views.search_add_related', name='search_related'),
url(r'^random/?$', 'simplewiki.views.random_article', name='wiki_random'),
url(r'^revision_feed' + namespace + r'/(?P<page>[0-9]+)?$', 'simplewiki.views.revision_feed', name='wiki_revision_feed'),
url(r'^search' + namespace + r'?$', 'simplewiki.views.search_articles', name='wiki_search_articles'),
url(r'^list' + namespace + r'?$', 'simplewiki.views.search_articles', name='wiki_list_articles'), #Just an alias for the search, but you usually don't submit a search term
)
.CodeMirror {
line-height: 1em;
font-family: monospace;
}
.CodeMirror-scroll {
overflow: auto;
height: 300px;
/* This is needed to prevent an IE[67] bug where the scrolled content
is visible outside of the scrolling box. */
position: relative;
outline: none;
}
.CodeMirror-gutter {
position: absolute; left: 0; top: 0;
z-index: 10;
background-color: #f7f7f7;
border-right: 1px solid #eee;
min-width: 2em;
height: 100%;
}
.CodeMirror-gutter-text {
color: #aaa;
text-align: right;
padding: .4em .2em .4em .4em;
white-space: pre !important;
}
.CodeMirror-lines {
padding: .4em;
white-space: pre;
}
.CodeMirror pre {
-moz-border-radius: 0;
-webkit-border-radius: 0;
-o-border-radius: 0;
border-radius: 0;
border-width: 0; margin: 0; padding: 0; background: transparent;
font-family: inherit;
font-size: inherit;
padding: 0; margin: 0;
white-space: pre;
word-wrap: normal;
}
.CodeMirror-wrap pre {
word-wrap: break-word;
white-space: pre-wrap;
}
.CodeMirror-wrap .CodeMirror-scroll {
overflow-x: hidden;
}
.CodeMirror textarea {
outline: none !important;
}
.CodeMirror pre.CodeMirror-cursor {
z-index: 10;
position: absolute;
visibility: hidden;
border-left: 1px solid black;
border-right:none;
width:0;
}
.CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {}
.CodeMirror-focused pre.CodeMirror-cursor {
visibility: visible;
}
div.CodeMirror-selected { background: #d9d9d9; }
.CodeMirror-focused div.CodeMirror-selected { background: #d7d4f0; }
.CodeMirror-searching {
background: #ffa;
background: rgba(255, 255, 0, .4);
}
/* Default theme */
.cm-s-default span.cm-keyword {color: #708;}
.cm-s-default span.cm-atom {color: #219;}
.cm-s-default span.cm-number {color: #164;}
.cm-s-default span.cm-def {color: #00f;}
.cm-s-default span.cm-variable {color: black;}
.cm-s-default span.cm-variable-2 {color: #05a;}
.cm-s-default span.cm-variable-3 {color: #085;}
.cm-s-default span.cm-property {color: black;}
.cm-s-default span.cm-operator {color: black;}
.cm-s-default span.cm-comment {color: #a50;}
.cm-s-default span.cm-string {color: #a11;}
.cm-s-default span.cm-string-2 {color: #f50;}
.cm-s-default span.cm-meta {color: #555;}
.cm-s-default span.cm-error {color: #f00;}
.cm-s-default span.cm-qualifier {color: #555;}
.cm-s-default span.cm-builtin {color: #30a;}
.cm-s-default span.cm-bracket {color: #cc7;}
.cm-s-default span.cm-tag {color: #170;}
.cm-s-default span.cm-attribute {color: #00c;}
.cm-s-default span.cm-header {color: #a0a;}
.cm-s-default span.cm-quote {color: #090;}
.cm-s-default span.cm-hr {color: #999;}
.cm-s-default span.cm-link {color: #00c;}
span.cm-header, span.cm-strong {font-weight: bold;}
span.cm-em {font-style: italic;}
span.cm-emstrong {font-style: italic; font-weight: bold;}
span.cm-link {text-decoration: underline;}
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
This source diff could not be displayed because it is too large. You can view the blob instead.
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},
doNotIndent: {"pre": true},
allowUnquoted: true,
allowMissing: false
} : {autoSelfClosers: {}, doNotIndent: {}, allowUnquoted: false, allowMissing: false};
var alignCDATA = parserConfig.alignCDATA;
// Return variables for tokenizers
var tagName, type;
function inText(stream, state) {
function chain(parser) {
state.tokenize = parser;
return parser(stream, state);
}
var ch = stream.next();
if (ch == "<") {
if (stream.eat("!")) {
if (stream.eat("[")) {
if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
else return null;
}
else if (stream.match("--")) return chain(inBlock("comment", "-->"));
else if (stream.match("DOCTYPE", true, true)) {
stream.eatWhile(/[\w\._\-]/);
return chain(doctype(1));
}
else return null;
}
else if (stream.eat("?")) {
stream.eatWhile(/[\w\._\-]/);
state.tokenize = inBlock("meta", "?>");
return "meta";
}
else {
type = stream.eat("/") ? "closeTag" : "openTag";
stream.eatSpace();
tagName = "";
var c;
while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c;
state.tokenize = inTag;
return "tag";
}
}
else if (ch == "&") {
var ok;
if (stream.eat("#")) {
if (stream.eat("x")) {
ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
} else {
ok = stream.eatWhile(/[\d]/) && stream.eat(";");
}
} else {
ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
}
return ok ? "atom" : "error";
}
else {
stream.eatWhile(/[^&<]/);
return null;
}
}
function inTag(stream, state) {
var ch = stream.next();
if (ch == ">" || (ch == "/" && stream.eat(">"))) {
state.tokenize = inText;
type = ch == ">" ? "endTag" : "selfcloseTag";
return "tag";
}
else if (ch == "=") {
type = "equals";
return null;
}
else if (/[\'\"]/.test(ch)) {
state.tokenize = inAttribute(ch);
return state.tokenize(stream, state);
}
else {
stream.eatWhile(/[^\s\u00a0=<>\"\'\/?]/);
return "word";
}
}
function inAttribute(quote) {
return function(stream, state) {
while (!stream.eol()) {
if (stream.next() == quote) {
state.tokenize = inTag;
break;
}
}
return "string";
};
}
function inBlock(style, terminator) {
return function(stream, state) {
while (!stream.eol()) {
if (stream.match(terminator)) {
state.tokenize = inText;
break;
}
stream.next();
}
return style;
};
}
function doctype(depth) {
return function(stream, state) {
var ch;
while ((ch = stream.next()) != null) {
if (ch == "<") {
state.tokenize = doctype(depth + 1);
return state.tokenize(stream, state);
} else if (ch == ">") {
if (depth == 1) {
state.tokenize = inText;
break;
} else {
state.tokenize = doctype(depth - 1);
return state.tokenize(stream, state);
}
}
}
return "meta";
};
}
var curState, setStyle;
function pass() {
for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]);
}
function cont() {
pass.apply(null, arguments);
return true;
}
function pushContext(tagName, startOfLine) {
var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent);
curState.context = {
prev: curState.context,
tagName: tagName,
indent: curState.indented,
startOfLine: startOfLine,
noIndent: noIndent
};
}
function popContext() {
if (curState.context) curState.context = curState.context.prev;
}
function element(type) {
if (type == "openTag") {
curState.tagName = tagName;
return cont(attributes, endtag(curState.startOfLine));
} else if (type == "closeTag") {
var err = false;
if (curState.context) {
err = curState.context.tagName != tagName;
} else {
err = true;
}
if (err) setStyle = "error";
return cont(endclosetag(err));
}
return cont();
}
function endtag(startOfLine) {
return function(type) {
if (type == "selfcloseTag" ||
(type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(curState.tagName.toLowerCase())))
return cont();
if (type == "endTag") {pushContext(curState.tagName, startOfLine); return cont();}
return cont();
};
}
function endclosetag(err) {
return function(type) {
if (err) setStyle = "error";
if (type == "endTag") { popContext(); return cont(); }
setStyle = "error";
return cont(arguments.callee);
}
}
function attributes(type) {
if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);}
if (type == "endTag" || type == "selfcloseTag") return pass();
setStyle = "error";
return cont(attributes);
}
function attribute(type) {
if (type == "equals") return cont(attvalue, attributes);
if (!Kludges.allowMissing) setStyle = "error";
return (type == "endTag" || type == "selfcloseTag") ? pass() : cont();
}
function attvalue(type) {
if (type == "string") return cont(attvaluemaybe);
if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();}
setStyle = "error";
return (type == "endTag" || type == "selfCloseTag") ? pass() : cont();
}
function attvaluemaybe(type) {
if (type == "string") return cont(attvaluemaybe);
else return pass();
}
return {
startState: function() {
return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, context: null};
},
token: function(stream, state) {
if (stream.sol()) {
state.startOfLine = true;
state.indented = stream.indentation();
}
if (stream.eatSpace()) return null;
setStyle = type = tagName = null;
var style = state.tokenize(stream, state);
state.type = type;
if ((style || type) && style != "comment") {
curState = state;
while (true) {
var comb = state.cc.pop() || element;
if (comb(type || style)) break;
}
}
state.startOfLine = false;
return setStyle || style;
},
indent: function(state, textAfter, fullLine) {
var context = state.context;
if ((state.tokenize != inTag && state.tokenize != inText) ||
context && context.noIndent)
return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
if (alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
if (context && /^<\//.test(textAfter))
context = context.prev;
while (context && !context.startOfLine)
context = context.prev;
if (context) return context.indent + indentUnit;
else return 0;
},
compareStates: function(a, b) {
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;
}
},
electricChars: "/"
};
});
CodeMirror.defineMIME("application/xml", "xml");
CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
form {
font-size: 0em;
font-size: 1em;
label {
color: $base-font-color;
......
......@@ -17,7 +17,7 @@ def url_class(url):
<li class="book"><a href="${reverse('book', args=[course.id])}" class="${url_class('book')}">Textbook</a></li>
<li class="discussion"><a href="${reverse('questions')}">Discussion</a></li>
% endif
<li class="wiki"><a href="${reverse('wiki_root')}" class="${url_class('wiki')}">Wiki</a></li>
<li class="wiki"><a href="${reverse('wiki_root', args=[course.id])}" class="${url_class('wiki')}">Wiki</a></li>
% if user.is_authenticated():
<li class="profile"><a href="${reverse('profile', args=[course.id])}" class="${url_class('profile')}">Profile</a></li>
% endif
......
##This file is based on the template from the SimpleWiki source which carries the GPL license
<%inherit file="main.html"/>
<%namespace name='static' file='static_content.html'/>
<%inherit file="../main.html"/>
<%namespace name='static' file='../static_content.html'/>
<%block name="headextra">
<script type="text/javascript" src="${static.url('js/simplewiki/bsn.AutoSuggest_c_2.0.js')}"></script>
<%!
from django.core.urlresolvers import reverse
from simplewiki.views import wiki_reverse
%>
<%!
from django.core.urlresolvers import reverse
%>
<%block name="headextra">
<script type="text/javascript" src="${static.url('js/simplewiki-AutoSuggest_c_2.0.js')}"></script>
<script type="text/javascript">
function set_related_article_id(s) {
......@@ -19,7 +20,7 @@
var x = window.onload;
window.onload = function(){
var options = {
script: "${reverse("search_related", args=[wiki_article.get_url()] )}/?self=${wiki_article.pk}&",
script: "${ wiki_reverse('search_related', wiki_article, course)}/?self=${wiki_article.pk}&",
json: true,
varname: "query",
maxresults: 35,
......@@ -69,23 +70,19 @@
<%block name="bodyextra">
<%include file="course_navigation.html" args="active_page='wiki'" />
%if course:
<%include file="../course_navigation.html" args="active_page='wiki'" />
%endif
<section class="main-content">
<div class="wiki-wrapper">
<%block name="wiki_panel">
<div aria-label="Wiki Navigation" id="wiki_panel">
<h2>Course Wiki</h2>
<%
if (wiki_article is not UNDEFINED):
baseURL = reverse("wiki_view", args=[wiki_article.get_url()])
else:
baseURL = reverse("wiki_view", args=["/"])
%>
<ul class="action">
<li>
<h3>
<a href="${reverse("wiki_list_articles", args=[])}">All Articles</a>
<a href="${wiki_reverse("wiki_list_articles", course=course, namespace=namespace)}">All Articles</a>
</h3>
</li>
......@@ -96,36 +93,25 @@
<div id="wiki_create_form">
<%
theaction = "this.wiki_article_name.value.replace(/([^a-zA-Z0-9\-])/g, '')"
baseURL = reverse("wiki_create", args=["/"])
baseURL = wiki_reverse("wiki_create", course=course, kwargs={"article_path" : namespace + "/" })
%>
<form method="GET" onsubmit="this.action='${baseURL + "' + " + theaction};">
<form method="GET" onsubmit="this.action='${baseURL}' + this.wiki_article_name.value.replace(/([^a-zA-Z0-9\-])/g, '');">
<div>
<label for="id_wiki_article_name">Title of article</label>
<input type="text" name="wiki_article_name" id="id_wiki_article_name" /><br/>
<!-- <label for="id_wiki_article_is_child">Create as a child of current article</label> -->
<!-- <input type="checkbox" name="wiki_article_is_child" id="id_wiki_artcile_is_child" disabled="true" ${ 'checked="checked"' if wiki_article is not UNDEFINED else ""}> -->
</div>
<ul>
<li>
<input type="submit" class="button" value="Create" style="display: inline-block; margin-right: 2px; font-weight: bold;" />
</li>
</ul>
</form>
</div>
</li>
<li class="search">
<form method="POST" action='${reverse("wiki_search_articles", args=[])}'>
<form method="GET" action='${wiki_reverse("wiki_search_articles", course=course, namespace=namespace)}'>
<label class="wiki_box_title">Search</label>
<div style="display:none">
<input type="hidden" name="csrfmiddlewaretoken" value="${csrf_token}"/>
</div>
<input type="text" placeholder="Search" name="value" id="wiki_search_input" style="width: 71%" value="${wiki_search_query if wiki_search_query is not UNDEFINED else '' |h}"/>
<input type="submit" id="wiki_search_input_submit" value="Go!" style="width: 20%" />
</form>
......@@ -148,15 +134,15 @@
<ul>
<li>
<input type="button" onclick="javascript:location.href='${reverse("wiki_view", args=[wiki_article.get_url()])}'" value="View" class="view" />
<a href="${ wiki_reverse('wiki_view', wiki_article, course)}" class="view">View</a>
</li>
<li>
<input type="button" onclick="javascript:location.href='${reverse("wiki_edit", args=[wiki_article.get_url()])}'" value="Edit" ${'disabled="true"' if not wiki_write else ""} class="edit"/>
<a href="${ wiki_reverse('wiki_edit', wiki_article, course)}" class="edit">Edit</a>
</li>
<li>
<input type="button" onclick="javascript:location.href='${reverse("wiki_history", args=[wiki_article.get_url(),1])}'" value="History" class="button history" />
<a href="${ wiki_reverse('wiki_history', wiki_article, course)}" class="history">History</a>
</li>
</ul>
</header>
......
......@@ -20,35 +20,35 @@ ${"Edit " + wiki_title + " - " if wiki_title is not UNDEFINED else ""}MITx 6.002
</%block>
<%block name="wiki_head">
<script type="text/javascript" src="${ settings.LIB_URL }CodeMirror/codemirror.js"></script>
<link rel="stylesheet" href="${ settings.LIB_URL }CodeMirror/codemirror.css" />
<script type="text/javascript" src="${ settings.LIB_URL }vendor/CodeMirror/codemirror.js"></script>
<link rel="stylesheet" href="${ settings.LIB_URL }vendor/CodeMirror/codemirror.css" />
<script type="text/javascript" src="${ settings.LIB_URL }CodeMirror/xml.js"></script>
<script type="text/javascript" src="${ settings.LIB_URL }CodeMirror/mitx_markdown.js"></script>
<script type="text/javascript" src="${ settings.LIB_URL }vendor/CodeMirror/xml.js"></script>
<script type="text/javascript" src="${ settings.LIB_URL }vendor/CodeMirror/mitx_markdown.js"></script>
<script>
$(function(){
$(document).ready(function() {
var editor = CodeMirror.fromTextArea(document.getElementById("id_contents"), {
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.";
}
};
$("#submit_edit").click(function() {
initial_contents = editor.getValue();
});
//TODO: Re-enable this once the styling supports it
// var editor = CodeMirror.fromTextArea(document.getElementById("id_contents"), {
// 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.";
// }
// };
//
// $("#submit_edit").click(function() {
// initial_contents = editor.getValue();
// });
});
});
......
......@@ -3,7 +3,7 @@
<%inherit file="simplewiki_base.html"/>
<%!
from django.core.urlresolvers import reverse
from simplewiki.views import wiki_reverse
%>
<%block name="title"><title>Wiki Error – MITx 6.002x</title></%block>
......@@ -21,30 +21,17 @@ ${wiki_error}
%endif
%if wiki_err_notfound is not UNDEFINED:
%if wiki_url is not UNDEFINED:
<p>
The page you requested could not be found.
Click <a href="${reverse("wiki_create", args=[wiki_url])}">here</a> to create it.
</p>
%else:
<p>
Or maybe rather: Congratulations! It seems that there's no root
article, which is probably because you just installed simple-wiki
and your installation is working. Now you can create the root article.
Click <a href="{% url wiki_create "" %}">here</a> to create it.
</p>
%endif
%else:
%if wiki_err_noparent is not UNDEFINED:
<p>
You cannot create this page, because its parent
does not exist. Click <a href="${reverse("wiki_create", args=[wiki_url_parent])}">here</a>
to create it.
The page you requested could not be found.
Click <a href="${wiki_reverse("wiki_create", course=course, kwargs={'article_path' : article_path})}">here</a> to create it.
</p>
%else:
%if wiki_err_keyword is not UNDEFINED and wiki_err_keyword:
%elif wiki_err_no_namespace is not UNDEFINED and wiki_err_no_namespace:
<p>
You must specify a namespace to create an article in.
</p>
%elif wiki_err_bad_namespace is not UNDEFINED and wiki_err_bad_namespace:
<p>
The page you're trying to create <b>${wiki_url}</b> starts with <b>_</b>, which is reserved for internal use.
The namespace for this article does not exist. This article cannot be created.
</p>
%elif wiki_err_locked is not UNDEFINED and wiki_err_locked:
<p>
......@@ -75,7 +62,7 @@ ${wiki_error}
</p>
%elif wiki_err_deleted is not UNDEFINED and wiki_err_deleted:
<p>
The article you tried to access has been deleted. You may be able to restore it to an earlier version in its <a href="${reverse("wiki_history", args=[wiki_article.get_url(),1])}">history</a>, or <a href="${reverse("wiki_edit", args=[wiki_article.get_url()])}">create a new version</a>.
The article you tried to access has been deleted. You may be able to restore it to an earlier version in its <a href="${wiki_reverse("wiki_history", wiki_article, course)}">history</a>, or <a href="${wiki_reverse("wiki_edit", wiki_article, course)}">create a new version</a>.
</p>
%elif wiki_err_norevision is not UNDEFINED:
<p>
......@@ -86,8 +73,6 @@ ${wiki_error}
An error has occured.
</p>
%endif
%endif
%endif
</div>
</%block>
......
......@@ -6,6 +6,7 @@
<%!
from django.core.urlresolvers import reverse
from simplewiki.views import wiki_reverse
%>
<%block name="wiki_page_title">
......@@ -64,10 +65,10 @@ ${ wiki_article.title }
<tr>
<td colspan="4">
%if wiki_prev_page:
<a href="${reverse("wiki_history", args=[wiki_article.get_url(), wiki_prev_page])}">Previous page</a>
<a href="${wiki_reverse('wiki_history', wiki_article, course, kwargs={'page' : wiki_prev_page})}">Previous page</a>
%endif
%if wiki_next_page:
<a href="${reverse("wiki_history", args=[wiki_article.get_url(), wiki_next_page])}">Next page</a>
<a href="${wiki_reverse('wiki_history', wiki_article, course, kwargs={'page' : wiki_next_page})}">Next page</a>
%endif
</td>
</tr>
......
......@@ -5,7 +5,7 @@
<%block name="title"><title>Wiki - Revision feed - MITx 6.002x</title></%block>
<%!
from django.core.urlresolvers import reverse
from simplewiki.views import wiki_reverse
%>
<%block name="wiki_page_title">
......@@ -29,7 +29,7 @@
<% loopCount += 1 %>
<tr style="border-top: 1px" class="${'dark ' if (loopCount % 2) == 0 else ''}${'deleted ' if (revision.deleted==2) else ''}" >
<td width="15px">
<a href="${reverse('wiki_view_revision',args=[revision.counter, revision.article.get_url()])}"> ${revision.article.title} - ${revision}</a>
<a href="${wiki_reverse('wiki_view_revision', revision.article, course, kwargs={'revision_number' : revision.counter})}"> ${revision.article.title} - ${revision}</a>
</td>
<td>
${ revision.revision_text if revision.revision_text else "<i>None</i>" }</td>
......@@ -50,10 +50,10 @@
<tr>
<td colspan="4">
%if wiki_prev_page:
<a href="${reverse("wiki_revision_feed", args=[wiki_prev_page])}">Previous page</a>
<a href="${wiki_reverse("wiki_revision_feed", course=course, namespace=namespace, kwargs={'page': wiki_prev_page})}">Previous page</a>
%endif
%if wiki_next_page:
<a href="${reverse("wiki_revision_feed", args=[wiki_next_page])}">Next page</a>
<a href="${wiki_reverse("wiki_revision_feed", course=course, namespace=namespace, kwargs={'page': wiki_next_page})}">Next page</a>
%endif
</td>
</tr>
......
......@@ -5,7 +5,7 @@
<%block name="title"><title>Wiki - Search Results - MITx 6.002x</title></%block>
<%!
from django.core.urlresolvers import reverse
from simplewiki.views import wiki_reverse
%>
<%block name="wiki_page_title">
......@@ -23,7 +23,7 @@ Displaying all articles
<ul class="article-list">
%for article in wiki_search_results:
<% article_deleted = not article.current_revision.deleted == 0 %>
<li><h3><a href="${reverse("wiki_view", args=[article.get_url()])}">${article.title} ${'(Deleted)' if article_deleted else ''}</a></h3></li>
<li><h3><a href="${wiki_reverse("wiki_view", article, course)}">${article.title} ${'(Deleted)' if article_deleted else ''}</a></h3></li>
%endfor
%if not wiki_search_results:
......
......@@ -52,7 +52,6 @@ if settings.PERFSTATS:
if settings.COURSEWARE_ENABLED:
urlpatterns += (
url(r'^wiki/', include('simplewiki.urls')),
url(r'^masquerade/', include('masquerade.urls')),
url(r'^jumpto/(?P<probname>[^/]+)/$', 'courseware.views.jump_to'),
url(r'^modx/(?P<id>.*?)/(?P<dispatch>[^/]*)$', 'courseware.module_render.modx_dispatch'), #reset_problem'),
......@@ -80,6 +79,12 @@ if settings.COURSEWARE_ENABLED:
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/about$', 'student.views.course_info', name="about_course"),
)
# Multicourse wiki
urlpatterns += (
url(r'^wiki/', include('simplewiki.urls')),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/wiki/', include('simplewiki.urls')),
)
if settings.ENABLE_MULTICOURSE:
urlpatterns += (url(r'^mitxhome$', 'multicourse.views.mitxhome'),)
......
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