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 = ( ...@@ -74,8 +74,6 @@ TEMPLATE_DIRS = (
'templates', 'templates',
) )
TEMPLATE_CONTEXT_PROCESSORS =( TEMPLATE_CONTEXT_PROCESSORS =(
'django.contrib.auth.context_processors.auth', 'django.contrib.auth.context_processors.auth',
'django.core.context_processors.debug', 'django.core.context_processors.debug',
......
...@@ -4,6 +4,9 @@ from django.utils.html import conditional_escape ...@@ -4,6 +4,9 @@ from django.utils.html import conditional_escape
from django.utils.encoding import force_unicode from django.utils.encoding import force_unicode
from django.forms.util import flatatt from django.forms.util import flatatt
from conf import settings
from django.core.urlresolvers import get_callable
class BaseEditor(): class BaseEditor():
# The editor id can be used for conditional testing. If you write your # 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 # own editor class, you can use the same editor_id as some editor
...@@ -53,7 +56,7 @@ class MarkItUpWidget(forms.Widget): ...@@ -53,7 +56,7 @@ class MarkItUpWidget(forms.Widget):
def render(self, name, value, attrs=None): def render(self, name, value, attrs=None):
if value is None: value = '' if value is None: value = ''
final_attrs = self.build_attrs(attrs, name=name) 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)))) conditional_escape(force_unicode(value))))
class MarkItUp(BaseEditor): class MarkItUp(BaseEditor):
...@@ -84,3 +87,6 @@ class MarkItUp(BaseEditor): ...@@ -84,3 +87,6 @@ class MarkItUp(BaseEditor):
"wiki/markitup/jquery.markitup.js", "wiki/markitup/jquery.markitup.js",
"wiki/markitup/sets/frontend/set.js", "wiki/markitup/sets/frontend/set.js",
) )
EditorClass = get_callable(settings.EDITOR)
editor = EditorClass()
from django import forms from django import forms
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
import editors
class CreateRoot(forms.Form): class CreateRoot(forms.Form):
title = forms.CharField(label=_(u'Title'), help_text=_(u'Initial title of the article. May be overridden with revision titles.')) 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'), 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...'), 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: ...@@ -21,6 +21,9 @@ if not 'sekizai' in django_settings.INSTALLED_APPS:
if not 'django.contrib.contenttypes' 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') 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 # Warnings
###################### ######################
......
...@@ -5,7 +5,10 @@ from django.contrib.contenttypes.models import ContentType ...@@ -5,7 +5,10 @@ from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic from django.contrib.contenttypes import generic
from django.contrib.auth.models import User, Group from django.contrib.auth.models import User, Group
from markdown import markdown
from wiki.conf import settings from wiki.conf import settings
from django.utils.safestring import mark_safe
class Article(models.Model): class Article(models.Model):
...@@ -122,9 +125,14 @@ class Article(models.Model): ...@@ -122,9 +125,14 @@ class Article(models.Model):
class Meta: class Meta:
app_label = settings.APP_LABEL app_label = settings.APP_LABEL
def render_contents(self): def render(self, preview_content=None):
if not self.current_revision: if not self.current_revision:
return "" return ""
if preview_content:
content = preview_content
else:
content = self.current_revision.content
return mark_safe(markdown(content))
class ArticleForObject(models.Model): class ArticleForObject(models.Model):
...@@ -201,7 +209,7 @@ class ArticleRevision(BaseRevision): ...@@ -201,7 +209,7 @@ class ArticleRevision(BaseRevision):
related_name='redirect_set') related_name='redirect_set')
def __unicode__(self): 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): def inherit_predecessor(self, article):
""" """
......
...@@ -92,22 +92,23 @@ class URLPath(MPTTModel): ...@@ -92,22 +92,23 @@ class URLPath(MPTTModel):
parent = cls.root() parent = cls.root()
for slug in slugs: for slug in slugs:
if settings.URL_CASE_SENSITIVE: if settings.URL_CASE_SENSITIVE:
parent = parent.get_children.get(slug=slug) parent = parent.get_children().get(slug=slug)
else: else:
parent = parent.get_children.get(slug__iexact=slug) parent = parent.get_children().get(slug__iexact=slug)
level += 1 level += 1
return parent return parent
@classmethod @classmethod
def create_root(cls, site=None): def create_root(cls, site=None, title="Root", content=""):
if not site: site = Site.objects.get_current() if not site: site = Site.objects.get_current()
root_nodes = cls.objects.root_nodes().filter(site=site) root_nodes = cls.objects.root_nodes().filter(site=site)
if not root_nodes: if not root_nodes:
# (get_or_create does not work for MPTT models??) # (get_or_create does not work for MPTT models??)
root = cls.objects.create(site=site) root = cls.objects.create(site=site)
article = Article() article = Article(title=title)
article.add_revision(ArticleRevision(), save=True) article.add_revision(ArticleRevision(title=title, content=content),
save=True)
article.add_object_relation(root) article.add_object_relation(root)
else: else:
root = root_nodes[0] 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 = { ...@@ -34,8 +34,6 @@ mySettings = {
{separator:'---------------'}, {separator:'---------------'},
{name:'Quotes', openWith:'> '}, {name:'Quotes', openWith:'> '},
{name:'Code Block / Code', openWith:'(!(\t|!|`)!)', closeWith:'(!(`)!)'}, {name:'Code Block / Code', openWith:'(!(\t|!|`)!)', closeWith:'(!(`)!)'},
{separator:'---------------'},
{name:'Preview', call:'preview', className:"preview"}
] ]
} }
......
...@@ -5,9 +5,13 @@ ...@@ -5,9 +5,13 @@
/*label[for=id_content] {float: none; clear: both; width: 100%; display: block;}*/ /*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 { .markItUp .markItUpButton1 a {
background-image:url(images/h1.png); background-image:url(images/h1.png);
......
{% extends "wiki/base.html" %} {% 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 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 %} {% endblock %}
...@@ -26,10 +26,6 @@ ...@@ -26,10 +26,6 @@
<h2 class="page-header">{% trans "Root article" %}</h2> <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"> <form method="POST" class="form-horizontal">
{% wiki_form create_form %} {% wiki_form create_form %}
<div class="form-actions"> <div class="form-actions">
......
...@@ -2,17 +2,22 @@ ...@@ -2,17 +2,22 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <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="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content=""> <meta name="description" content="">
<meta name="author" content="www.django-wiki.org"> <meta name="author" content="www.django-wiki.org">
<!-- Le styles --> <!-- Le styles -->
<link href="{{ STATIC_URL }}wiki/bootstrap/css/bootstrap.css" rel="stylesheet"> <link href="{{ STATIC_URL }}wiki/bootstrap/css/bootstrap.css" rel="stylesheet">
<!-- TODO: Put all this stuff in Less -->
<style> <style>
body { #id_title {font-size: 20px; height: 25px; padding: 10px; width: 400px;}
padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
} .form-horizontal label { font-size: 18px; font-weight: bold; color: #777;}
.asteriskField { font-size: 20px; }
</style> </style>
<link href="{{ STATIC_URL }}wiki/bootstrap/css/bootstrap-responsive.css" rel="stylesheet"> <link href="{{ STATIC_URL }}wiki/bootstrap/css/bootstrap-responsive.css" rel="stylesheet">
...@@ -29,6 +34,9 @@ ...@@ -29,6 +34,9 @@
<body> <body>
{% block wiki_body %}
{% block navbar %}
<div class="navbar navbar-fixed-top"> <div class="navbar navbar-fixed-top">
<div class="navbar-inner"> <div class="navbar-inner">
<div class="container"> <div class="container">
...@@ -48,28 +56,43 @@ ...@@ -48,28 +56,43 @@
</div> </div>
</div> </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 %} {% block wiki_contents %}
<h1>Bootstrap starter template</h1> <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> <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 %} {% endblock %}
<footer style="margin: 50px 0"> <div style="padding: 50px 0;">
<p>Powered by django-wiki: <a href="http://www.django-wiki.org">www.django-wiki.org</a></p> <footer>
<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> <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> </footer>
</div>
</div> <!-- /container --> </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 <!-- Le javascript
================================================== --> ================================================== -->
<!-- Placed at the end of the document so the pages load faster --> <!-- 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-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-dropdown.js"></script>
<script src="{{ STATIC_URL }}wiki/bootstrap/js/bootstrap-scrollspy.js"></script> <script src="{{ STATIC_URL }}wiki/bootstrap/js/bootstrap-scrollspy.js"></script>
<script src="{{ STATIC_URL }}wiki/bootstrap/js/bootstrap-tab.js"></script> <script src="{{ STATIC_URL }}wiki/bootstrap/js/bootstrap-tab.js"></script>
...@@ -80,6 +103,7 @@ ...@@ -80,6 +103,7 @@
<script src="{{ STATIC_URL }}wiki/bootstrap/js/bootstrap-carousel.js"></script> <script src="{{ STATIC_URL }}wiki/bootstrap/js/bootstrap-carousel.js"></script>
<script src="{{ STATIC_URL }}wiki/bootstrap/js/bootstrap-typeahead.js"></script> <script src="{{ STATIC_URL }}wiki/bootstrap/js/bootstrap-typeahead.js"></script>
--> -->
</body> </body>
</html> </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 %} {% csrf_token %}
{% if form.non_field_errors %} {% 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"> <div class="alert alert-block alert-error">
{% if form_error_title %}<h4 class="alert-heading">{{ form_error_title }}</h4>{% endif %} {{ error_message }}
<ul> </div>
{{ form.non_field_errors|unordered_list }} {% endfor %}
</ul>
</div>
{% endif %} {% endif %}
{% for field in form %} {% for field in form %}
...@@ -17,11 +17,11 @@ ...@@ -17,11 +17,11 @@
<div id="div_{{ field.auto_id }}" class="clearfix control-group{% if field.errors %} error{% endif %}"> <div id="div_{{ field.auto_id }}" class="clearfix control-group{% if field.errors %} error{% endif %}">
{% if field.label %} {% if field.label %}
<label for="{{ field.id_for_label }}" class="control-label {% if field.field.required %}requiredField{% endif %}"> <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> </label>
{% endif %} {% endif %}
<div class="controls"> <div class="controls">
{{ field }} {{ field }} {% if field.field.required %}<span class="asteriskField">*</span>{% endif %}
{% if field.errors %} {% if field.errors %}
{% for error in field.errors %} {% for error in field.errors %}
<div id="error_{{ forloop.counter }}_{{ field.auto_id }}" class="help-block"><strong>{{ error }}</strong></div> <div id="error_{{ forloop.counter }}_{{ field.auto_id }}" class="help-block"><strong>{{ error }}</strong></div>
...@@ -30,7 +30,6 @@ ...@@ -30,7 +30,6 @@
{% if field.help_text %} {% if field.help_text %}
<p id="hint_{{ field.auto_id }}" class="help-block">{{ field.help_text|safe }}</p> <p id="hint_{{ field.auto_id }}" class="help-block">{{ field.help_text|safe }}</p>
{% endif %} {% endif %}
</div> </div>
</div> </div>
{% endif %} {% endif %}
......
{% load wiki_tags i18n cache %} {% load wiki_tags i18n cache %}
{% block wiki_contents %} {% block wiki_contents %}
{% cache 500 article.current_revision %} {% if not preview %}
{{ article.render }} {% cache 500 article article.current_revision.id %}
{{ article.render }}
{% endcache %} {% endcache %}
{% else %}
{{ content }}
{% endif %}
{% endblock %} {% 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() ...@@ -6,21 +6,38 @@ register = template.Library()
from wiki import models from wiki import models
@register.inclusion_tag('wiki/article/render.html') # Cache for looking up objects for articles... article_for_object is
def wiki_article(obj): # 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): if not isinstance(obj, Model):
raise TypeError("A Wiki article can only be associated to a Django Model instance, not %s" % type(obj)) 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) 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 { return {
'obj': obj,
'article': article, 'article': article,
'content': content,
'preview': not preview_content is None,
} }
@register.inclusion_tag('wiki/includes/form.html', takes_context=True) @register.inclusion_tag('wiki/includes/form.html', takes_context=True)
...@@ -32,3 +49,13 @@ def wiki_form(context, form_obj): ...@@ -32,3 +49,13 @@ def wiki_form(context, form_obj):
return { return {
'form': form_obj, '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 @@ ...@@ -2,8 +2,11 @@
from django.conf.urls.defaults import patterns, url from django.conf.urls.defaults import patterns, url
urlpatterns = patterns('', 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('^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'), url('(.*)', 'wiki.views.get_url', name='get_url'),
) )
......
# -*- coding: utf-8 -*- # -*- 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.template.context import RequestContext
from django.contrib.auth.decorators import permission_required from django.contrib.auth.decorators import permission_required
from django.http import HttpResponseForbidden
from django.utils.translation import ugettext as _
import models import models
import forms import forms
from conf import settings import editors
from wiki.core.exceptions import NoRootURL 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: def the_func(request, *args, **kwargs):
urlpath = models.URLPath.root()
except NoRootURL: path = kwargs.pop('path', None)
return redirect('wiki:root_create') 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}) c = RequestContext(request, {'urlpath': urlpath,
return render_to_response("wiki/article.html", c) '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') @permission_required('wiki.add_article')
def root_create(request): def root_create(request):
if request.method == 'POST': if request.method == 'POST':
create_form = forms.CreateRoot(request.POST) create_form = forms.CreateRoot(request.POST)
if create_form.is_valid(): 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") return redirect("wiki:root")
else: else:
create_form = forms.CreateRoot() 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, c = RequestContext(request, {'create_form': create_form,
'editor': editor,}) 'editor': editors.editor,})
return render_to_response("wiki/article/create_root.html", c) return render_to_response("wiki/article/create_root.html", c)
def get_url(request, path): 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