def edit(request, aktivitet): if request.method == 'GET': aktivitet = Aktivitet.objects.prefetch_related('municipalities', 'counties').get(id=aktivitet) context = { 'aktivitet': aktivitet, 'difficulties': Aktivitet.DIFFICULTY_CHOICES, 'audiences': AktivitetAudience.AUDIENCE_CHOICES, 'categories': Aktivitet.CATEGORY_CHOICES, 'all_foreninger': Forening.get_all_sorted(), 'cabins': Cabin.objects.order_by('name'), 'admin_user_search_char_length': settings.ADMIN_USER_SEARCH_CHAR_LENGTH, 'counties': County.typical_objects().order_by('name'), 'municipalities': Municipality.objects.order_by('name'), 'omrader': sorted(Omrade.lookup(), key=lambda o: o.navn), 'now': datetime.now() } return render(request, 'common/admin/aktiviteter/edit/edit.html', context) elif request.method == 'POST': errors = False aktivitet = Aktivitet.objects.get(id=aktivitet) if aktivitet.is_imported(): # Should only be possible by circumventing client-side restrictions return redirect('admin.aktiviteter.views.edit', aktivitet.id) if 'code' in request.POST: aktivitet.code = request.POST['code'] if 'title' in request.POST: aktivitet.title = request.POST['title'] if 'description' in request.POST: aktivitet.description = request.POST['description'] if 'difficulty' in request.POST: aktivitet.difficulty = request.POST['difficulty'] if 'audiences' in request.POST: aktivitet.audiences = [ AktivitetAudience.objects.get(name=audience) for audience in request.POST.getlist('audiences') ] if 'category' in request.POST: aktivitet.category = request.POST['category'] if 'category_type' in request.POST: aktivitet.category_type = request.POST['category_type'] if 'publish' in request.POST: aktivitet.published = request.POST.get('publish') == 'publish' if 'getting_there' in request.POST: aktivitet.getting_there = request.POST['getting_there'] if 'omrader' in request.POST: aktivitet.omrader = request.POST.getlist('omrader') if 'ntb_id' not in request.POST or request.POST['ntb_id'] == '': aktivitet.turforslag = None else: aktivitet.turforslag = request.POST['ntb_id'] if aktivitet.published: # If published, set the extra relevant fields (otherwise ignore them) aktivitet.private = request.POST['private'] == 'private' try: aktivitet.pub_date = datetime.strptime(request.POST['pub_date'], "%d.%m.%Y").date() except ValueError: errors = True messages.error(request, 'invalid_date_format') forening_type, forening_id = request.POST['forening'].split(':') if forening_type == 'forening': forening = Forening.objects.get(id=forening_id) if not forening in request.user.children_foreninger(): raise PermissionDenied aktivitet.forening = forening elif forening_type == 'cabin': aktivitet.forening_cabin = Cabin.objects.get(id=forening_id) else: raise PermissionDenied if 'co_foreninger[]' in request.POST and request.POST['co_foreninger[]'] != '': co_foreninger = [] co_foreninger_cabin = [] for co_forening in request.POST.getlist('co_foreninger[]'): type, id = co_forening.split(':') if type == 'forening': co_foreninger.append(id) elif type == 'cabin': co_foreninger_cabin.append(id) else: raise PermissionDenied aktivitet.co_foreninger = co_foreninger aktivitet.co_foreninger_cabin = co_foreninger_cabin else: aktivitet.co_foreninger = [] aktivitet.co_foreninger_cabin = [] if 'latlng' in request.POST: latlng = request.POST['latlng'].split(',') if len(latlng) == 2: aktivitet.start_point = Point(float(latlng[0]), float(latlng[1])) aktivitet.save() aktivitet.counties = request.POST.getlist('counties') aktivitet.municipalities = request.POST.getlist('municipalities') aktivitet.category_tags.clear() if 'category_tags' in request.POST and request.POST['category_tags'] != '': for tag in request.POST.getlist('category_tags'): obj, created = Tag.objects.get_or_create(name=tag) aktivitet.category_tags.add(obj) aktivitet.images.all().delete() for i, image in parse_html_array(request.POST, 'images').items(): AktivitetImage( aktivitet=aktivitet, url=image['url'], text=image['description'], photographer=image['photographer'], order=i ).save() dates = parse_html_array(request.POST, 'dates').items() # Remove the date objects that were explicitly deleted (checks and verifications are done # client-side). Verify that those that would be implicitly deleted (by not being POSTed for # editing) match those explicitly POSTed. date_ids = [int(d['id']) for k, d in dates if d['id'] != ''] implicit_del = set([date.id for date in aktivitet.dates.all() if date.id not in date_ids]) if len(implicit_del) > 0: # Better to raise an exception and not delete anything. The user will be confused and # lose edits, but we'll get a report and hopefully be able to fix this, if it ever # happens. raise Exception("Implicit delete of AktivitetDate is strictly forbidden!") for i, date in dates: if date['id'] != '': # @TODO Check if this can be exploited. Can you hijack another trip's date by # setting an arbitrary ID in the date['id'] field? model = AktivitetDate.objects.get(id=date['id']) else: model = AktivitetDate(aktivitet=aktivitet) # @TODO for existing dates; if model.start_date > now; dissalow editing. # Explicit delete of dates if date['status'] == 'delete': if date['id'] != '': if model.participant_count() > 0: raise Exception("Date with participants can not be deleted!") model.delete() continue try: if not date['start_time']: date['start_time'] = '08:00' if not date['end_time']: date['end_time'] = '16:00' # @TODO check start_time > now model.start_date = datetime.strptime( "%s %s" % (date['start_date'], date['start_time']), "%d.%m.%Y %H:%M" ) # @TODO check end_time > start_time model.end_date = datetime.strptime( "%s %s" % (date['end_date'], date['end_time']), "%d.%m.%Y %H:%M" ) # @TODO check start_date > meeting_time if date['start_date'] and date['meeting_time']: model.meeting_time = datetime.strptime( "%s %s" % (date['start_date'], date['meeting_time']), "%d.%m.%Y %H:%M" ) if not date['signup_method'] or date['signup_method'] == 'none': # To the next maintainer. This block indicates that a date does not allow # signup. However, keep in mind that this might be an existing date with # participants. Hence, do not set model.participant to None event though it # might be tempting! model.signup_enabled = False model.signup_start = None model.signup_deadline = None model.cancel_deadline = None elif date['signup_method'] == 'normal' or date['signup_method'] == 'simple': model.signup_enabled = True if date.get('max_participants_limited'): model.max_participants = date['max_participants'] else: model.max_participants = None if date.get('no_signup_start') == '1': model.signup_start = None else: model.signup_start = datetime.strptime( date['signup_start'], "%d.%m.%Y", ).date() if 'no_signup_deadline' in date and date['no_signup_deadline'] == '1': model.signup_deadline = None elif 'signup_deadline' in date and date['signup_deadline'] != '': model.signup_deadline = datetime.strptime( date['signup_deadline'], "%d.%m.%Y", ).date() if 'no_cancel_deadline' in date and date['no_cancel_deadline'] == '1': model.cancel_deadline = None elif 'cancel_deadline' in date and date['cancel_deadline'] != '': model.cancel_deadline = datetime.strptime( date['cancel_deadline'], "%d.%m.%Y" ).date() else: raise Exception("Unrecognized POST value for signup_method field") except ValueError: errors = True messages.error(request, 'invalid_date_format') return redirect('admin.aktiviteter.views.edit', aktivitet.id) # Note that simple signup is currently disabled and due to be removed model.signup_simple_allowed = False model.meeting_place = date['meeting_place'] model.contact_type = date['contact_type'] model.contact_custom_name = date['contact_custom_name'] model.contact_custom_phone = date['contact_custom_phone'] model.contact_custom_email = date['contact_custom_email'] model.should_have_turleder = date.get('should_have_turleder') == '1' model.save() if date.get('should_have_turleder') == '1': # We need to specify the key for this particular field because the parse_html_array # function does not properly parse multidimensional arrays. key = 'dates[%s][turleder][]' % i if key in request.POST and request.POST[key] != '': model.turledere = request.POST.getlist(key) else: model.turledere = [] if not errors: messages.info(request, 'save_success') if json.loads(request.POST['preview']): return redirect('admin.aktiviteter.views.preview', aktivitet.id) else: return redirect('admin.aktiviteter.views.edit', aktivitet.id)