Repository: nextcloud/appstore

Issue:
RSS feed for the appstore 
Hi.

"Small" feature request - it would be nice to have a RSS feed for new and/or updated  apps in the appstore.

Discussion on [help.nextcloud.com](https://help.nextcloud.com/t/rss-feed-for-the-appstore/3381)

Cheers,
Andreas



Discussion:
The feeds should be available under:
- /en/feeds/releases.atom
- /en/feeds/releases.rss

and linked in the head https://github.com/nextcloud/appstore/blob/master/nextcloudappstore/core/templates/base.html#L12 by using <link> tags http://stackoverflow.com/questions/440391/how-to-tell-web-browsers-about-atom-feeds

The views should go into https://github.com/nextcloud/appstore/blob/master/nextcloudappstore/core/views.py and the urls should added here https://github.com/nextcloud/appstore/blob/master/nextcloudappstore/urls.py#L11

Ordering should be done by using the last_modified attribute (descending) on the AppRelease model https://github.com/nextcloud/appstore/blob/master/nextcloudappstore/core/models.py#L323

The title should be something like **News (1.0.0)**, the description should be the description of the app rendered using markdown (the href attribute of all links needs to be sanitized, e.g. via a regex like **^https://**), the authors should be taken from the app authors and the link should be the release download link. It should be possible to pass another language via URL parameter

Use https://docs.djangoproject.com/en/1.10/ref/contrib/syndication/ for publishing the feed


Resolving patch:
diff --git a/nextcloudappstore/core/feeds.py b/nextcloudappstore/core/feeds.py
new file mode 100644
--- /dev/null
+++ b/nextcloudappstore/core/feeds.py
@@ -0,0 +1,42 @@
+from django.conf import settings
+from django.contrib.syndication.views import Feed
+from django.urls import reverse_lazy
+from django.utils.feedgenerator import Atom1Feed
+from django.utils.translation import ugettext_lazy as _  # type: ignore
+from nextcloudappstore.core.models import AppRelease
+from markdown import markdown
+from bleach import clean
+
+
+class AppReleaseRssFeed(Feed):
+    title = _('Newest app releases')
+    description = _('Get the newest app release updates')
+    link = reverse_lazy('home')
+
+    def items(self):
+        return AppRelease.objects.order_by('-last_modified')[:10]
+
+    def item_title(self, item):
+        return '%s (%s)' % (item.app.name, item.version)
+
+    def item_description(self, item):
+        return clean(markdown(item.app.description),
+                     attributes=settings.MARKDOWN_ALLOWED_ATTRIBUTES,
+                     tags=settings.MARKDOWN_ALLOWED_TAGS)
+
+    def item_link(self, item):
+        return item.download
+
+    def item_author_name(self, item):
+        return '%s %s' % (item.app.owner.first_name, item.app.owner.last_name)
+
+    def item_pubdate(self, item):
+        return item.last_modified
+
+    def item_updateddate(self, item):
+        return item.last_modified
+
+
+class AppReleaseAtomFeed(AppReleaseRssFeed):
+    feed_type = Atom1Feed
+    subtitle = AppReleaseRssFeed.description
diff --git a/nextcloudappstore/settings/base.py b/nextcloudappstore/settings/base.py
--- a/nextcloudappstore/settings/base.py
+++ b/nextcloudappstore/settings/base.py
@@ -254,3 +254,22 @@
     BASE_DIR, 'nextcloudappstore/core/certificate/nextcloud.crt')
 NEXTCLOUD_CRL_LOCATION = join(
     BASE_DIR, 'nextcloudappstore/core/certificate/nextcloud.crl')
+
+# whitelist for serializing markdown
+MARKDOWN_ALLOWED_TAGS = [
+    'audio', 'video', 'source', 'dt', 'dd', 'dl', 'table', 'caption', 'tr',
+    'th', 'td', 'tbody', 'thead', 'h2', 'h3', 'h4', 'h5', 'h6', 'strong', 'em',
+    'code', 'pre', 'blockquote', 'p', 'ul', 'li', 'ol', 'br', 'del', 'a',
+    'img', 'figure', 'figcaption', 'cite', 'time', 'abbr', 'iframe', 'q'
+]
+MARKDOWN_ALLOWED_ATTRIBUTES = {
+    'audio': ['controls', 'src'],
+    'video': ['poster', 'controls', 'height', 'width', 'src'],
+    'source': ['src', 'type'],
+    'a': ['href'],
+    'img': ['src', 'title', 'alt'],
+    'time': ['datetime'],
+    'abbr': ['title'],
+    'iframe': ['width', 'height', 'frameborder', 'src', 'allowfullscreen'],
+    'q': ['cite'],
+}
diff --git a/nextcloudappstore/urls.py b/nextcloudappstore/urls.py
--- a/nextcloudappstore/urls.py
+++ b/nextcloudappstore/urls.py
@@ -2,7 +2,10 @@
 from allauth.socialaccount.views import signup as social_signup
 from csp.decorators import csp_exempt
 from django.conf.urls import url, include
+from django.conf.urls.i18n import i18n_patterns
 from django.contrib import admin
+
+from nextcloudappstore.core.feeds import AppReleaseAtomFeed, AppReleaseRssFeed
 from nextcloudappstore.core.views import CategoryAppListView, AppDetailView, \
     app_description, AppReleasesView, AppUploadView, LegalNoticeView, \
     AppRatingApi, AppRegisterView
@@ -32,3 +35,9 @@
     url(r'^admin/', admin.site.urls),
     url(r'^i18n/', include('django.conf.urls.i18n')),
 ]
+
+urlpatterns += i18n_patterns(
+    url(r'feeds/releases.rss', AppReleaseRssFeed(), name='feeds-releases-rss'),
+    url(r'feeds/releases.atom', AppReleaseAtomFeed(),
+        name='feeds-releases-atom'),
+)


Test patch: