From 3f150dcfade9443b3435309cb928f330966eb749 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Thu, 26 Apr 2012 22:19:02 -0500 Subject: Migrate news views to class-based generic views Signed-off-by: Dan McGee --- news/urls.py | 25 ++++++++---- news/views.py | 124 +++++++++++++++++++++++++++------------------------------- 2 files changed, 75 insertions(+), 74 deletions(-) (limited to 'news') diff --git a/news/urls.py b/news/urls.py index 10020f31..0eec6d86 100644 --- a/news/urls.py +++ b/news/urls.py @@ -1,14 +1,25 @@ from django.conf.urls import patterns +from django.contrib.auth.decorators import permission_required +from .views import (NewsDetailView, NewsListView, + NewsCreateView, NewsEditView, NewsDeleteView) + urlpatterns = patterns('news.views', - (r'^$', 'news_list', {}, 'news-list'), - (r'^add/$', 'add'), - (r'^preview/$', 'preview'), + (r'^$', + NewsListView.as_view(), {}, 'news-list'), + + (r'^preview/$', 'preview'), # old news URLs, permanent redirect view so we don't break all links - (r'^(?P\d+)/$', 'view_redirect'), - (r'^(?P[-\w]+)/$', 'view'), - (r'^(?P[-\w]+)/edit/$', 'edit'), - (r'^(?P[-\w]+)/delete/$', 'delete'), + (r'^(?P\d+)/$', 'view_redirect'), + + (r'^add/$', + permission_required('news.add_news')(NewsCreateView.as_view())), + (r'^(?P[-\w]+)/$', + NewsDetailView.as_view()), + (r'^(?P[-\w]+)/edit/$', + permission_required('news.change_news')(NewsEditView.as_view())), + (r'^(?P[-\w]+)/delete/$', + permission_required('news.delete_news')(NewsDeleteView.as_view())), ) # vim: set ts=4 sw=4 et: diff --git a/news/views.py b/news/views.py index 7ac009ba..268f0523 100644 --- a/news/views.py +++ b/news/views.py @@ -1,38 +1,15 @@ +import markdown + from django import forms -from django.contrib.auth.decorators import permission_required from django.http import HttpResponse from django.shortcuts import get_object_or_404, redirect from django.template.defaultfilters import slugify -from django.views.decorators.cache import never_cache -from django.views.generic import list_detail, create_update -from django.views.generic.simple import direct_to_template - -import markdown +from django.views.decorators.http import require_POST +from django.views.generic import (DetailView, ListView, + CreateView, UpdateView, DeleteView) from .models import News -def view_redirect(request, object_id): - newsitem = get_object_or_404(News, pk=object_id) - return redirect(newsitem, permanent=True) - -def view(request, slug=None): - return list_detail.object_detail(request, News.objects.all(), - slug=slug, - template_name="news/view.html", - template_object_name='news') - -#TODO: May as well use a date-based list here sometime -def news_list(request): - return list_detail.object_list(request, - News.objects.all().select_related('author').defer('content'), - paginate_by=50, - template_name="news/list.html", - template_object_name="news") - -class NewsForm(forms.ModelForm): - class Meta: - model = News - exclude = ('id', 'slug', 'author', 'postdate') def find_unique_slug(newsitem): '''Attempt to find a unique slug for this news item.''' @@ -46,46 +23,59 @@ def find_unique_slug(newsitem): return suffixed -@permission_required('news.add_news') -@never_cache -def add(request): - if request.POST: - form = NewsForm(request.POST) - if form.is_valid(): - newsitem = form.save(commit=False) - newsitem.author = request.user - newsitem.slug = find_unique_slug(newsitem) - newsitem.save() - return redirect(newsitem) - else: - form = NewsForm() - return direct_to_template(request, 'news/add.html', { 'form': form }) - -@permission_required('news.delete_news') -@never_cache -def delete(request, slug): - return create_update.delete_object(request, - News, - slug=slug, - post_delete_redirect='/news/', - template_name='news/delete.html', - template_object_name='news') - -@permission_required('news.change_news') -@never_cache -def edit(request, slug): - return create_update.update_object(request, - slug=slug, - form_class=NewsForm, - template_name="news/add.html") - -@permission_required('news.change_news') -@never_cache + +class NewsForm(forms.ModelForm): + class Meta: + model = News + exclude = ('id', 'slug', 'author', 'postdate') + + +class NewsDetailView(DetailView): + model = News + template_name = "news/view.html" + + +class NewsListView(ListView): + queryset = News.objects.all().select_related('author').defer('content') + template_name = "news/list.html" + paginate_by = 50 + + +class NewsCreateView(CreateView): + model = News + form_class = NewsForm + template_name = "news/add.html" + + def form_valid(self, form): + # special logic, we auto-fill the author and slug fields + newsitem = form.save(commit=False) + newsitem.author = self.request.user + newsitem.slug = find_unique_slug(newsitem) + newsitem.save() + return super(NewsCreateView, self).form_valid(form) + + +class NewsEditView(UpdateView): + model = News + form_class = NewsForm + template_name = "news/add.html" + + +class NewsDeleteView(DeleteView): + model = News + template_name = "news/delete.html" + success_url = "/news/" + + +def view_redirect(request, object_id): + newsitem = get_object_or_404(News, pk=object_id) + return redirect(newsitem, permanent=True) + + +@require_POST def preview(request): - markup = '' - if request.POST: - data = request.POST.get('data', '') - markup = markdown.markdown(data) + data = request.POST.get('data', '') + markup = markdown.markdown(data) return HttpResponse(markup) # vim: set ts=4 sw=4 et: -- cgit v1.2.3-2-g168b From badc535aeb1d310a9b8aa59aade07045e6eae653 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Wed, 18 Apr 2012 15:05:43 -0500 Subject: Ensure order_by default value is cleared when using distinct() Otherwise the queryset returns nonsensical results. I find the design of this less than obvious but so be it; we can ensure the results work regardless of a default ordering on the model. Signed-off-by: Dan McGee --- news/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'news') diff --git a/news/views.py b/news/views.py index 268f0523..03f3b0ac 100644 --- a/news/views.py +++ b/news/views.py @@ -13,7 +13,8 @@ from .models import News def find_unique_slug(newsitem): '''Attempt to find a unique slug for this news item.''' - existing = list(News.objects.values_list('slug', flat=True).distinct()) + existing = list(News.objects.values_list( + 'slug', flat=True).order_by().distinct()) suffixed = slug = slugify(newsitem.title) suffix = 0 -- cgit v1.2.3-2-g168b From c0bf9e20660cfae7ea8994472555bba23398b598 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Tue, 24 Jul 2012 09:19:48 -0500 Subject: Remove custom utc_now() function, use django.utils.timezone.now() This was around from the time when we handled timezones sanely and Django did not; now that we are on 1.4 we no longer need our own code to handle this. Signed-off-by: Dan McGee --- news/models.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'news') diff --git a/news/models.py b/news/models.py index 95026e1d..2efea579 100644 --- a/news/models.py +++ b/news/models.py @@ -1,8 +1,7 @@ from django.db import models from django.contrib.auth.models import User from django.contrib.sites.models import Site - -from main.utils import utc_now +from django.utils.timezone import now class News(models.Model): @@ -29,13 +28,13 @@ class News(models.Model): def set_news_fields(sender, **kwargs): news = kwargs['instance'] - now = utc_now() - news.last_modified = now + current_time = now() + news.last_modified = current_time if not news.postdate: - news.postdate = now + news.postdate = current_time # http://diveintomark.org/archives/2004/05/28/howto-atom-id news.guid = 'tag:%s,%s:%s' % (Site.objects.get_current(), - now.strftime('%Y-%m-%d'), news.get_absolute_url()) + current_time.strftime('%Y-%m-%d'), news.get_absolute_url()) # connect signals needed to keep cache in line with reality from main.utils import refresh_latest -- cgit v1.2.3-2-g168b From 0b97d52351fc2bdcae16f1a1e7c56afd4ed476ad Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Fri, 26 Oct 2012 16:49:58 -0500 Subject: Enable safe mode for markdown parsing Although we don't allow unauthenticated users to post content, we should still cover our bases here and ensure people can't inject stuff into the production website via an inadvertent XSS. Signed-off-by: Dan McGee --- news/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'news') diff --git a/news/views.py b/news/views.py index 03f3b0ac..c0230f1e 100644 --- a/news/views.py +++ b/news/views.py @@ -76,7 +76,7 @@ def view_redirect(request, object_id): @require_POST def preview(request): data = request.POST.get('data', '') - markup = markdown.markdown(data) + markup = markdown.markdown(data, safe_mode=True) return HttpResponse(markup) # vim: set ts=4 sw=4 et: -- cgit v1.2.3-2-g168b From bdee24b9d1279de67dd238e3644c2efff314bd7b Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Fri, 26 Oct 2012 17:11:11 -0500 Subject: Cleanup meta model attributes Signed-off-by: Dan McGee --- news/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'news') diff --git a/news/models.py b/news/models.py index 2efea579..91232706 100644 --- a/news/models.py +++ b/news/models.py @@ -24,7 +24,7 @@ class News(models.Model): db_table = 'news' verbose_name_plural = 'news' get_latest_by = 'postdate' - ordering = ['-postdate'] + ordering = ('-postdate',) def set_news_fields(sender, **kwargs): news = kwargs['instance'] -- cgit v1.2.3-2-g168b From 62bb3db8ada68a22c7a58f32b2e6bed63f19e53c Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Fri, 26 Oct 2012 17:36:12 -0500 Subject: Remove usages of 'django.contrib.markup' Switch to the news model being able to spit out the HTML version of the content, and don't use the markup contrib module. This is deprecated as of Django 1.5 so we can move off it now to save trouble down the road when it is fully removed. Signed-off-by: Dan McGee --- news/models.py | 7 +++++++ news/views.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'news') diff --git a/news/models.py b/news/models.py index 91232706..40238cde 100644 --- a/news/models.py +++ b/news/models.py @@ -1,6 +1,9 @@ +import markdown + from django.db import models from django.contrib.auth.models import User from django.contrib.sites.models import Site +from django.utils.safestring import mark_safe from django.utils.timezone import now @@ -17,6 +20,10 @@ class News(models.Model): def get_absolute_url(self): return '/news/%s/' % self.slug + def html(self): + return mark_safe(markdown.markdown( + self.content, safe_mode=True, enable_attributes=False)) + def __unicode__(self): return self.title diff --git a/news/views.py b/news/views.py index c0230f1e..74bec058 100644 --- a/news/views.py +++ b/news/views.py @@ -76,7 +76,7 @@ def view_redirect(request, object_id): @require_POST def preview(request): data = request.POST.get('data', '') - markup = markdown.markdown(data, safe_mode=True) + markup = markdown.markdown(data, safe_mode=True, enable_attributes=False) return HttpResponse(markup) # vim: set ts=4 sw=4 et: -- cgit v1.2.3-2-g168b From ce5b0b2c5c10a3c840fd8aaa696ec2b8f403dc5b Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Wed, 31 Oct 2012 00:21:45 -0500 Subject: Disable markdown safe mode Unless we want older news items to look like [HTML_REMOVED]this[HTML_REMOVED] all over the place. I'm tempted to mark old items as non-safe but enforce safe mode for all new news postings. Signed-off-by: Dan McGee --- news/models.py | 2 +- news/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'news') diff --git a/news/models.py b/news/models.py index 40238cde..55d36318 100644 --- a/news/models.py +++ b/news/models.py @@ -22,7 +22,7 @@ class News(models.Model): def html(self): return mark_safe(markdown.markdown( - self.content, safe_mode=True, enable_attributes=False)) + self.content, safe_mode=False, enable_attributes=False)) def __unicode__(self): return self.title diff --git a/news/views.py b/news/views.py index 74bec058..52182800 100644 --- a/news/views.py +++ b/news/views.py @@ -76,7 +76,7 @@ def view_redirect(request, object_id): @require_POST def preview(request): data = request.POST.get('data', '') - markup = markdown.markdown(data, safe_mode=True, enable_attributes=False) + markup = markdown.markdown(data, safe_mode=False, enable_attributes=False) return HttpResponse(markup) # vim: set ts=4 sw=4 et: -- cgit v1.2.3-2-g168b From 4122e97f7aa5ebb919c2afc88a3e548a2ab1e2aa Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Wed, 31 Oct 2012 00:56:52 -0500 Subject: Store 'safe_mode' attribute on news model This lets us identify old news items that need to allow HTML through the markdown parser. For all new news items, we will disallow raw HTML. Signed-off-by: Dan McGee --- .../0011_auto__add_field_news_safe_mode.py | 68 ++++++++++++++++++++ news/migrations/0012_mark_old_news_safe_exempt.py | 73 ++++++++++++++++++++++ news/models.py | 3 +- 3 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 news/migrations/0011_auto__add_field_news_safe_mode.py create mode 100644 news/migrations/0012_mark_old_news_safe_exempt.py (limited to 'news') diff --git a/news/migrations/0011_auto__add_field_news_safe_mode.py b/news/migrations/0011_auto__add_field_news_safe_mode.py new file mode 100644 index 00000000..565c7adb --- /dev/null +++ b/news/migrations/0011_auto__add_field_news_safe_mode.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + db.add_column('news', 'safe_mode', + self.gf('django.db.models.fields.BooleanField')(default=True), + keep_default=True) + + def backwards(self, orm): + db.delete_column('news', 'safe_mode') + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'news.news': { + 'Meta': {'ordering': "('-postdate',)", 'object_name': 'News', 'db_table': "'news'"}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'news_author'", 'on_delete': 'models.PROTECT', 'to': "orm['auth.User']"}), + 'content': ('django.db.models.fields.TextField', [], {}), + 'guid': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_modified': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'postdate': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'safe_mode': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + } + } + + complete_apps = ['news'] diff --git a/news/migrations/0012_mark_old_news_safe_exempt.py b/news/migrations/0012_mark_old_news_safe_exempt.py new file mode 100644 index 00000000..b2661cd8 --- /dev/null +++ b/news/migrations/0012_mark_old_news_safe_exempt.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +import markdown + +from south.db import db +from south.v2 import DataMigration +from django.db import models + +class Migration(DataMigration): + + def forwards(self, orm): + md = markdown.Markdown(safe_mode=True, enable_attributes=False) + magic = md.html_replacement_text + items = orm.News.objects.all() + has_html = [item.pk for item in items if magic in md.convert(item.content)] + for pk in has_html: + orm.News.objects.filter(pk=pk).update(safe_mode=False) + + def backwards(self, orm): + orm.News.objects.all().update(safe_mode=True) + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'news.news': { + 'Meta': {'ordering': "('-postdate',)", 'object_name': 'News', 'db_table': "'news'"}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'news_author'", 'on_delete': 'models.PROTECT', 'to': "orm['auth.User']"}), + 'content': ('django.db.models.fields.TextField', [], {}), + 'guid': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_modified': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'postdate': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'safe_mode': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + } + } + + complete_apps = ['news'] + symmetrical = True diff --git a/news/models.py b/news/models.py index 55d36318..42adc199 100644 --- a/news/models.py +++ b/news/models.py @@ -16,13 +16,14 @@ class News(models.Model): title = models.CharField(max_length=255) guid = models.CharField(max_length=255, editable=False) content = models.TextField() + safe_mode = models.BooleanField(default=True, editable=False) def get_absolute_url(self): return '/news/%s/' % self.slug def html(self): return mark_safe(markdown.markdown( - self.content, safe_mode=False, enable_attributes=False)) + self.content, safe_mode=self.safe_mode, enable_attributes=False)) def __unicode__(self): return self.title -- cgit v1.2.3-2-g168b From 761084f280007e302fc9ae9c738b32fd0490bb70 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Thu, 1 Nov 2012 09:21:54 -0500 Subject: Allow editing news.safe_mode flag via admin screen We need to mark the property as editable, but you still don't have access to it through the normal non-admin views and edit screen. Signed-off-by: Dan McGee --- news/admin.py | 2 +- news/models.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'news') diff --git a/news/admin.py b/news/admin.py index 1b7de1d8..ad3cf517 100644 --- a/news/admin.py +++ b/news/admin.py @@ -3,7 +3,7 @@ from django.contrib import admin from .models import News class NewsAdmin(admin.ModelAdmin): - list_display = ('title', 'author', 'postdate', 'last_modified') + list_display = ('title', 'author', 'postdate', 'last_modified', 'safe_mode') list_filter = ('postdate', 'author') search_fields = ('title', 'content') diff --git a/news/models.py b/news/models.py index 42adc199..d51db7c7 100644 --- a/news/models.py +++ b/news/models.py @@ -16,7 +16,7 @@ class News(models.Model): title = models.CharField(max_length=255) guid = models.CharField(max_length=255, editable=False) content = models.TextField() - safe_mode = models.BooleanField(default=True, editable=False) + safe_mode = models.BooleanField(default=True) def get_absolute_url(self): return '/news/%s/' % self.slug @@ -34,6 +34,7 @@ class News(models.Model): get_latest_by = 'postdate' ordering = ('-postdate',) + def set_news_fields(sender, **kwargs): news = kwargs['instance'] current_time = now() -- cgit v1.2.3-2-g168b From d616a40cf57d417775d7277a6d03f51c2967e8d9 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sun, 11 Nov 2012 15:47:44 -0600 Subject: Exclude news.safe_mode on news edit screen Signed-off-by: Dan McGee --- news/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'news') diff --git a/news/views.py b/news/views.py index 52182800..efd93fdb 100644 --- a/news/views.py +++ b/news/views.py @@ -28,7 +28,7 @@ def find_unique_slug(newsitem): class NewsForm(forms.ModelForm): class Meta: model = News - exclude = ('id', 'slug', 'author', 'postdate') + exclude = ('id', 'slug', 'author', 'postdate', 'safe_mode') class NewsDetailView(DetailView): -- cgit v1.2.3-2-g168b From fc7eb4aebf63525155bcadd366a87eed8f161568 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Wed, 28 Nov 2012 09:28:28 -0600 Subject: Add safe_mode filter to news admin; preview uses safe mode Signed-off-by: Dan McGee --- news/admin.py | 3 ++- news/views.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'news') diff --git a/news/admin.py b/news/admin.py index ad3cf517..acceb515 100644 --- a/news/admin.py +++ b/news/admin.py @@ -4,7 +4,8 @@ from .models import News class NewsAdmin(admin.ModelAdmin): list_display = ('title', 'author', 'postdate', 'last_modified', 'safe_mode') - list_filter = ('postdate', 'author') + list_filter = ('postdate', 'author', 'safe_mode') search_fields = ('title', 'content') + date_hierarchy = 'postdate' admin.site.register(News, NewsAdmin) diff --git a/news/views.py b/news/views.py index efd93fdb..0e22ac34 100644 --- a/news/views.py +++ b/news/views.py @@ -76,7 +76,7 @@ def view_redirect(request, object_id): @require_POST def preview(request): data = request.POST.get('data', '') - markup = markdown.markdown(data, safe_mode=False, enable_attributes=False) + markup = markdown.markdown(data, safe_mode=True, enable_attributes=False) return HttpResponse(markup) # vim: set ts=4 sw=4 et: -- cgit v1.2.3-2-g168b From e68a5073a6e8b9473f726734e0b51fdb0a42c14b Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Thu, 27 Dec 2012 17:02:40 -0600 Subject: Fix "RuntimeWarning: DateTimeField received a naive datetime" warnings When running tests, we can find old migrations that didn't use datetime objects with timezones attached. Signed-off-by: Dan McGee --- news/migrations/0003_new_date_columns_precision.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'news') diff --git a/news/migrations/0003_new_date_columns_precision.py b/news/migrations/0003_new_date_columns_precision.py index 21b64443..1c97f488 100644 --- a/news/migrations/0003_new_date_columns_precision.py +++ b/news/migrations/0003_new_date_columns_precision.py @@ -1,14 +1,14 @@ # encoding: utf-8 -import datetime from south.db import db from south.v2 import SchemaMigration from django.db import models +from django.utils.timezone import now class Migration(SchemaMigration): def forwards(self, orm): # Adding field 'News.last_modified' - db.add_column('news', 'last_modified', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, default=datetime.datetime.now(), db_index=True, blank=True), keep_default=False) + db.add_column('news', 'last_modified', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, default=now(), db_index=True, blank=True), keep_default=False) # Changing field 'News.postdate' db.alter_column('news', 'postdate', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True)) # Adding index on 'News', fields ['postdate'] -- cgit v1.2.3-2-g168b From 1dc6b867f48786ab6973d6832f386c9d771b58e0 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Fri, 28 Dec 2012 09:42:17 -0600 Subject: Populate the todolist slug field and mark non-null This is ripped off from commit 7c92ddbd3c86d when we added slugs to News objects. Signed-off-by: Dan McGee --- news/migrations/0005_add_slugs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'news') diff --git a/news/migrations/0005_add_slugs.py b/news/migrations/0005_add_slugs.py index 2a3b6174..96bd5213 100644 --- a/news/migrations/0005_add_slugs.py +++ b/news/migrations/0005_add_slugs.py @@ -11,7 +11,7 @@ class Migration(DataMigration): def forwards(self, orm): existing = list(orm.News.objects.values_list( 'slug', flat=True).distinct()) - for item in orm.News.objects.all(): + for item in orm.News.objects.defer('content').filter(slug=None): suffixed = slug = slugify(item.title) suffix = 1 while suffixed in existing: @@ -24,7 +24,7 @@ class Migration(DataMigration): item.save() def backwards(self, orm): - orm.News.obects.all.update(slug=None) + orm.News.objects.all.update(slug=None) models = { 'auth.group': { -- cgit v1.2.3-2-g168b From 563a618e697c918c2a76c63a5217047a8d3c1489 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Fri, 28 Dec 2012 10:12:09 -0600 Subject: Move slug creation helper to main/utils Signed-off-by: Dan McGee --- news/views.py | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) (limited to 'news') diff --git a/news/views.py b/news/views.py index 0e22ac34..62d30fde 100644 --- a/news/views.py +++ b/news/views.py @@ -3,26 +3,12 @@ import markdown from django import forms from django.http import HttpResponse from django.shortcuts import get_object_or_404, redirect -from django.template.defaultfilters import slugify from django.views.decorators.http import require_POST from django.views.generic import (DetailView, ListView, CreateView, UpdateView, DeleteView) from .models import News - - -def find_unique_slug(newsitem): - '''Attempt to find a unique slug for this news item.''' - existing = list(News.objects.values_list( - 'slug', flat=True).order_by().distinct()) - - suffixed = slug = slugify(newsitem.title) - suffix = 0 - while suffixed in existing: - suffix += 1 - suffixed = "%s-%d" % (slug, suffix) - - return suffixed +from main.utils import find_unique_slug class NewsForm(forms.ModelForm): @@ -51,7 +37,7 @@ class NewsCreateView(CreateView): # special logic, we auto-fill the author and slug fields newsitem = form.save(commit=False) newsitem.author = self.request.user - newsitem.slug = find_unique_slug(newsitem) + newsitem.slug = find_unique_slug(News, newsitem.title) newsitem.save() return super(NewsCreateView, self).form_valid(form) -- cgit v1.2.3-2-g168b From 7bb5c54d7ee3112eaa827bad5b2be10a017dbb3e Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Mon, 31 Dec 2012 09:42:16 -0600 Subject: Minor news admin code cleanup Signed-off-by: Dan McGee --- news/admin.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'news') diff --git a/news/admin.py b/news/admin.py index acceb515..562c16d4 100644 --- a/news/admin.py +++ b/news/admin.py @@ -2,10 +2,14 @@ from django.contrib import admin from .models import News + class NewsAdmin(admin.ModelAdmin): list_display = ('title', 'author', 'postdate', 'last_modified', 'safe_mode') list_filter = ('postdate', 'author', 'safe_mode') search_fields = ('title', 'content') date_hierarchy = 'postdate' + admin.site.register(News, NewsAdmin) + +# vim: set ts=4 sw=4 et: -- cgit v1.2.3-2-g168b