[Migration] use migrated users
[mygpo.git] / mygpo / web / views / device.py
blobc51e68206e0304322b815d8c0332dfd3434bed22
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 wraps
19 from xml.parsers.expat import ExpatError
21 from django.shortcuts import render
22 from django.core.urlresolvers import reverse
23 from django.http import HttpResponseRedirect, HttpResponseBadRequest, \
24 HttpResponseNotFound
25 from django.contrib import messages
26 from mygpo.web.forms import DeviceForm, SyncForm
27 from mygpo.web.utils import symbian_opml_changes
28 from django.utils.translation import ugettext as _
29 from django.contrib.auth.decorators import login_required
30 from django.views.decorators.vary import vary_on_cookie
31 from django.views.decorators.cache import never_cache, cache_control
33 from restkit.errors import Unauthorized
35 from mygpo.api import simple
36 from mygpo.decorators import allowed_methods, repeat_on_conflict
37 from mygpo.users.models import DeviceUIDException, Client
38 from mygpo.users.tasks import sync_user, set_device_task_state
39 from mygpo.users.sync import get_grouped_devices
40 from mygpo.db.couchdb.podcast_state import podcast_states_for_device, \
41 remove_device_from_podcast_state
42 from mygpo.db.couchdb.user import set_device_deleted, unsync_device, set_device
45 @vary_on_cookie
46 @cache_control(private=True)
47 @login_required
48 def overview(request):
50 device_groups = get_grouped_devices(request.user)
51 deleted_devices = Client.objects.filter(user=request.user, deleted=True)
53 # create a "default" device
54 device = Client()
55 device_form = DeviceForm({
56 'name': device.name,
57 'type': device.type,
58 'uid': device.uid
61 return render(request, 'devicelist.html', {
62 'device_groups': device_groups,
63 'deleted_devices': deleted_devices,
64 'device_form': device_form,
69 def device_decorator(f):
70 @login_required
71 @vary_on_cookie
72 @cache_control(private=True)
73 @wraps(f)
74 def _decorator(request, uid, *args, **kwargs):
76 try:
77 device = Client.objects.get(user=request.user, uid=uid)
79 except Client.DoesNotExist as e:
80 return HttpResponseNotFound(str(e))
82 return f(request, device, *args, **kwargs)
84 return _decorator
88 @login_required
89 @device_decorator
90 def show(request, device):
92 request.user.sync_group(device)
94 subscriptions = list(device.get_subscribed_podcasts())
95 synced_with = request.user.get_synced(device)
97 sync_targets = list(request.user.get_sync_targets(device))
98 sync_form = SyncForm()
99 sync_form.set_targets(sync_targets,
100 _('Synchronize with the following devices'))
102 return render(request, 'device.html', {
103 'device': device,
104 'sync_form': sync_form,
105 'subscriptions': subscriptions,
106 'synced_with': synced_with,
107 'has_sync_targets': len(sync_targets) > 0,
111 @login_required
112 @never_cache
113 @allowed_methods(['POST'])
114 def create(request):
115 device_form = DeviceForm(request.POST)
117 if not device_form.is_valid():
118 messages.error(request, _('Please fill out all fields.'))
119 return HttpResponseRedirect(reverse('devices'))
121 device = Client()
122 device.name = device_form.cleaned_data['name']
123 device.type = device_form.cleaned_data['type']
124 device.uid = device_form.cleaned_data['uid'].replace(' ', '-')
125 try:
126 set_device(request.user, device)
127 messages.success(request, _('Device saved'))
129 except DeviceUIDException as e:
130 messages.error(request, _(unicode(e)))
131 return HttpResponseRedirect(reverse('devices'))
133 except Unauthorized:
134 messages.error(request, _("You can't use the same Device "
135 "ID for two devices."))
136 return HttpResponseRedirect(reverse('devices'))
138 return HttpResponseRedirect(reverse('device-edit', args=[device.uid]))
142 @device_decorator
143 @login_required
144 @allowed_methods(['POST'])
145 def update(request, device):
146 device_form = DeviceForm(request.POST)
148 uid = device.uid
150 if device_form.is_valid():
152 device.name = device_form.cleaned_data['name']
153 device.type = device_form.cleaned_data['type']
154 device.uid = device_form.cleaned_data['uid'].replace(' ', '-')
155 try:
156 set_device(request.user, device)
157 messages.success(request, _('Device updated'))
158 uid = device.uid # accept the new UID after rest has succeeded
160 except DeviceUIDException as e:
161 messages.error(request, _(str(e)))
163 except Unauthorized as u:
164 messages.error(request, _("You can't use the same Device "
165 "ID for two devices."))
167 return HttpResponseRedirect(reverse('device-edit', args=[uid]))
170 @device_decorator
171 @login_required
172 @allowed_methods(['GET'])
173 def edit(request, device):
175 device_form = DeviceForm({
176 'name': device.name,
177 'type': device.type,
178 'uid': device.uid
181 synced_with = request.user.get_synced(device)
183 sync_targets = list(request.user.get_sync_targets(device))
184 sync_form = SyncForm()
185 sync_form.set_targets(sync_targets,
186 _('Synchronize with the following devices'))
188 return render(request, 'device-edit.html', {
189 'device': device,
190 'device_form': device_form,
191 'sync_form': sync_form,
192 'synced_with': synced_with,
193 'has_sync_targets': len(sync_targets) > 0,
197 @device_decorator
198 @login_required
199 def upload_opml(request, device):
201 if not 'opml' in request.FILES:
202 return HttpResponseRedirect(reverse('device-edit', args=[device.uid]))
204 opml = request.FILES['opml'].read()
206 try:
207 subscriptions = simple.parse_subscription(opml, 'opml')
208 simple.set_subscriptions(subscriptions, request.user, device.uid, None)
210 except ExpatError as ee:
211 msg = _('Could not upload subscriptions: {err}').format(err=str(ee))
212 messages.error(request, msg)
213 return HttpResponseRedirect(reverse('device-edit', args=[device.uid]))
215 return HttpResponseRedirect(reverse('device', args=[device.uid]))
218 @device_decorator
219 @login_required
220 def opml(request, device):
221 response = simple.format_podcast_list(simple.get_subscriptions(request.user, device.uid), 'opml', request.user.username)
222 response['Content-Disposition'] = 'attachment; filename=%s.opml' % device.uid
223 return response
226 @device_decorator
227 @login_required
228 def symbian_opml(request, device):
229 subscriptions = simple.get_subscriptions(request.user, device.uid)
230 subscriptions = map(symbian_opml_changes, subscriptions)
232 response = simple.format_podcast_list(subscriptions, 'opml', request.user.username)
233 response['Content-Disposition'] = 'attachment; filename=%s.opml' % device.uid
234 return response
237 @device_decorator
238 @login_required
239 @allowed_methods(['POST'])
240 def delete(request, device):
241 user = request.user
242 unsync_device(user, device)
243 set_device_deleted(user, device, True)
244 set_device_task_state.delay(user)
245 return HttpResponseRedirect(reverse('devices'))
248 @login_required
249 @device_decorator
250 def delete_permanently(request, device):
252 states = podcast_states_for_device(device.id)
253 for state in states:
254 remove_device_from_podcast_state(state, device)
256 @repeat_on_conflict(['user'])
257 def _remove(user, device):
258 user.remove_device(device)
259 user.save()
261 _remove(user=request.user, device=device)
263 return HttpResponseRedirect(reverse('devices'))
265 @device_decorator
266 @login_required
267 def undelete(request, device):
268 user = request.user
269 set_device_deleted(user, device, False)
270 set_device_task_state.delay(user)
271 return HttpResponseRedirect(reverse('device', args=[device.uid]))
274 @device_decorator
275 @login_required
276 @allowed_methods(['POST'])
277 def sync(request, device):
279 form = SyncForm(request.POST)
280 if not form.is_valid():
281 return HttpResponseBadRequest('invalid')
283 try:
284 target_uid = form.get_target()
285 sync_target = request.user.get_device_by_uid(target_uid)
286 device.sync_with(sync_target)
288 except Client.DoesNotExist as e:
289 messages.error(request, str(e))
291 sync_user.delay(request.user)
293 return HttpResponseRedirect(reverse('device', args=[device.uid]))
296 @device_decorator
297 @login_required
298 @allowed_methods(['GET'])
299 def unsync(request, device):
301 @repeat_on_conflict(['user'])
302 def do_unsync(user, device):
303 user.unsync_device(device)
304 user.save()
306 try:
307 do_unsync(user=request.user, device=device)
309 except ValueError, e:
310 messages.error(request, 'Could not unsync the device: {err}'.format(
311 err=str(e)))
313 return HttpResponseRedirect(reverse('device', args=[device.uid]))
316 from mygpo.web import views
317 history = views.history