Access-Control-Allow-Origin: * in all API requests
[mygpo.git] / mygpo / api / advanced / lists.py
blob13b9198bf67fdff764dd2b4d01d329f39a03da56
2 # This file is part of my.gpodder.org.
4 # my.gpodder.org is free software: you can redistribute it and/or modify it
5 # under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or (at your
7 # option) any later version.
9 # my.gpodder.org is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
12 # License for more details.
14 # You should have received a copy of the GNU Affero General Public License
15 # along with my.gpodder.org. If not, see <http://www.gnu.org/licenses/>.
18 from functools import partial
19 from datetime import datetime
21 from django.http import HttpResponse, HttpResponseBadRequest, \
22 HttpResponseForbidden
23 from django.views.decorators.csrf import csrf_exempt
24 from django.core.urlresolvers import reverse
25 from django.contrib.sites.models import RequestSite
26 from django.utils.text import slugify
27 from django.views.decorators.cache import never_cache
28 from django.http import Http404
30 from mygpo.utils import get_timestamp
31 from mygpo.api.advanced.directory import podcast_data
32 from mygpo.api.httpresponse import JsonResponse
33 from mygpo.share.models import PodcastList
34 from mygpo.api.basic_auth import require_valid_user, check_username
35 from mygpo.core.models import Podcast
36 from mygpo.decorators import allowed_methods, repeat_on_conflict, cors_origin
37 from mygpo.api.simple import parse_subscription, format_podcast_list, \
38 check_format
39 from mygpo.share.views import list_decorator
40 from mygpo.users.models import User
41 from mygpo.db.couchdb.podcast import podcasts_by_id, podcast_for_url
42 from mygpo.db.couchdb.podcastlist import podcastlist_for_user_slug, \
43 podcastlists_for_user
47 @csrf_exempt
48 @require_valid_user
49 @check_username
50 @check_format
51 @never_cache
52 @allowed_methods(['POST'])
53 @cors_origin()
54 def create(request, username, format):
55 """ Creates a new podcast list and links to it in the Location header """
57 title = request.GET.get('title', None)
59 if not title:
60 return HttpResponseBadRequest('Title missing')
62 slug = slugify(title)
64 if not slug:
65 return HttpResponseBadRequest('Invalid title')
67 plist = podcastlist_for_user_slug(request.user._id, slug)
69 if plist:
70 return HttpResponse('List already exists', status=409)
72 urls = parse_subscription(request.body, format)
73 podcasts = [podcast_for_url(url, create=True) for url in urls]
74 podcast_ids = map(Podcast.get_id, podcasts)
76 plist = PodcastList()
77 plist.created_timestamp = get_timestamp(datetime.utcnow())
78 plist.title = title
79 plist.slug = slug
80 plist.user = request.user._id
81 plist.podcasts = podcast_ids
82 plist.save()
84 response = HttpResponse(status=201)
85 list_url = reverse('api-get-list', args=[request.user.username, slug, format])
86 response['Location'] = list_url
88 return response
92 def _get_list_data(l, username, domain):
93 return dict(
94 title= l.title,
95 name = l.slug,
96 web = 'http://%s%s' % (domain,
97 reverse('list-show', args=[username, l.slug])),
101 @csrf_exempt
102 @never_cache
103 @allowed_methods(['GET'])
104 @cors_origin()
105 def get_lists(request, username):
106 """ Returns a list of all podcast lists by the given user """
108 user = User.get_user(username)
109 if not user:
110 raise Http404
112 lists = podcastlists_for_user(user._id)
114 site = RequestSite(request)
116 get_data = partial(_get_list_data, username=user.username,
117 domain=site.domain)
118 lists_data = map(get_data, lists)
120 return JsonResponse(lists_data)
123 @csrf_exempt
124 @check_format
125 @never_cache
126 @allowed_methods(['GET', 'PUT', 'DELETE'])
127 @cors_origin()
128 def podcast_list(request, *args, **kwargs):
130 handlers = dict(
131 GET = get_list,
132 PUT = update_list,
133 DELETE = delete_list,
136 return handlers[request.method](request, *args, **kwargs)
139 @never_cache
140 @list_decorator(must_own=False)
141 @cors_origin()
142 def get_list(request, plist, owner, format):
143 """ Returns the contents of the podcast list """
145 try:
146 scale = int(request.GET.get('scale_logo', 64))
147 except (TypeError, ValueError):
148 return HttpResponseBadRequest('scale_logo has to be a numeric value')
150 podcasts = podcasts_by_id(plist.podcasts)
152 domain = RequestSite(request).domain
153 p_data = lambda p: podcast_data(p, domain, scale)
154 title = '{title} by {username}'.format(title=plist.title,
155 username=owner.username)
157 return format_podcast_list(podcasts, format, title, json_map=p_data,
158 jsonp_padding=request.GET.get('jsonp', ''),
159 xml_template='podcasts.xml', request=request)
162 @never_cache
163 @require_valid_user
164 @list_decorator(must_own=True)
165 @cors_origin()
166 def update_list(request, plist, owner, format):
167 """ Replaces the podcasts in the list and returns 204 No Content """
169 is_own = owner == request.uuser
171 if not is_own:
172 return HttpResponseForbidden()
174 urls = parse_subscription(request.body, format)
175 podcasts = [podcast_for_url(url, create=True) for url in urls]
176 podcast_ids = map(Podcast.get_id, podcasts)
178 @repeat_on_conflict(['podcast_ids'])
179 def _update(plist, podcast_ids):
180 plist.podcasts = podcast_ids
181 plist.save()
183 _update(plist=plist, podcast_ids=podcast_ids)
185 return HttpResponse(status=204)
188 @never_cache
189 @require_valid_user
190 @list_decorator(must_own=True)
191 @cors_origin()
192 def delete_list(request, plist, owner, format):
193 """ Delete the podcast list and returns 204 No Content """
195 is_own = owner == request.user
197 if not is_own:
198 return HttpResponseForbidden()
200 @repeat_on_conflict(['plist'])
201 def _delete(plist):
202 plist.delete()
204 _delete(plist=plist)
206 return HttpResponse(status=204)