Add tag support for news items
authorMagnus Hagander <magnus@hagander.net>
Wed, 13 Dec 2017 19:55:07 +0000 (20:55 +0100)
committerMagnus Hagander <magnus@hagander.net>
Wed, 13 Dec 2017 19:55:07 +0000 (20:55 +0100)
This lets us separate things like project news from other OSS and from
commercial postings, for example, allowing for people to subscribe to
different feeds with just the parts they are interested in.

12 files changed:
media/css/text.css
pgweb/core/views.py
pgweb/news/admin.py
pgweb/news/feeds.py
pgweb/news/forms.py
pgweb/news/migrations/0003_news_tags.py [new file with mode: 0644]
pgweb/news/models.py
pgweb/news/views.py
pgweb/urls.py
templates/base/base.html
templates/news/item.html
templates/news/newsarchive.html

index 5a1f666e51e1887891b1b3996db70794c9be5027..afaccf4dc44ae2b2463164ce9afc2c84761ca4d1 100644 (file)
@@ -178,3 +178,20 @@ a:hover                         { color:#000000; text-decoration: underline; }
 #pgFrontNewsEventsContainer h3 img {
   margin-bottom: 10px;
 }
+
+/*
+ * News items tag list
+ */
+span.newstag {
+   background-color: #336791;
+   color: white;
+   padding: 4px;
+   -webkit-border-radius: 6px;
+   -moz-border-radius: 6px;
+   border-radius: 6px;
+}
+
+span.newstag a {
+   color: white;
+   text-decoration: none;
+}
index 4bdb35a52c02545b58aa55e79d63e237972d34a0..70f1dd54c06abd94b87bcce055e47f9e5f73f887 100644 (file)
@@ -24,7 +24,7 @@ from pgweb.util.misc import get_client_ip, varnish_purge
 from pgweb.util.sitestruct import get_all_pages_struct
 
 # models needed for the pieces on the frontpage
-from pgweb.news.models import NewsArticle
+from pgweb.news.models import NewsArticle, NewsTag
 from pgweb.events.models import Event
 from pgweb.quotes.models import Quote
 from models import Version, ImportedRSSItem
@@ -63,6 +63,7 @@ def home(request):
        return render_to_response('index.html', {
                'title': 'The world\'s most advanced open source database',
                'news': news,
+               'newstags': NewsTag.objects.all(),
                'community_events': community_events,
                'other_events': other_events,
                'traininginfo': traininginfo,
index 2ebe061490e5d4a87ca2f19d458d328ddb0c9d59..a4146ba28e6706f6c553ae2d54df67e5d47fad69 100644 (file)
@@ -1,11 +1,12 @@
 from django.contrib import admin
 
 from pgweb.util.admin import PgwebAdmin
-from models import NewsArticle
+from models import NewsArticle, NewsTag
 
 class NewsArticleAdmin(PgwebAdmin):
        list_display = ('title', 'org', 'date', 'approved', )
        list_filter = ('approved', )
+       filter_horizontal = ('tags', )
        search_fields = ('content', 'title', )
        change_form_template = 'admin/news/newsarticle/change_form.html'
 
@@ -17,3 +18,4 @@ class NewsArticleAdmin(PgwebAdmin):
                return super(NewsArticleAdmin, self).change_view(request, object_id, extra_context=my_context)
 
 admin.site.register(NewsArticle, NewsArticleAdmin)
+admin.site.register(NewsTag)
index 3bd563d27922c5a6fc227dfb9ca208e00befdf46..6c77fe3c643b1b59305a0c9f1e6f8fc3895bf731 100644 (file)
@@ -11,8 +11,14 @@ class NewsFeed(Feed):
        description_template = 'news/rss_description.html'
        title_template = 'news/rss_title.html'
 
-       def items(self):
-               return NewsArticle.objects.filter(approved=True)[:10]
+       def get_object(self, request, tagurl=None):
+               return tagurl
+
+       def items(self, obj):
+               if obj:
+                       return NewsArticle.objects.filter(approved=True, tags__urlname=obj)[:10]
+               else:
+                       return NewsArticle.objects.filter(approved=True)[:10]
 
        def item_link(self, obj):
                return "https://www.postgresql.org/about/news/%s/" % obj.id
index 6173cc2bbe218bb914782309ffb98b1b8857d43b..565a0aa17f9c6569f445c1d31b6e4f4dc3edbc12 100644 (file)
@@ -2,7 +2,7 @@ from django import forms
 from django.forms import ValidationError
 
 from pgweb.core.models import Organisation
-from models import NewsArticle
+from models import NewsArticle, NewsTag
 
 class NewsArticleForm(forms.ModelForm):
        def __init__(self, *args, **kwargs):
@@ -15,6 +15,15 @@ class NewsArticleForm(forms.ModelForm):
                                raise ValidationError("You cannot change the date on an article that has been approved")
                return self.cleaned_data['date']
 
+       @property
+       def described_checkboxes(self):
+               return {
+                       'tags': [(t.id, t.description) for t in NewsTag.objects.all()]
+               }
+
        class Meta:
                model = NewsArticle
                exclude = ('submitter', 'approved', 'tweeted')
+               widgets = {
+                       'tags': forms.CheckboxSelectMultiple,
+               }
diff --git a/pgweb/news/migrations/0003_news_tags.py b/pgweb/news/migrations/0003_news_tags.py
new file mode 100644 (file)
index 0000000..3243a43
--- /dev/null
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('news', '0002_news_tweet'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='NewsTag',
+            fields=[
+                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+                ('urlname', models.CharField(unique=True, max_length=20)),
+                ('name', models.CharField(max_length=32)),
+                ('description', models.CharField(max_length=200)),
+            ],
+        ),
+        migrations.AlterField(
+            model_name='newsarticle',
+            name='tweeted',
+            field=models.BooleanField(default=False),
+        ),
+        migrations.AddField(
+            model_name='newsarticle',
+            name='tags',
+            field=models.ManyToManyField(help_text=b'Hover mouse over tags to view full description', to='news.NewsTag'),
+        ),
+    ]
index 22deee7e0db2b525dba2a2ff23e6746bd8f9812d..5c3833820ea1a7cae79b73112b6f997a655eb193 100644 (file)
@@ -2,6 +2,14 @@ from django.db import models
 from datetime import date
 from pgweb.core.models import Organisation
 
+class NewsTag(models.Model):
+       urlname = models.CharField(max_length=20, null=False, blank=False, unique=True)
+       name = models.CharField(max_length=32, null=False, blank=False)
+       description = models.CharField(max_length=200, null=False, blank=False)
+
+       def __unicode__(self):
+               return self.name
+
 class NewsArticle(models.Model):
        org = models.ForeignKey(Organisation, null=False, blank=False, verbose_name="Organisation", help_text="If no organisations are listed, please check the <a href=\"/account/orglist/\">organisation list</a> and contact the organisation manager or webmaster@postgresql.org if none are listed.")
        approved = models.BooleanField(null=False, blank=False, default=False)
@@ -9,14 +17,17 @@ class NewsArticle(models.Model):
        title = models.CharField(max_length=200, null=False, blank=False)
        content = models.TextField(null=False, blank=False)
        tweeted = models.BooleanField(null=False, blank=False, default=False)
+       tags = models.ManyToManyField(NewsTag, blank=False, help_text="Hover mouse over tags to view full description")
 
        send_notification = True
+       send_m2m_notification = True
        markdown_fields = ('content',)
 
        def purge_urls(self):
                yield '/about/news/%s/' % self.pk
                yield '/about/newsarchive/'
                yield '/news.rss'
+               yield '/news/.*.rss'
                # FIXME: when to expire the front page?
                yield '/$'
 
index 37a731ff036e9651be2842876121952a9c4574c4..a20f456b93ea7ea3888587cf98849be76dbfc246 100644 (file)
@@ -5,13 +5,20 @@ from pgweb.util.decorators import login_required
 from pgweb.util.contexts import NavContext
 from pgweb.util.helpers import simple_form
 
-from models import NewsArticle
+from models import NewsArticle, NewsTag
 from forms import NewsArticleForm
 
-def archive(request, paging=None):
-       news = NewsArticle.objects.filter(approved=True)
+def archive(request, tag=None, paging=None):
+       if tag:
+               tag = get_object_or_404(NewsTag,urlname=tag.strip('/'))
+               news = NewsArticle.objects.filter(approved=True, tags=tag)
+       else:
+               tag = None
+               news = NewsArticle.objects.filter(approved=True)
        return render_to_response('news/newsarchive.html', {
                'news': news,
+               'tag': tag,
+               'newstags': NewsTag.objects.all(),
        }, NavContext(request, 'about'))
 
 def item(request, itemid, throwaway=None):
@@ -20,6 +27,7 @@ def item(request, itemid, throwaway=None):
                raise Http404
        return render_to_response('news/item.html', {
                'obj': news,
+               'newstags': NewsTag.objects.all(),
        }, NavContext(request, 'about'))
 
 @login_required
index 63724eef5ff6984f5728e8a97427105fd68a0c5c..04e6d61061d967f888fe69f7f5e665e8a54637ed 100644 (file)
@@ -19,7 +19,7 @@ urlpatterns = patterns('',
        (r'^$', 'pgweb.core.views.home'),
        (r'^dyncss/(?P<css>base|docs).css$', 'pgweb.core.views.dynamic_css'),
 
-       (r'^about/newsarchive/$', 'pgweb.news.views.archive'),
+       (r'^about/newsarchive/([^/]+/)?$', 'pgweb.news.views.archive'),
        (r'^about/news/(\d+)(-.*)?/$', 'pgweb.news.views.item'),
        (r'^about/events/$', 'pgweb.events.views.main'),
        (r'^about/eventarchive/$', 'pgweb.events.views.archive'),
@@ -72,7 +72,7 @@ urlpatterns = patterns('',
        # RSS feeds
        ###
        (r'^versions.rss$', VersionFeed()),
-       (r'^news.rss$', NewsFeed()),
+       (r'^news(/(?P<tagurl>[^/]+))?.rss$', NewsFeed()),
        (r'^events.rss$', EventFeed()),
 
        ###
index 6f742da11cc9707c0fa7d16672ec9057fa6e2274..1143e0f4b468518f1ae2e3afeaa59bec9355ba9b 100644 (file)
@@ -8,8 +8,14 @@
   <meta name="copyright" content="The PostgreSQL Global Development Group" />
   <style type="text/css" media="screen" title="Normal Text">@import url("/dyncss/base.css?{{gitrev}}");</style>
   <link rel="shortcut icon" href="/favicon.ico" />
-  <link rel="alternate" type="application/rss+xml" title="PostgreSQL News" href="{{link_root}}/news.rss" />
+{%if newstags %}
+{%comment%}Default RSS links are only shown on pages that have newstags set{%endcomment%}
+  <link rel="alternate" type="application/rss+xml" title="All PostgreSQL News" href="{{link_root}}/news.rss" />
+{%for t in newstags%}
+  <link rel="alternate" type="application/rss+xml" title="PostgreSQL News about {{t.name}}" href="{{link_root}}/news/{{t.urlname}}.rss" />
+{%endfor%}
   <link rel="alternate" type="application/rss+xml" title="PostgreSQL Events" href="{{link_root}}/events.rss" />
+{%endif%}
   <script type="text/javascript">
   var _gaq = _gaq || [];
   _gaq.push(['_setAccount', 'UA-1345454-1']);
index 6f7b7701a5ec04f46c143188fc3fc5f512798781..fbb71000dce4000377669e2692a2f4ef26fd45ba 100644 (file)
@@ -9,4 +9,7 @@
 <p><i>This post has been migrated from a previous version of the PostgreSQL
 website. We apologise for any formatting issues caused by the migration.</i></p>
 {%endif%}
+{%for t in obj.tags.all%}
+<span class="newstag"><a href="/about/newsarchive/{{t.urlname}}/">{{t.name}}</a></span>
+{%endfor%}
 {%endblock%}
index 76598427aab16e11d84caaeb61615e98d593a847..1ac52af583a731cb9e1ca4a240898ac5abe941a3 100644 (file)
@@ -1,8 +1,13 @@
 {%extends "base/page.html"%}
 {%load markup%}
-{%block title%}News archive{%endblock%}
+{%block title%}News archive{%if tag%} - {{tag.name}}{%endif%}{%endblock%}
 {%block contents%}
-<h1>News archive</h1>
+<h1>News archive{%if tag%} - {{tag.name}}{%endif%}</h1>
+
+<p>
+{%for t in newstags%}<span class="newstag"><a href="/about/newsarchive/{{t.urlname}}/">{{t.name}}</a></span> {%endfor%}
+</p>
+
 {%for obj in news %}
 <h2><a href="/about/news/{{obj.id}}/">{{obj.title}}</a></h2>
 <div class="newsdate">Posted on <b>{{obj.displaydate}}</b></div>