[Migration] fix issues with users from Django Auth
[mygpo.git] / mygpo / web / views / device.py
blob8dd775870e985f0378826e5c88b48a8eda8792cb
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 import uuid
19 from functools import wraps
20 from xml.parsers.expat import ExpatError
22 from django.shortcuts import render
23 from django.core.urlresolvers import reverse
24 from django.core.exceptions import ValidationError
25 from django.http import HttpResponseRedirect, HttpResponseBadRequest, \
26 HttpResponseNotFound
27 from django.contrib import messages
28 from mygpo.web.forms import DeviceForm, SyncForm
29 from mygpo.web.utils import symbian_opml_changes
30 from django.utils.translation import ugettext as _
31 from django.contrib.auth.decorators import login_required
32 from django.views.decorators.vary import vary_on_cookie
33 from django.views.decorators.cache import never_cache, cache_control
35 from restkit.errors import Unauthorized
37 from mygpo.api import simple
38 from mygpo.decorators import allowed_methods, repeat_on_conflict
39 from mygpo.users.models import Client, UserProxy
40 from mygpo.users.tasks import sync_user, set_device_task_state
41 from mygpo.db.couchdb.podcast_state import podcast_states_for_device, \
42 remove_device_from_podcast_state
45 @vary_on_cookie
46 @cache_control(private=True)
47 @login_required
48 def overview(request):
50 user = UserProxy.objects.from_user(request.user)
51 device_groups = user.get_grouped_devices()
52 deleted_devices = Client.objects.filter(user=request.user, deleted=True)
54 # create a "default" device
55 device = Client()
56 device_form = DeviceForm({
57 'name': device.name,
58 'type': device.type,
59 'uid': device.uid
62 return render(request, 'devicelist.html', {
63 'device_groups': list(device_groups),
64 'deleted_devices': list(deleted_devices),
65 'device_form': device_form,
70 def device_decorator(f):
71 @login_required
72 @vary_on_cookie
73 @cache_control(private=True)
74 @wraps(f)
75 def _decorator(request, uid, *args, **kwargs):
77 try:
78 device = Client.objects.get(user=request.user, uid=uid)
80 except Client.DoesNotExist as e:
81 return HttpResponseNotFound(str(e))
83 return f(request, device, *args, **kwargs)
85 return _decorator
89 @login_required
90 @device_decorator
91 def show(request, device):
93 subscriptions = list(device.get_subscribed_podcasts())
94 synced_with = device.synced_with()
96 sync_targets = list(device.get_sync_targets())
97 sync_form = SyncForm()
98 sync_form.set_targets(sync_targets,
99 _('Synchronize with the following devices'))
101 return render(request, 'device.html', {
102 'device': device,
103 'sync_form': sync_form,
104 'subscriptions': subscriptions,
105 'synced_with': synced_with,
106 'has_sync_targets': len(sync_targets) > 0,
110 @login_required
111 @never_cache
112 @allowed_methods(['POST'])
113 def create(request):
114 device_form = DeviceForm(request.POST)
116 if not device_form.is_valid():
117 messages.error(request, _('Please fill out all fields.'))
118 return HttpResponseRedirect(reverse('devices'))
120 try:
121 device = Client()
122 device.user = request.user
123 device.id = uuid.uuid1()
124 device.name = device_form.cleaned_data['name']
125 device.type = device_form.cleaned_data['type']
126 device.uid = device_form.cleaned_data['uid'].replace(' ', '-')
127 device.save()
128 messages.success(request, _('Device saved'))
130 except ValidationError as e:
131 messages.error(request, _(unicode(e)))
132 return HttpResponseRedirect(reverse('devices'))
134 except Unauthorized:
135 messages.error(request, _("You can't use the same Device "
136 "ID for two devices."))
137 return HttpResponseRedirect(reverse('devices'))
139 return HttpResponseRedirect(reverse('device-edit', args=[device.uid]))
143 @device_decorator
144 @login_required
145 @allowed_methods(['POST'])
146 def update(request, device):
147 device_form = DeviceForm(request.POST)
149 uid = device.uid
151 if device_form.is_valid():
153 try:
154 device.name = device_form.cleaned_data['name']
155 device.type = device_form.cleaned_data['type']
156 device.uid = device_form.cleaned_data['uid'].replace(' ', '-')
157 device.save()
158 messages.success(request, _('Device updated'))
159 uid = device.uid # accept the new UID after rest has succeeded
161 except ValidationError as e:
162 messages.error(request, _(str(e)))
164 except Unauthorized as u:
165 messages.error(request, _("You can't use the same Device "
166 "ID for two devices."))
168 return HttpResponseRedirect(reverse('device-edit', args=[uid]))
171 @device_decorator
172 @login_required
173 @allowed_methods(['GET'])
174 def edit(request, device):
176 device_form = DeviceForm({
177 'name': device.name,
178 'type': device.type,
179 'uid': device.uid
182 synced_with = device.synced_with()
184 sync_targets = list(device.get_sync_targets())
185 sync_form = SyncForm()
186 sync_form.set_targets(sync_targets,
187 _('Synchronize with the following devices'))
189 return render(request, 'device-edit.html', {
190 'device': device,
191 'device_form': device_form,
192 'sync_form': sync_form,
193 'synced_with': synced_with,
194 'has_sync_targets': len(sync_targets) > 0,
198 @device_decorator
199 @login_required
200 def upload_opml(request, device):
202 if not 'opml' in request.FILES:
203 return HttpResponseRedirect(reverse('device-edit', args=[device.uid]))
205 opml = request.FILES['opml'].read()
207 try:
208 subscriptions = simple.parse_subscription(opml, 'opml')
209 simple.set_subscriptions(subscriptions, request.user, device.uid, None)
211 except ExpatError as ee:
212 msg = _('Could not upload subscriptions: {err}').format(err=str(ee))
213 messages.error(request, msg)
214 return HttpResponseRedirect(reverse('device-edit', args=[device.uid]))
216 return HttpResponseRedirect(reverse('device', args=[device.uid]))
219 @device_decorator
220 @login_required
221 def opml(request, device):
222 response = simple.format_podcast_list(simple.get_subscriptions(request.user, device.uid), 'opml', request.user.username)
223 response['Content-Disposition'] = 'attachment; filename=%s.opml' % device.uid
224 return response
227 @device_decorator
228 @login_required
229 def symbian_opml(request, device):
230 subscriptions = simple.get_subscriptions(request.user, device.uid)
231 subscriptions = map(symbian_opml_changes, subscriptions)
233 response = simple.format_podcast_list(subscriptions, 'opml', request.user.username)
234 response['Content-Disposition'] = 'attachment; filename=%s.opml' % device.uid
235 return response
238 @device_decorator
239 @login_required
240 @allowed_methods(['POST'])
241 def delete(request, device):
242 user = request.user
243 device.stop_sync()
244 device.deleted = True
245 device.save()
246 set_device_task_state.delay(user)
247 return HttpResponseRedirect(reverse('devices'))
250 @login_required
251 @device_decorator
252 def delete_permanently(request, device):
254 states = podcast_states_for_device(device.id.hex)
255 for state in states:
256 remove_device_from_podcast_state(state, device)
258 device.delete()
259 return HttpResponseRedirect(reverse('devices'))
261 @device_decorator
262 @login_required
263 def undelete(request, device):
264 user = request.user
265 device.deleted = False
266 device.save()
267 set_device_task_state.delay(user)
268 return HttpResponseRedirect(reverse('device', args=[device.uid]))
271 @device_decorator
272 @login_required
273 @allowed_methods(['POST'])
274 def sync(request, device):
276 form = SyncForm(request.POST)
277 if not form.is_valid():
278 return HttpResponseBadRequest('invalid')
280 try:
281 target_uid = form.get_target()
282 sync_target = request.user.client_set.get(uid=target_uid)
283 device.sync_with(sync_target)
285 except Client.DoesNotExist as e:
286 messages.error(request, str(e))
288 sync_user.delay(request.user)
290 return HttpResponseRedirect(reverse('device', args=[device.uid]))
293 @device_decorator
294 @login_required
295 @allowed_methods(['GET'])
296 def unsync(request, device):
298 @repeat_on_conflict(['user'])
299 def do_unsync(user, device):
300 device.stop_sync()
301 user.save()
303 try:
304 do_unsync(user=request.user, device=device)
306 except ValueError, e:
307 messages.error(request, 'Could not unsync the device: {err}'.format(
308 err=str(e)))
310 return HttpResponseRedirect(reverse('device', args=[device.uid]))
313 from mygpo.web import views
314 history = views.history