Commit 58cf1a33 by benjaoming

Adding edit page, preview function and MORE. South migrations will be added back soon.

parent dba6f826
......@@ -74,8 +74,6 @@ TEMPLATE_DIRS = (
'templates',
)
TEMPLATE_CONTEXT_PROCESSORS =(
'django.contrib.auth.context_processors.auth',
'django.core.context_processors.debug',
......
......@@ -4,6 +4,9 @@ from django.utils.html import conditional_escape
from django.utils.encoding import force_unicode
from django.forms.util import flatatt
from conf import settings
from django.core.urlresolvers import get_callable
class BaseEditor():
# The editor id can be used for conditional testing. If you write your
# own editor class, you can use the same editor_id as some editor
......@@ -53,7 +56,7 @@ class MarkItUpWidget(forms.Widget):
def render(self, name, value, attrs=None):
if value is None: value = ''
final_attrs = self.build_attrs(attrs, name=name)
return mark_safe(u'<textarea%s>%s</textarea>' % (flatatt(final_attrs),
return mark_safe(u'<div><textarea%s>%s</textarea></div>' % (flatatt(final_attrs),
conditional_escape(force_unicode(value))))
class MarkItUp(BaseEditor):
......@@ -84,3 +87,6 @@ class MarkItUp(BaseEditor):
"wiki/markitup/jquery.markitup.js",
"wiki/markitup/sets/frontend/set.js",
)
EditorClass = get_callable(settings.EDITOR)
editor = EditorClass()
from django import forms
from django.utils.translation import ugettext as _
import editors
class CreateRoot(forms.Form):
title = forms.CharField(label=_(u'Title'), help_text=_(u'Initial title of the article. May be overridden with revision titles.'))
content = forms.CharField(label=_(u'Type in some contents'),
help_text=_(u'This is just the initial contents of your article. After creating it, you can use more complex features like adding plugins, meta data, related articles etc...'),
required=False)
required=False, widget=editors.editor.get_widget())
class EditForm(forms.Form):
title = forms.CharField(label=_(u'Title'),)
content = forms.CharField(label=_(u'Contents'),
required=False, widget=editors.editor.get_widget())
def __init__(self, instance, *args, **kwargs):
self.preview = kwargs.pop('preview', False)
if instance:
initial = {'content': instance.content,
'title': instance.title,}
initial.update(kwargs.get('initial', {}))
kwargs['initial'] = initial
self.instance = instance
super(EditForm, self).__init__(*args, **kwargs)
def clean(self):
cd = self.cleaned_data
if cd['title'] == self.instance.title and cd['content'] == self.instance.content:
raise forms.ValidationError(_(u'No changes made. Nothing to save.'))
return cd
\ No newline at end of file
......@@ -21,6 +21,9 @@ if not 'sekizai' in django_settings.INSTALLED_APPS:
if not 'django.contrib.contenttypes' in django_settings.INSTALLED_APPS:
raise ImproperlyConfigured('django-wiki: needs django.contrib.contenttypes in INSTALLED_APPS')
if not 'django.contrib.auth.context_processors.auth' in django_settings.TEMPLATE_CONTEXT_PROCESSORS:
raise ImproperlyConfigured('django-wiki: needs django.contrib.auth.context_processors.auth in TEMPLATE_CONTEXT_PROCESSORS')
######################
# Warnings
######################
......
......@@ -5,7 +5,10 @@ from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
from django.contrib.auth.models import User, Group
from markdown import markdown
from wiki.conf import settings
from django.utils.safestring import mark_safe
class Article(models.Model):
......@@ -122,9 +125,14 @@ class Article(models.Model):
class Meta:
app_label = settings.APP_LABEL
def render_contents(self):
def render(self, preview_content=None):
if not self.current_revision:
return ""
if preview_content:
content = preview_content
else:
content = self.current_revision.content
return mark_safe(markdown(content))
class ArticleForObject(models.Model):
......@@ -201,7 +209,7 @@ class ArticleRevision(BaseRevision):
related_name='redirect_set')
def __unicode__(self):
return "%s (%d)" % (self.article.title, self.revision_number)
return "%s (%d)" % (self.title, self.revision_number)
def inherit_predecessor(self, article):
"""
......
......@@ -92,22 +92,23 @@ class URLPath(MPTTModel):
parent = cls.root()
for slug in slugs:
if settings.URL_CASE_SENSITIVE:
parent = parent.get_children.get(slug=slug)
parent = parent.get_children().get(slug=slug)
else:
parent = parent.get_children.get(slug__iexact=slug)
parent = parent.get_children().get(slug__iexact=slug)
level += 1
return parent
@classmethod
def create_root(cls, site=None):
def create_root(cls, site=None, title="Root", content=""):
if not site: site = Site.objects.get_current()
root_nodes = cls.objects.root_nodes().filter(site=site)
if not root_nodes:
# (get_or_create does not work for MPTT models??)
root = cls.objects.create(site=site)
article = Article()
article.add_revision(ArticleRevision(), save=True)
article = Article(title=title)
article.add_revision(ArticleRevision(title=title, content=content),
save=True)
article.add_object_relation(root)
else:
root = root_nodes[0]
......
/* ==========================================================
* 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-modal.js v2.0.4
* http://twitter.github.com/bootstrap/javascript.html#modals
* =========================================================
* 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 ;_;
/* MODAL CLASS DEFINITION
* ====================== */
var Modal = function (content, options) {
this.options = options
this.$element = $(content)
.delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this))
}
Modal.prototype = {
constructor: Modal
, toggle: function () {
return this[!this.isShown ? 'show' : 'hide']()
}
, show: function () {
var that = this
, e = $.Event('show')
this.$element.trigger(e)
if (this.isShown || e.isDefaultPrevented()) return
$('body').addClass('modal-open')
this.isShown = true
escape.call(this)
backdrop.call(this, function () {
var transition = $.support.transition && that.$element.hasClass('fade')
if (!that.$element.parent().length) {
that.$element.appendTo(document.body) //don't move modals dom position
}
that.$element
.show()
if (transition) {
that.$element[0].offsetWidth // force reflow
}
that.$element.addClass('in')
transition ?
that.$element.one($.support.transition.end, function () { that.$element.trigger('shown') }) :
that.$element.trigger('shown')
})
}
, hide: function (e) {
e && e.preventDefault()
var that = this
e = $.Event('hide')
this.$element.trigger(e)
if (!this.isShown || e.isDefaultPrevented()) return
this.isShown = false
$('body').removeClass('modal-open')
escape.call(this)
this.$element.removeClass('in')
$.support.transition && this.$element.hasClass('fade') ?
hideWithTransition.call(this) :
hideModal.call(this)
}
}
/* MODAL PRIVATE METHODS
* ===================== */
function hideWithTransition() {
var that = this
, timeout = setTimeout(function () {
that.$element.off($.support.transition.end)
hideModal.call(that)
}, 500)
this.$element.one($.support.transition.end, function () {
clearTimeout(timeout)
hideModal.call(that)
})
}
function hideModal(that) {
this.$element
.hide()
.trigger('hidden')
backdrop.call(this)
}
function backdrop(callback) {
var that = this
, animate = this.$element.hasClass('fade') ? 'fade' : ''
if (this.isShown && this.options.backdrop) {
var doAnimate = $.support.transition && animate
this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
.appendTo(document.body)
if (this.options.backdrop != 'static') {
this.$backdrop.click($.proxy(this.hide, this))
}
if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
this.$backdrop.addClass('in')
doAnimate ?
this.$backdrop.one($.support.transition.end, callback) :
callback()
} else if (!this.isShown && this.$backdrop) {
this.$backdrop.removeClass('in')
$.support.transition && this.$element.hasClass('fade')?
this.$backdrop.one($.support.transition.end, $.proxy(removeBackdrop, this)) :
removeBackdrop.call(this)
} else if (callback) {
callback()
}
}
function removeBackdrop() {
this.$backdrop.remove()
this.$backdrop = null
}
function escape() {
var that = this
if (this.isShown && this.options.keyboard) {
$(document).on('keyup.dismiss.modal', function ( e ) {
e.which == 27 && that.hide()
})
} else if (!this.isShown) {
$(document).off('keyup.dismiss.modal')
}
}
/* MODAL PLUGIN DEFINITION
* ======================= */
$.fn.modal = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('modal')
, options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('modal', (data = new Modal(this, options)))
if (typeof option == 'string') data[option]()
else if (options.show) data.show()
})
}
$.fn.modal.defaults = {
backdrop: true
, keyboard: true
, show: true
}
$.fn.modal.Constructor = Modal
/* MODAL DATA-API
* ============== */
$(function () {
$('body').on('click.modal.data-api', '[data-toggle="modal"]', function ( e ) {
var $this = $(this), href
, $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
, option = $target.data('modal') ? 'toggle' : $.extend({}, $target.data(), $this.data())
e.preventDefault()
$target.modal(option)
})
})
}(window.jQuery);
\ No newline at end of file
......@@ -34,8 +34,6 @@ mySettings = {
{separator:'---------------'},
{name:'Quotes', openWith:'> '},
{name:'Code Block / Code', openWith:'(!(\t|!|`)!)', closeWith:'(!(`)!)'},
{separator:'---------------'},
{name:'Preview', call:'preview', className:"preview"}
]
}
......
......@@ -5,9 +5,13 @@
/*label[for=id_content] {float: none; clear: both; width: 100%; display: block;}*/
.markItUp {max-width: 600px; padding: 0; width: 100%;}
.markItUp {padding: 0; width: auto;}
.markItUpContainer {}
textarea.markItUp {font-size: 16px; padding: 10px; float: none; display: block; width: 100%; }
.markItUpHeader {float: none; display: block; }
.markItUpContainer {margin-right: 40px;;}
.markItUp .markItUpButton1 a {
background-image:url(images/h1.png);
......
{% extends "wiki/base.html" %}
{% load wiki_tags %}
{% load wiki_tags i18n %}
{% block pagetitle %}{% article_for_object urlpath as article %}{{ article.current_revision.title }}{% endblock %}
{% block wiki_breadcrumbs %}
{% include "wiki/includes/breadcrumbs.html" %}
{% endblock %}
{% block wiki_contents %}
{% block page-title %}{{ article.current_revision.title }}{% endblock %}
{% article_for_object urlpath as article %}
{% wiki_article urlpath %}
{% if article %}
<div class="tabbable tabs-top" style="margin-top: 40px;">
<ul class="nav nav-tabs">
{% with "view" as selected %}
{% include "wiki/includes/article_menu.html" %}
{% endwith %}
<li><h1 style="margin-top: -10px;">{{ article.current_revision.title }}</h1></li>
</ul>
<div class="tab-content">
{% wiki_render article %}
</div>
</div>
<div class="tabbable tabs-below" style="margin-top: 20px;">
<ul class="nav nav-tabs">
{% with "view" as selected %}
{% include "wiki/includes/article_menu.html" %}
{% endwith %}
<li style="margin-top: 10px;"><em>{% trans "Article last modified:" %} {{ article.current_revision.modified }}</em></li>
</ul>
</div>
{% else %}
{% trans "An article for this path does not exist." %}
{% endif %}
{% endblock %}
......@@ -26,10 +26,6 @@
<h2 class="page-header">{% trans "Root article" %}</h2>
<style type="text/css">
#id_title {font-size: 20px; padding: 10px; width: 400px;}
</style>
<form method="POST" class="form-horizontal">
{% wiki_form create_form %}
<div class="form-actions">
......
......@@ -2,17 +2,22 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>{% block page-title %}{% endblock %} ~~ django-wiki ^_^</title>
<title>{% block pagetitle %}{% endblock %} | django-wiki \/\/</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="www.django-wiki.org">
<!-- Le styles -->
<link href="{{ STATIC_URL }}wiki/bootstrap/css/bootstrap.css" rel="stylesheet">
<!-- TODO: Put all this stuff in Less -->
<style>
body {
padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
}
#id_title {font-size: 20px; height: 25px; padding: 10px; width: 400px;}
.form-horizontal label { font-size: 18px; font-weight: bold; color: #777;}
.asteriskField { font-size: 20px; }
</style>
<link href="{{ STATIC_URL }}wiki/bootstrap/css/bootstrap-responsive.css" rel="stylesheet">
......@@ -29,6 +34,9 @@
<body>
{% block wiki_body %}
{% block navbar %}
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
......@@ -48,28 +56,43 @@
</div>
</div>
</div>
{% endblock %}
<div class="container">
<div class="container" style="margin-top: 60px;">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }}">
<a class="close" data-dismiss="alert" href="#">&times;</a>
{{ message }}
</div>
{% endfor %}
{% endif %}
{% block wiki_breadcrumbs %}{% endblock %}
{% block wiki_contents %}
<h1>Bootstrap starter template</h1>
<p>Use this document as a way to quick start any new project.<br> All you get is this message and a barebones HTML document.</p>
{% endblock %}
<footer style="margin: 50px 0">
<p>Powered by django-wiki: <a href="http://www.django-wiki.org">www.django-wiki.org</a></p>
<p>This is open source, <a href="http://www.gnu.org/licenses/quick-guide-gplv3.html">GPLv3</a>. <a href="https://github.com/benjaoming/django-wiki">Fork me on Github.</a></p>
<div style="padding: 50px 0;">
<footer>
<hr />
<a href="https://github.com/benjaoming/django-wiki" class="pull-right"><img src="{{ STATIC_URL }}img/github_icon.png" /></a>
<p>Powered by <a href="http://www.django-wiki.org">django-wiki</a>, an open source application under the <a href="http://www.gnu.org/licenses/quick-guide-gplv3.html">GPLv3</a> license. Let knowledge be the cure.</p>
<div style="clear: both"></div>
</footer>
</div>
</div> <!-- /container -->
{% endblock %}
<script src="{{ STATIC_URL }}wiki/bootstrap/js/bootstrap-modal.js"></script>
<script src="{{ STATIC_URL }}wiki/bootstrap/js/bootstrap-alert.js"></script>
<!-- Le javascript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<!--
<script src="{{ STATIC_URL }}wiki/bootstrap/js/bootstrap-transition.js"></script>
<script src="{{ STATIC_URL }}wiki/bootstrap/js/bootstrap-alert.js"></script>
<script src="{{ STATIC_URL }}wiki/bootstrap/js/bootstrap-modal.js"></script>
<script src="{{ STATIC_URL }}wiki/bootstrap/js/bootstrap-dropdown.js"></script>
<script src="{{ STATIC_URL }}wiki/bootstrap/js/bootstrap-scrollspy.js"></script>
<script src="{{ STATIC_URL }}wiki/bootstrap/js/bootstrap-tab.js"></script>
......@@ -80,6 +103,7 @@
<script src="{{ STATIC_URL }}wiki/bootstrap/js/bootstrap-carousel.js"></script>
<script src="{{ STATIC_URL }}wiki/bootstrap/js/bootstrap-typeahead.js"></script>
-->
</body>
</html>
{% extends "wiki/base.html" %}
{% load wiki_tags i18n %}
{% block pagetitle %}{% article_for_object urlpath as article %}{{ article.current_revision.title }}{% endblock %}
{% block wiki_breadcrumbs %}
{% include "wiki/includes/breadcrumbs.html" %}
{% endblock %}
{% block wiki_contents %}
{% article_for_object urlpath as article %}
{% if article %}
<div class="tabbable tabs-top" style="margin-top: 40px;">
<ul class="nav nav-tabs">
{% with "edit" as selected %}
{% include "wiki/includes/article_menu.html" %}
{% endwith %}
<li>
<h1 style="margin-top: -10px;">
{{ article.current_revision.title }}
</h1>
</li>
</ul>
<div class="tab-content">
<form method="POST" class="form-horizontal">
{% 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_url urlpath.path %}'">
<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_url urlpath.path %}'">
<span class="icon-ok"></span>
{% trans "Save changes" %}
</button>
<a class="pull-right btn btn-danger">
<span class="icon-trash"></span>
{% trans "Delete article" %}
</a>
</div>
<div class="modal hide fade" id="previewModal" style="width: 80%; min-height: 500px; margin-left: -40%;">
<div class="modal-body">
<iframe name="previewWindow" style="width: 100%; min-height: 400px; border: 0;" 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_url urlpath.path %}'">
<span class="icon-ok"></span>
{% trans "Save changes" %}
</button>
</div>
</div>
</form>
</div>
</div>
<div class="tabbable tabs-below" style="margin-top: 20px;">
<ul class="nav nav-tabs">
<li style="margin-top: 10px;"><em>{% trans "Article last modified:" %} {{ article.current_revision.modified }}</em></li>
</ul>
</div>
{% else %}
{% trans "An article for this path does not exist." %}
{% endif %}
{% endblock %}
{% extends "wiki/base.html" %}
{% load wiki_tags i18n %}
{% block pagetitle %}{% article_for_object urlpath as article %}{{ article.current_revision.title }}{% endblock %}
{% block wiki_breadcrumbs %}
{% include "wiki/includes/breadcrumbs.html" %}
{% endblock %}
{% block wiki_contents %}
{% article_for_object urlpath as article %}
{% if article %}
<div class="tabbable tabs-top" style="margin-top: 40px;">
<ul class="nav nav-tabs">
{% with "history" as selected %}
{% include "wiki/includes/article_menu.html" %}
{% endwith %}
<li>
<h1 style="margin-top: -10px;">
{{ article.current_revision.title }}
</h1>
</li>
</ul>
<div class="tab-content">
<form method="POST" class="form-horizontal">
<div class="form-actions">
<button type="submit" name="save" value="1" class="btn btn-large btn-primary" onclick="this.form.target=''; this.form.action='{% url wiki:edit_url urlpath.path %}'">
<span class="icon-ok"></span>
{% trans "Save changes" %}
</button>
</div>
</form>
</div>
</div>
<div class="tabbable tabs-below" style="margin-top: 20px;">
<ul class="nav nav-tabs">
<li style="margin-top: 10px;"><em>{% trans "Article last modified:" %} {{ article.current_revision.modified }}</em></li>
</ul>
</div>
{% else %}
{% trans "An article for this path does not exist." %}
{% endif %}
{% endblock %}
{% load i18n wiki_tags %}
<li style="float: right;">
<a href="#">
<span class="icon-wrench"></span>
{% trans "Settings" %}
</a>
</li>
<li style="float: right;"{% if selected == "history" %} class="active"{% endif %}>
<a href="{% url wiki:history_url urlpath.path %}">
<span class="icon-time"></span>
{% trans "Changes" %}
</a>
</li>
<li style="float: right;"{% if selected == "edit" %} class="active"{% endif %}>
<a href="{% url wiki:edit_url urlpath.path %}">
<span class="icon-edit"></span>
{% trans "Edit" %}
</a>
</li>
<li{% if selected == "view" %} class="active"{% endif %} style="float: right;">
<a href="{% url wiki:get_url urlpath.path %}">
<span class="icon-home"></span>
{% trans "View" %}
</a>
</li>
{% load i18n %}<ul class="breadcrumb">
{% for ancestor in urlpath.get_ancestors.all %}
<span class="divider">/</span>
<a href="{% url wiki:get_url ancestor.path %}"><li>{{ ancestor.article.current_revision.title }}</li></a>
{% endfor %}
<span class="divider">/</span>
<li class="active"><a href="{% url wiki:get_url urlpath.path %}">{{ urlpath.article.current_revision.title }}</a></li>
<span class="divider">/</span>
<li><a href="{% url wiki:get_url urlpath.path %}">{% trans "+ Add article" %}</a></li>
</ul>
{% load sekizai_tags wiki_tags %}
{% addtoblock "js" %}
{% for js in editor.Media.js %}
<script type="text/javascript" src="{{ STATIC_URL }}{{ js }}"></script>
{% endfor %}
{% endaddtoblock %}
{% addtoblock "css" %}
{% for media, srcs in editor.Media.css.items %}
{% for src in srcs %}
<link rel="stylesheet" media="{{ media }}" href="{{ STATIC_URL }}{{ src }}" />
{% endfor %}
{% endfor %}
{% endaddtoblock %}
{% wiki_form edit_form %}
{% 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">
{% if form_error_title %}<h4 class="alert-heading">{{ form_error_title }}</h4>{% endif %}
<ul>
{{ form.non_field_errors|unordered_list }}
</ul>
</div>
{{ error_message }}
</div>
{% endfor %}
{% endif %}
{% for field in form %}
......@@ -17,11 +17,11 @@
<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 }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}
{{ field.label|safe }}
</label>
{% endif %}
<div class="controls">
{{ field }}
{{ 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>
......@@ -30,7 +30,6 @@
{% if field.help_text %}
<p id="hint_{{ field.auto_id }}" class="help-block">{{ field.help_text|safe }}</p>
{% endif %}
</div>
</div>
{% endif %}
......
{% load wiki_tags i18n cache %}
{% block wiki_contents %}
{% cache 500 article.current_revision %}
{{ article.render }}
{% if not preview %}
{% cache 500 article article.current_revision.id %}
{{ article.render }}
{% endcache %}
{% else %}
{{ content }}
{% endif %}
{% endblock %}
{% extends "wiki/base.html" %}
{% load wiki_tags i18n %}
{% block wiki_body %}
<h1 class="page-header">{{ title }}</h1>
{% wiki_render article content %}
{% endblock %}
......@@ -6,21 +6,38 @@ register = template.Library()
from wiki import models
@register.inclusion_tag('wiki/article/render.html')
def wiki_article(obj):
# Cache for looking up objects for articles... article_for_object is
# called more than once per page in multiple template blocks.
_cache = {}
@register.assignment_tag(takes_context=True)
def article_for_object(context, obj):
if not isinstance(obj, Model):
raise TypeError("A Wiki article can only be associated to a Django Model instance, not %s" % type(obj))
content_type = ContentType.objects.get_for_model(obj)
try:
article = models.ArticleForObject.objects.get(content_type=content_type, object_id=obj.pk).article
except models.ArticleForObject.DoesNotExist:
article = None
# TODO: This is disabled for now, as it should only fire once per request
# Maybe store cache in the request object?
if True or not obj in _cache.keys():
try:
article = models.ArticleForObject.objects.get(content_type=content_type, object_id=obj.pk).article
except models.ArticleForObject.DoesNotExist:
article = None
_cache[obj] = article
return _cache[obj]
@register.inclusion_tag('wiki/includes/render.html')
def wiki_render(article, preview_content=None):
if preview_content:
content = article.render(preview_content=preview_content)
else:
content = None
return {
'obj': obj,
'article': article,
'content': content,
'preview': not preview_content is None,
}
@register.inclusion_tag('wiki/includes/form.html', takes_context=True)
......@@ -32,3 +49,13 @@ def wiki_form(context, form_obj):
return {
'form': form_obj,
}
@register.filter(takes_context=True)
def can_read(context, obj):
"""Articles and plugins have a can_read method..."""
return obj.can_read(context.user)
@register.filter(takes_context=True)
def can_write(context, obj):
"""Articles and plugins have a can_write method..."""
return obj.can_write(context.user)
......@@ -2,8 +2,11 @@
from django.conf.urls.defaults import patterns, url
urlpatterns = patterns('',
url('^$', 'wiki.views.root', name='root'),
url('^$', 'wiki.views.root', name='root', kwargs={'path': ''}),
url('^create-root/$', 'wiki.views.root_create', name='root_create'),
url('^(?P<path>.*)/?_edit/$', 'wiki.views.edit', name='edit_url'),
url('^(?P<path>.*)/?_preview/$', 'wiki.views.preview', name='preview_url'),
url('^(?P<path>.*)/?_history/$', 'wiki.views.history', name='history_url'),
url('(.*)', 'wiki.views.get_url', name='get_url'),
)
......
# -*- coding: utf-8 -*-
from django.shortcuts import render_to_response, redirect
from django.shortcuts import render_to_response, redirect, get_object_or_404
from django.template.context import RequestContext
from django.contrib.auth.decorators import permission_required
from django.http import HttpResponseForbidden
from django.utils.translation import ugettext as _
import models
import forms
from conf import settings
import editors
from wiki.core.exceptions import NoRootURL
from django.core.urlresolvers import get_callable
from django.contrib import messages
from django.views.generic.list import ListView
def root(request):
def get_article(func=None, can_read=True, can_write=False):
"""Intercepts the keyword args path or article_id and looks up an article,
calling the decorated func with this ID."""
try:
urlpath = models.URLPath.root()
except NoRootURL:
return redirect('wiki:root_create')
def the_func(request, *args, **kwargs):
path = kwargs.pop('path', None)
article_id = kwargs.pop('article_id', None)
urlpath = None
if not path is None:
try:
urlpath = models.URLPath.get_by_path(path)
except NoRootURL:
return redirect('wiki:root_create')
article = urlpath.article
elif article_id:
article = get_object_or_404(models.Article, id=article_id)
if not article.can_write(request.user):
raise HttpResponseForbidden()
kwargs['urlpath'] = urlpath
return func(request, article, *args, **kwargs)
if func:
return the_func
else:
return lambda func: get_article(func, can_read=can_read, can_write=can_write)
@get_article(can_read=True)
def preview(request, article, urlpath=None, template_file="wiki/preview_inline.html"):
content = article.current_revision.content
title = article.current_revision.title
if request.method == 'POST':
edit_form = forms.EditForm(article.current_revision, request.POST, preview=True)
if edit_form.is_valid():
title = edit_form.cleaned_data['title']
content = edit_form.cleaned_data['content']
c = RequestContext(request, {'urlpath': urlpath,
'article': article,
'title': title,
'content': content})
return render_to_response(template_file, c)
@get_article(can_read=True)
def root(request, article, template_file="wiki/article.html", urlpath=None):
c = RequestContext(request, {'urlpath': urlpath})
return render_to_response("wiki/article.html", c)
c = RequestContext(request, {'urlpath': urlpath,
'article': article,})
return render_to_response(template_file, c)
@get_article(can_write=True)
def edit(request, article, template_file="wiki/edit.html", urlpath=None):
if request.method == 'POST':
edit_form = forms.EditForm(article.current_revision, request.POST)
if edit_form.is_valid():
revision = models.ArticleRevision()
revision.inherit_predecessor(article)
revision.title = edit_form.cleaned_data['title']
revision.content = edit_form.cleaned_data['content']
article.add_revision(revision)
messages.success(request, _(u'A new revision of the article was succesfully added.'))
if not urlpath is None:
return redirect("wiki:get_url", urlpath.path)
# TODO: Where to go if it's a different object? It's probably
# an ajax callback, so we don't care... but should perhaps return
# a status
return
else:
edit_form = forms.EditForm(article.current_revision)
c = RequestContext(request, {'article': article,
'urlpath': urlpath,
'edit_form': edit_form,
'editor': editors.editor})
return render_to_response(template_file, c)
@get_article(can_read=True)
def history(request, article, template_file="wiki/history.html", urlpath=None):
c = RequestContext(request, {'article': article,
'urlpath': urlpath,})
return render_to_response(template_file, c)
@permission_required('wiki.add_article')
def root_create(request):
if request.method == 'POST':
create_form = forms.CreateRoot(request.POST)
if create_form.is_valid():
root = models.URLPath.create_root()
root = models.URLPath.create_root(title=create_form.cleaned_data["title"],
content=create_form.cleaned_data["content"])
return redirect("wiki:root")
else:
create_form = forms.CreateRoot()
# Insert current editor
EditorClass = get_callable(settings.EDITOR)
editor = EditorClass()
create_form.fields['content'].widget = editor.get_widget()
c = RequestContext(request, {'create_form': create_form,
'editor': editor,})
'editor': editors.editor,})
return render_to_response("wiki/article/create_root.html", c)
def get_url(request, path):
......
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