From: hernani Date: Mon, 7 Jun 2010 01:56:21 +0000 (+0000) Subject: Adds the option to create static pages with several options to customize its behaviour. X-Git-Tag: live~751 X-Git-Url: https://git.openstreetmap.org/osqa.git/commitdiff_plain/9308d78c7f1e4d2a4faf3b98d1b24c262f3b6287 Adds the option to create static pages with several options to customize its behaviour. git-svn-id: http://svn.osqa.net/svnroot/osqa/trunk@381 0cfe37f9-358a-4d5e-be75-b63607b5c754 --- diff --git a/forum/actions/__init__.py b/forum/actions/__init__.py index a681069..4c6c678 100644 --- a/forum/actions/__init__.py +++ b/forum/actions/__init__.py @@ -1,3 +1,4 @@ from meta import * from node import * -from user import * \ No newline at end of file +from user import * +from page import * \ No newline at end of file diff --git a/forum/actions/meta.py b/forum/actions/meta.py index adf51ca..ccdaa75 100644 --- a/forum/actions/meta.py +++ b/forum/actions/meta.py @@ -157,7 +157,7 @@ class AcceptAnswerAction(ActionProxy): return _("%(user)s accepted %(answerer)s answer on %(asker)s question %(question)s") % { 'user': self.hyperlink(self.user.get_profile_url(), self.friendly_username(viewer, self.user)), - 'answerer': self.hyperlink(answer.author.get_profile_url(), self.friendly_username(viewer, answer.author)), + 'answerer': self.hyperlink(answer.author.get_profile_url(), self.friendly_ownername(viewer, answer.author)), 'asker': asker, 'question': self.hyperlink(question.get_absolute_url(), question.title) } diff --git a/forum/actions/page.py b/forum/actions/page.py new file mode 100644 index 0000000..bfeda16 --- /dev/null +++ b/forum/actions/page.py @@ -0,0 +1,58 @@ +from django.utils.translation import ugettext as _ +from forum.models.action import ActionProxy +from forum.models import Page + +class NewPageAction(ActionProxy): + verb = _("created") + + def process_data(self, **data): + title = data.pop('title') + body = data.pop('content') + + page = Page(author=self.user, title=title, body=body, extra=data) + page.save() + self.node = page + + def describe(self, viewer=None): + return _("%(user)s created a new page titled %(page)s") % { + 'user': self.hyperlink(self.user.get_profile_url(), self.friendly_username(viewer, self.user)), + 'page': self.hyperlink(self.node.get_absolute_url(), self.node.title) + } + +class EditPageAction(ActionProxy): + verb = _("edited") + + def process_data(self, **data): + title = data.pop('title') + body = data.pop('content') + + if (title != self.node.title) and (body != self.node.body): + self.node.create_revision(self.user, title=title, body=body) + + self.node.extra = data + self.node.save() + + def describe(self, viewer=None): + return _("%(user)s edited the page titled %(page)s") % { + 'user': self.hyperlink(self.user.get_profile_url(), self.friendly_username(viewer, self.user)), + 'page': self.hyperlink(self.node.get_absolute_url(), self.node.title) + } + +class PublishAction(ActionProxy): + verb = _("published") + + def process_action(self): + self.node.marked = True + self.node.nstate.published = self + self.node.save() + + def cancel_action(self): + self.node.marked = False + self.node.nstate.published = None + self.node.save() + + def describe(self, viewer=None): + return _("%(user)s published a new page titled %(page)s") % { + 'user': self.hyperlink(self.user.get_profile_url(), self.friendly_username(viewer, self.user)), + 'page': self.hyperlink(self.node.get_absolute_url(), self.node.title) + } diff --git a/forum/actions/user.py b/forum/actions/user.py index 6c174ce..df90c66 100644 --- a/forum/actions/user.py +++ b/forum/actions/user.py @@ -24,7 +24,7 @@ class EditProfileAction(ActionProxy): def describe(self, viewer=None): return _("%(user)s edited %(hes_or_your)s %(profile_link)s") % { 'user': self.hyperlink(self.user.get_profile_url(), self.friendly_username(viewer, self.user)), - 'hes_or_your': self.viewer_or_user_verb(viewer, self.user, _('your'), _('hes')), + 'hes_or_your': self.viewer_or_user_verb(viewer, self.user, _('your'), _('his')), 'profile_link': self.hyperlink(self.user.get_profile_url(), _('profile')), } diff --git a/forum/forms/__init__.py b/forum/forms/__init__.py new file mode 100644 index 0000000..ea5108c --- /dev/null +++ b/forum/forms/__init__.py @@ -0,0 +1,4 @@ +from qanda import * +from admin import * +from auth import * +from general import * diff --git a/forum/forms/admin.py b/forum/forms/admin.py new file mode 100644 index 0000000..eabeb57 --- /dev/null +++ b/forum/forms/admin.py @@ -0,0 +1,86 @@ +import socket +from django import forms +from django.utils.translation import ugettext as _ +from qanda import TitleField, EditorField +from forum import settings + +class IPListField(forms.CharField): + def clean(self, value): + ips = [ip.strip() for ip in value.strip().strip(',').split(',')] + iplist = [] + + if len(ips) < 1: + raise forms.ValidationError(_('Please input at least one ip address')) + + for ip in ips: + try: + socket.inet_aton(ip) + except socket.error: + raise forms.ValidationError(_('Invalid ip address: %s' % ip)) + + if not len(ip.split('.')) == 4: + raise forms.ValidationError(_('Please use the dotted quad notation for the ip addresses')) + + iplist.append(ip) + + return iplist + +class MaintenanceModeForm(forms.Form): + ips = IPListField(label=_('Allow ips'), + help_text=_('Comma separated list of ips allowed to access the site while in maintenance'), + required=True, + widget=forms.TextInput(attrs={'class': 'longstring'})) + + message = forms.CharField(label=_('Message'), + help_text=_('A message to display to your site visitors while in maintainance mode'), + widget=forms.Textarea) + + +TEMPLATE_CHOICES = ( + ('default', _('Default')), + ('sidebar', _('Default with sidebar')), + ('none', _('None')), +) + +RENDER_CHOICES = ( + ('markdown', _('Markdown')), + ('html', _('HTML')), + ('escape', _('Escaped')) +) + +class UrlFieldWidget(forms.TextInput): + def render(self, name, value, attrs=None): + if not value: + value = '' + + return """ + + + """ % {'name': name, 'value': value, 'app_url': settings.APP_URL, 'script_alias': settings.FORUM_SCRIPT_ALIAS} + + +class PageForm(forms.Form): + + def __init__(self, page, *args, **kwargs): + if page: + initial = page.extra + initial.update(dict(title=page.title, content=page.body)) + super(PageForm, self).__init__(initial=initial, *args, **kwargs) + else: + super(PageForm, self).__init__(*args, **kwargs) + + + title = forms.CharField(label=_('Title'), max_length=255, widget=forms.TextInput(attrs={'class': 'longstring'}), initial='New page') + path = forms.CharField(label=_('Page URL'), widget=UrlFieldWidget, initial='pages/new/') + + content = forms.CharField(label=_('Page Content'), widget=forms.Textarea(attrs={'rows': 30})) + render = forms.ChoiceField(widget=forms.RadioSelect, choices=RENDER_CHOICES, initial='markdown', label=_('Render Mode')) + + template = forms.ChoiceField(widget=forms.RadioSelect, choices=TEMPLATE_CHOICES, initial='default', label=_('Template')) + sidebar = forms.CharField(label=_('Sidebar Content'), widget=forms.Textarea(attrs={'rows': 20}), required=False) + sidebar_wrap = forms.BooleanField(label=_("Wrap sidebar block"), initial=True, required=False) + sidebar_render = forms.ChoiceField(widget=forms.RadioSelect, choices=RENDER_CHOICES, initial='markdown', label=_('Sidebar Render Mode')) + + comments = forms.BooleanField(label=_("Allow comments"), initial=False, required=False) + + \ No newline at end of file diff --git a/forum/authentication/forms.py b/forum/forms/auth.py similarity index 96% rename from forum/authentication/forms.py rename to forum/forms/auth.py index 01c857d..594e59f 100644 --- a/forum/authentication/forms.py +++ b/forum/forms/auth.py @@ -1,4 +1,4 @@ -from forum.utils.forms import NextUrlField, UserNameField, UserEmailField, SetPasswordForm +from general import NextUrlField, UserNameField, UserEmailField, SetPasswordForm from forum.models import Question, User from django.contrib.contenttypes.models import ContentType from django.utils.translation import ugettext as _ diff --git a/forum/utils/forms.py b/forum/forms/general.py similarity index 100% rename from forum/utils/forms.py rename to forum/forms/general.py diff --git a/forum/forms.py b/forum/forms/qanda.py similarity index 98% rename from forum/forms.py rename to forum/forms/qanda.py index 0b86537..7c58500 100644 --- a/forum/forms.py +++ b/forum/forms/qanda.py @@ -1,13 +1,12 @@ import re from datetime import date from django import forms -from models import * +from forum.models import * from django.utils.translation import ugettext as _ from django.contrib.humanize.templatetags.humanize import apnumber -from forum.models import User from django.utils.safestring import mark_safe -from forum.utils.forms import NextUrlField, UserNameField, SetPasswordForm +from general import NextUrlField, UserNameField, SetPasswordForm from forum import settings import logging @@ -255,6 +254,7 @@ class EditUserForm(forms.Form): if self.user.email != self.cleaned_data['email']: if settings.EMAIL_UNIQUE == True: if 'email' in self.cleaned_data: + from forum.models import User try: User.objects.get(email = self.cleaned_data['email']) except User.DoesNotExist: diff --git a/forum/middleware/anon_user.py b/forum/middleware/anon_user.py index a517f66..26c138c 100644 --- a/forum/middleware/anon_user.py +++ b/forum/middleware/anon_user.py @@ -1,5 +1,5 @@ from django.http import HttpResponseRedirect -from forum.utils.forms import get_next_url +from forum.forms import get_next_url from django.utils.translation import ugettext as _ from forum.user_messages import create_message, get_and_delete_messages from forum import settings diff --git a/forum/middleware/cancel.py b/forum/middleware/cancel.py index 15a4371..2d78a17 100644 --- a/forum/middleware/cancel.py +++ b/forum/middleware/cancel.py @@ -1,5 +1,5 @@ from django.http import HttpResponseRedirect -from forum.utils.forms import get_next_url +from forum.forms import get_next_url import logging class CancelActionMiddleware(object): def process_view(self, request, view_func, view_args, view_kwargs): diff --git a/forum/models/__init__.py b/forum/models/__init__.py index 449ca3a..7a15132 100644 --- a/forum/models/__init__.py +++ b/forum/models/__init__.py @@ -7,6 +7,7 @@ from comment import Comment from action import Action, ActionRepute from meta import Vote, Flag, Badge, Award from utils import KeyValue +from page import Page try: from south.modelsinspector import add_introspection_rules @@ -22,7 +23,7 @@ __all__ = [ 'Answer', 'AnswerRevision', 'Tag', 'Comment', 'MarkedTag', 'Badge', 'Award', 'ValidationHash', 'AuthKeyUserAssociation', 'SubscriptionSettings', 'KeyValue', 'User', - 'Action', 'ActionRepute', 'Vote', 'Flag' + 'Action', 'ActionRepute', 'Vote', 'Flag', 'Page' ] diff --git a/forum/models/node.py b/forum/models/node.py index bf05cb5..b0f5361 100644 --- a/forum/models/node.py +++ b/forum/models/node.py @@ -23,8 +23,12 @@ class NodeContent(models.Model): def html(self): return self.as_markdown() + @classmethod + def _as_markdown(cls, content, *extensions): + return mark_safe(sanitize_html(markdown.markdown(content, extensions=extensions))) + def as_markdown(self, *extensions): - return mark_safe(sanitize_html(markdown.markdown(self.body, extensions=extensions))) + return self._as_markdown(self.body, *extensions) @property def headline(self): diff --git a/forum/models/page.py b/forum/models/page.py new file mode 100644 index 0000000..ff3b1a4 --- /dev/null +++ b/forum/models/page.py @@ -0,0 +1,40 @@ +from base import * +from django.utils.translation import ugettext as _ + +class Page(Node): + friendly_name = _("page") + + @property + def published(self): + return self.marked + + def save(self, *args, **kwargs): + old_options = self._original_state.get('extra', None) + + super(Page, self).save(*args, **kwargs) + + registry = settings.STATIC_PAGE_REGISTRY + + if old_options: + registry.pop(old_options.get('path', ''), None) + + registry[self.extra['path']] = self.id + + + settings.STATIC_PAGE_REGISTRY.set_value(registry) + + @property + def headline(self): + if self.published: + return self.title + else: + return _("[Unpublished] %s") % self.title + + @models.permalink + def get_absolute_url(self): + return ('static_page', (), {'path': self.extra['path']}) + + class Meta(Node.Meta): + proxy = True + + \ No newline at end of file diff --git a/forum/settings/__init__.py b/forum/settings/__init__.py index 95f2441..addab2d 100644 --- a/forum/settings/__init__.py +++ b/forum/settings/__init__.py @@ -1,6 +1,5 @@ import os.path from base import Setting, SettingSet -from forms import ImageFormWidget from django.forms.widgets import Textarea from django.utils.translation import ugettext_lazy as _ @@ -18,6 +17,8 @@ DJSTYLE_ADMIN_INTERFACE = Setting('DJSTYLE_ADMIN_INTERFACE', True) APP_URL = djsettings.APP_URL FORUM_SCRIPT_ALIAS = djsettings.FORUM_SCRIPT_ALIAS +STATIC_PAGE_REGISTRY = Setting('STATIC_PAGE_REGISTRY', {}) + from basic import * from sidebar import * diff --git a/forum/settings/forms.py b/forum/settings/forms.py index 27581d5..432ba0b 100644 --- a/forum/settings/forms.py +++ b/forum/settings/forms.py @@ -1,8 +1,7 @@ import os -import socket from string import strip from django import forms -from base import Setting +from forum.settings.base import Setting from django.utils.translation import ugettext as _ from django.core.files.storage import FileSystemStorage @@ -110,36 +109,5 @@ class CommaStringListWidget(forms.Textarea): return ', '.join(data[name]) -class IPListField(forms.CharField): - def clean(self, value): - ips = [ip.strip() for ip in value.strip().strip(',').split(',')] - iplist = [] - - if len(ips) < 1: - raise forms.ValidationError(_('Please input at least one ip address')) - - for ip in ips: - try: - socket.inet_aton(ip) - except socket.error: - raise forms.ValidationError(_('Invalid ip address: %s' % ip)) - - if not len(ip.split('.')) == 4: - raise forms.ValidationError(_('Please use the dotted quad notation for the ip addresses')) - - iplist.append(ip) - - return iplist - -class MaintenanceModeForm(forms.Form): - ips = IPListField(label=_('Allow ips'), - help_text=_('Comma separated list of ips allowed to access the site while in maintenance'), - required=True, - widget=forms.TextInput(attrs={'class': 'longstring'})) - - message = forms.CharField(label=_('Message'), - help_text=_('A message to display to your site visitors while in maintainance mode'), - widget=forms.Textarea) - diff --git a/forum/skins/default/media/js/osqa.admin.js b/forum/skins/default/media/js/osqa.admin.js index 634f4c8..cce64a5 100644 --- a/forum/skins/default/media/js/osqa.admin.js +++ b/forum/skins/default/media/js/osqa.admin.js @@ -41,4 +41,21 @@ $(function() { } } }); + + $('.url_field').each(function() { + var $input = $(this); + var $anchor = $input.parent().find('.url_field_anchor'); + var app_url = $anchor.attr('href'); + + function rewrite_anchor() { + var val = app_url + $input.val(); + + $anchor.attr('href', val); + $anchor.html(val); + + } + + $input.keyup(rewrite_anchor); + rewrite_anchor(); + }); }); \ No newline at end of file diff --git a/forum/skins/default/templates/osqaadmin/djstyle_base.html b/forum/skins/default/templates/osqaadmin/djstyle_base.html index 2d74adf..7798b49 100644 --- a/forum/skins/default/templates/osqaadmin/djstyle_base.html +++ b/forum/skins/default/templates/osqaadmin/djstyle_base.html @@ -38,7 +38,7 @@