fix calculation of page count in directory
[mygpo.git] / mygpo / web / views / device.py
blob9f102bd571b2de349aae1493d6741bbe109aa6ea
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 Device, DeviceUIDException, \
38 DeviceDoesNotExist
39 from mygpo.db.couchdb.podcast_state import podcast_states_for_device
42 @vary_on_cookie
43 @cache_control(private=True)
44 @login_required
45 def overview(request):
47 device_groups = request.user.get_grouped_devices()
48 deleted_devices = request.user.inactive_devices
50 return render(request, 'devicelist.html', {
51 'device_groups': device_groups,
52 'deleted_devices': deleted_devices,
57 def device_decorator(f):
58 @login_required
59 @vary_on_cookie
60 @cache_control(private=True)
61 @wraps(f)
62 def _decorator(request, uid, *args, **kwargs):
64 try:
65 device = request.user.get_device_by_uid(uid, only_active=False)
67 except DeviceDoesNotExist as e:
68 return HttpResponseNotFound(str(e))
70 return f(request, device, *args, **kwargs)
72 return _decorator
76 @login_required
77 @device_decorator
78 def show(request, device):
80 request.user.sync_group(device)
82 subscriptions = list(device.get_subscribed_podcasts())
83 synced_with = request.user.get_synced(device)
85 sync_targets = list(request.user.get_sync_targets(device))
86 sync_form = SyncForm()
87 sync_form.set_targets(sync_targets,
88 _('Synchronize with the following devices'))
90 return render(request, 'device.html', {
91 'device': device,
92 'sync_form': sync_form,
93 'subscriptions': subscriptions,
94 'synced_with': synced_with,
95 'has_sync_targets': len(sync_targets) > 0,
99 @login_required
100 @never_cache
101 @allowed_methods(['POST'])
102 def create(request):
103 device_form = DeviceForm(request.POST)
105 if not device_form.is_valid():
107 messages.error(request, _('Please fill out all fields.'))
109 return HttpResponseRedirect(reverse('device-edit-new'))
112 device = Device()
113 device.name = device_form.cleaned_data['name']
114 device.type = device_form.cleaned_data['type']
115 device.uid = device_form.cleaned_data['uid'].replace(' ', '-')
116 try:
117 request.user.set_device(device)
118 request.user.save()
119 messages.success(request, _('Device saved'))
121 except DeviceUIDException as e:
122 messages.error(request, _(str(e)))
124 return render(request, 'device-create.html', {
125 'device': device,
126 'device_form': device_form,
129 except Unauthorized:
130 messages.error(request, _("You can't use the same Device "
131 "ID for two devices."))
133 return render(request, 'device-create.html', {
134 'device': device,
135 'device_form': device_form,
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 device.name = device_form.cleaned_data['name']
154 device.type = device_form.cleaned_data['type']
155 device.uid = device_form.cleaned_data['uid'].replace(' ', '-')
156 try:
157 request.user.update_device(device)
158 messages.success(request, _('Device updated'))
159 uid = device.uid # accept the new UID after rest has succeeded
161 except DeviceUIDException 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 @login_required
172 @vary_on_cookie
173 @cache_control(private=True)
174 @allowed_methods(['GET'])
175 def edit_new(request):
177 device = Device()
179 device_form = DeviceForm({
180 'name': device.name,
181 'type': device.type,
182 'uid' : device.uid
185 return render(request, 'device-create.html', {
186 'device': device,
187 'device_form': device_form,
193 @device_decorator
194 @login_required
195 @allowed_methods(['GET'])
196 def edit(request, device):
198 device_form = DeviceForm({
199 'name': device.name,
200 'type': device.type,
201 'uid' : device.uid
204 synced_with = request.user.get_synced(device)
206 sync_targets = list(request.user.get_sync_targets(device))
207 sync_form = SyncForm()
208 sync_form.set_targets(sync_targets,
209 _('Synchronize with the following devices'))
211 return render(request, 'device-edit.html', {
212 'device': device,
213 'device_form': device_form,
214 'sync_form': sync_form,
215 'synced_with': synced_with,
216 'has_sync_targets': len(sync_targets) > 0,
220 @device_decorator
221 @login_required
222 def upload_opml(request, device):
224 if not 'opml' in request.FILES:
225 return HttpResponseRedirect(reverse('device-edit', args=[device.uid]))
227 opml = request.FILES['opml'].read()
229 try:
230 subscriptions = simple.parse_subscription(opml, 'opml')
231 simple.set_subscriptions(subscriptions, request.user, device.uid, None)
233 except ExpatError as ee:
234 msg = _('Could not upload subscriptions: {err}').format(err=str(ee))
235 messages.error(request, msg)
236 return HttpResponseRedirect(reverse('device-edit', args=[device.uid]))
238 return HttpResponseRedirect(reverse('device', args=[device.uid]))
241 @device_decorator
242 @login_required
243 def opml(request, device):
244 response = simple.format_podcast_list(simple.get_subscriptions(request.user, device.uid), 'opml', request.user.username)
245 response['Content-Disposition'] = 'attachment; filename=%s.opml' % device.uid
246 return response
249 @device_decorator
250 @login_required
251 def symbian_opml(request, device):
252 subscriptions = simple.get_subscriptions(request.user, device.uid)
253 subscriptions = map(symbian_opml_changes, subscriptions)
255 response = simple.format_podcast_list(subscriptions, 'opml', request.user.username)
256 response['Content-Disposition'] = 'attachment; filename=%s.opml' % device.uid
257 return response
260 @device_decorator
261 @login_required
262 @allowed_methods(['POST'])
263 def delete(request, device):
265 @repeat_on_conflict(['user'])
266 def _delete(user, device):
267 if request.user.is_synced(device):
268 request.user.unsync_device(device)
269 device.deleted = True
270 user.set_device(device)
271 if user.is_synced(device):
272 user.unsync_device(device)
273 user.save()
275 _delete(user=request.user, device=device)
277 return HttpResponseRedirect(reverse('devices'))
280 @login_required
281 @device_decorator
282 def delete_permanently(request, device):
284 @repeat_on_conflict(['state'])
285 def remove_device(state, dev):
286 state.remove_device(dev)
287 state.save()
289 states = podcast_states_for_device(device.id)
290 for state in states:
291 remove_device(state=state, dev=device)
293 @repeat_on_conflict(['user'])
294 def _remove(user, device):
295 user.remove_device(device)
296 user.save()
298 _remove(user=request.user, device=device)
300 return HttpResponseRedirect(reverse('devices'))
302 @device_decorator
303 @login_required
304 def undelete(request, device):
306 device.deleted = False
307 request.user.update_device(device)
309 return HttpResponseRedirect(reverse('device', args=[device.uid]))
312 @device_decorator
313 @login_required
314 @allowed_methods(['POST'])
315 def sync(request, device):
317 form = SyncForm(request.POST)
318 if not form.is_valid():
319 return HttpResponseBadRequest('invalid')
322 @repeat_on_conflict(['user'])
323 def do_sync(user, device, sync_target):
324 user.sync_devices(device, sync_target)
325 user.save()
328 try:
329 target_uid = form.get_target()
330 sync_target = request.user.get_device_by_uid(target_uid)
331 do_sync(user=request.user, device=device, sync_target=sync_target)
333 except DeviceDoesNotExist as e:
334 messages.error(request, str(e))
336 request.user.sync_all()
338 return HttpResponseRedirect(reverse('device', args=[device.uid]))
341 @device_decorator
342 @login_required
343 @allowed_methods(['GET'])
344 def unsync(request, device):
346 @repeat_on_conflict(['user'])
347 def do_unsync(user, device):
348 user.unsync_device(device)
349 user.save()
351 try:
352 do_unsync(user=request.user, device=device)
354 except ValueError, e:
355 messages.error(request, 'Could not unsync the device: {err}'.format(
356 err=str(e)))
358 return HttpResponseRedirect(reverse('device', args=[device.uid]))
361 from mygpo.web import views
362 history = views.history