def import_sequences_from_file(request, issue_id, changeset_id, use_csv = False): changeset = get_object_or_404(Changeset, id=changeset_id) if request.user != changeset.indexer: return render_error(request, 'Only the reservation holder may import stories.') try: # Process add form if this is a POST. if request.method == 'POST' and 'flatfile' in request.FILES: issue_revision = changeset.issuerevisions.get(issue=issue_id) if 'csv' in request.POST: use_csv = True else: use_csv = False lines, failure = _process_file(request, changeset, is_issue=False, use_csv=use_csv) if failure: return lines running_number = issue_revision.next_sequence_number() return _import_sequences(request, issue_id, changeset, lines, running_number) else: return HttpResponseRedirect(urlresolvers.reverse('edit', kwargs={ 'id': changeset.id })) except (Issue.DoesNotExist, Issue.MultipleObjectsReturned, IssueRevision.DoesNotExist): return render_error(request, 'Could not find issue for id %s and changeset %s' \ % (issue_id, changeset_id))
def upload_image(request, model_name, id, image_type, image=None): from apps.oi.views import DISPLAY_CLASSES, REVISION_CLASSES """ Handles uploading of supporting images """ display_obj = get_object_or_404(DISPLAY_CLASSES[model_name], id=id, deleted=False) kwargs = None # replacement if image: if image.object != display_obj: return render_error(request, 'Image and object id do not match.', redirect=False, is_safe=True) kwargs = {'upload_type': 'replacement', 'current_image': image, 'current_image_tag': get_generic_image_tag(image, 'current image')} # check if there is an image if image_type.unique is set else: img_type = get_object_or_404(ImageType, name=image_type) if img_type.unique: if Image.objects.filter(content_type=\ ContentType.objects.get_for_model(display_obj), object_id=display_obj.id, type=img_type, deleted=False).count(): return render_error(request, ('%s has an image. Further images cannot be added, ' 'only replaced.') % (esc(display_obj)), redirect=False, is_safe=True) # check if there is a pending object deletion filter_dict = {model_name : display_obj, 'deleted' : True, 'changeset__state__in' : states.ACTIVE} revision = REVISION_CLASSES[model_name].objects.filter(**filter_dict) if revision: revision = revision.get() return render_error(request, ('%s is <a href="%s">pending deletion</a>. Images ' 'cannot be added or modified.') % (esc(display_obj), urlresolvers.reverse('compare', kwargs={'id': revision.changeset.id})), redirect=False, is_safe=True) # current request is an upload if request.method == 'POST': if image: return handle_uploaded_image(request, display_obj, model_name, image_type, current_image=image) else: return handle_uploaded_image(request, display_obj, model_name, image_type) # request is a GET for the form else: form = UploadImageForm() # display the form return _display_image_upload_form(request, form, display_obj, model_name, image_type, kwargs=kwargs)
def upload_variant(request, issue_id): """ Handles uploading of variant covers """ issue = get_object_or_404(Issue, id=issue_id) if issue.variant_of: return render_error( request, 'Variants can only be uploaded to the base issue.') # check if there is a pending issue deletion if IssueRevision.objects.filter(issue=issue, deleted=True, changeset__state__in=states.ACTIVE): revision = IssueRevision.objects.get( issue=issue, changeset__state__in=states.ACTIVE) return render_error( request, ('%s is <a href="%s">pending deletion</a>. Covers ' 'cannot be added or modified.') % (esc(issue), urlresolvers.reverse('compare', kwargs={'id': revision.changeset.id})), redirect=False, is_safe=True) # current request is an upload if request.method == 'POST': return handle_uploaded_cover(request, None, issue, variant=True) # request is a GET for the form else: if 'oi_file_source' in request.session: vars = { 'source': request.session['oi_file_source'], 'remember_source': True } else: vars = None form = UploadVariantScanForm(initial=vars) kwargs={'pending_variant_adds': Changeset.objects\ .filter(issuerevisions__variant_of=issue, #state__in=states.ACTIVE, state__in=[states.PENDING,states.REVIEWING], change_type__in=[CTYPES['issue_add'], CTYPES['variant_add']])} # display the form return _display_cover_upload_form(request, form, None, issue, variant=True, kwargs=kwargs)
def handle_uploaded_image(request, display_obj, model_name, image_type, current_image=None): ''' process the uploaded file and generate ImageRevision ''' try: form = UploadImageForm(request.POST, request.FILES) except IOError: # sometimes uploads misbehave. connection dropped ? error_text = 'Something went wrong with the upload. ' + \ 'Please <a href="' + request.path + '">try again</a>.' return render_error(request, error_text, redirect=False, is_safe=True) if not form.is_valid(): return _display_image_upload_form(request, form, display_obj, model_name, image_type) # process form image = form.cleaned_data['image'] if current_image: revision_lock = _get_revision_lock(current_image) if not revision_lock: return render_error( request, u'Cannot replace %s as it is already reserved.' % current_image.description()) # create OI records changeset = Changeset(indexer=request.user, state=states.OPEN, change_type=CTYPES['image']) changeset.save() if current_image: revision_lock.changeset = changeset revision_lock.save() revision = ImageRevision( changeset=changeset, content_type=ContentType.objects.get_for_model(display_obj), object_id=display_obj.id, type=ImageType.objects.get(name=image_type), marked=form.cleaned_data['marked']) if current_image: revision.image = current_image revision.is_replacement = True revision.previous_revision = current_image.revisions.get( next_revision=None, changeset__state=states.APPROVED, committed=True) revision.save() revision.image_file.save(str(revision.id) + '.jpg', content=File(image)) revision.changeset.submit(form.cleaned_data['comments']) return HttpResponseRedirect(urlresolvers.reverse('editing'))
def replace_image(request, model_name, id, image_id): image = get_object_or_404(Image, id=image_id, deleted=False) if image.reserved: return render_error(request, ('%s is reserved.') % (image.description()), redirect=False, is_safe=True) return upload_image(request, model_name, id, image.type.name, image=image)
def flip_artwork_flag(request, revision_id=None): """ flips the status in regard to variants with different cover artwork """ cover = get_object_or_404(CoverRevision, id=revision_id) changeset = cover.changeset if request.user != changeset.approver: return render_error(request, 'The variant artwork status may only be changed by the approver.') story = list(changeset.storyrevisions.all()) if len(story) == 1: for s in story: s.delete() elif len(story) == 0: story_revision = StoryRevision( changeset=changeset, type=StoryType.objects.get(name='cover'), pencils='?', inks='?', colors='?', sequence_number=0, page_count=2 if cover.is_wraparound else 1, ) story_revision.save() else: # this should never happen raise ValueError("More than one story sequence in a cover revision.") return HttpResponseRedirect(urlresolvers.reverse('compare', kwargs={'id': cover.changeset.id}))
def get_collection_for_owner(request, collection_id): collection = get_object_or_404(Collection, id=collection_id) if collection.collector.user != request.user: return None, render_error(request, 'Only the owner of a collection can add issues to it.', redirect=False) return collection, None
def get_collection_for_owner(request, collection_id): collection = get_object_or_404(Collection, id=collection_id) if collection.collector.user != request.user: return None, render_error(request, 'Only the owner of a collection can add issues to it.', redirect=False) return collection, None
def replace_image(request, model_name, id, image_id): image = get_object_or_404(Image, id=image_id, deleted=False) if is_locked(image): return render_error( request, ('%s is reserved.') % (image.description()), redirect=False, is_safe=True) return upload_image(request, model_name, id, image.type.name, image=image)
def process_response(self, request, response): if request.user.is_authenticated(): logout(request) return render_error(request, "Online editing is currently turned " "off, no user can login. We are working on the site. " "More information on the <a href='/'>main page</a>.", redirect=False, is_safe=True) return response
def unsubscribe_series(request, subscription_id): subscription = get_object_or_404(Subscription, id=subscription_id) if subscription.collection.collector.user != request.user: return render_error(request, 'Only the owner of a collection can unsubscribe series.', redirect=False) subscription.delete() return HttpResponseRedirect( urlresolvers.reverse('subscriptions_collection', kwargs={'collection_id': subscription.collection.id}))
def dummy(request, id=None, issue_id=None, model_name=None, image_type=None, image_id=None): return render_error(request, \ "Online editing and change history are currently turned off. " "We are working on the site. More information on the " "<a href='/'>main page</a>.", redirect=False, is_safe=True)
def process_response(self, request, response): if request.user.is_authenticated: logout(request) return render_error( request, "Online editing is currently turned " "off, no user can login. We are working on the site. " "More information on the <a href='/'>main page</a>.", redirect=False, is_safe=True) return response
def _handle_import_error(request, changeset, error_text): response = render_error(request, '%s Back to the <a href="%s">editing page</a>.' % \ (error_text, urlresolvers.reverse('edit', kwargs={'id': changeset.id})), is_safe=True) # there might be a temporary file attached if hasattr(request, "tmpfile"): request.tmpfile.close() os.remove(request.tmpfile_name) return response, True
def unsubscribe_series(request, subscription_id): subscription = get_object_or_404(Subscription, id=subscription_id) if subscription.collection.collector.user != request.user: return render_error( request, 'Only the owner of a collection can unsubscribe series.', redirect=False) subscription.delete() return HttpResponseRedirect( urlresolvers.reverse('subscriptions_collection', kwargs={'collection_id': subscription.collection.id}))
def _handle_import_error(request, changeset, error_text): response = render_error(request, '%s Back to the <a href="%s">editing page</a>.' % \ (error_text, urlresolvers.reverse('edit', kwargs={'id': changeset.id})), is_safe=True) # there might be a temporary file attached if hasattr(request, "tmpfile"): request.tmpfile.close() os.remove(request.tmpfile_name) return response, True
def upload_variant(request, issue_id): """ Handles uploading of variant covers """ issue = get_object_or_404(Issue, id=issue_id) if issue.variant_of: return render_error(request, 'Variants can only be uploaded to the base issue.') # check if there is a pending issue deletion if IssueRevision.objects.filter(issue=issue, deleted=True, changeset__state__in=states.ACTIVE): revision = IssueRevision.objects.get(issue=issue, changeset__state__in=states.ACTIVE) return render_error(request, ('%s is <a href="%s">pending deletion</a>. Covers ' 'cannot be added or modified.') % (esc(issue), urlresolvers.reverse('compare', kwargs={'id': revision.changeset.id})), redirect=False, is_safe=True) # current request is an upload if request.method == 'POST': return handle_uploaded_cover(request, None, issue, variant=True) # request is a GET for the form else: if 'oi_file_source' in request.session: vars = {'source' : request.session['oi_file_source'], 'remember_source' : True} else: vars = None form = UploadVariantScanForm(initial=vars) kwargs={'pending_variant_adds': Changeset.objects\ .filter(issuerevisions__variant_of=issue, #state__in=states.ACTIVE, state__in=[states.PENDING,states.REVIEWING], change_type__in=[CTYPES['issue_add'], CTYPES['variant_add']])} # display the form return _display_cover_upload_form(request, form, None, issue, variant=True, kwargs=kwargs)
def topic(request, id): topic = get_object_or_404(Topic, id=id) if not request.user.has_perm('indexer.can_vote'): return render_error(request, 'You do not have permission to vote on this topic.') # Note that if this was a secret ballot, this will be an empty # queryset. That is OK, the UI won't look at it in that case. # But this is why "voted" is not just a check for at least one vote here. votes = topic.options.filter(votes__voter=request.user) return render(request, 'voting/topic.html', {'topic': topic, 'voted': topic.has_vote_from(request.user), 'votes': votes, 'closed': topic.deadline < datetime.now() \ or topic.result_calculated, 'settings': settings})
def _cant_get_key(request): return render_error( request, 'Internal data for selecting objects is corrupted. If this message ' 'persists try logging out and logging in.', redirect=False)
def select_object(request, select_key): try: data = get_select_data(request, select_key) except KeyError: return _cant_get_key(request) if request.method == 'GET': if 'refine_search' in request.GET or 'search_issue' in request.GET: request_data = request.GET else: request_data = None initial = data.get('initial', {}) initial['select_key'] = select_key publisher = data.get('publisher', False) series = data.get('series', False) issue = data.get('issue', False) story = data.get('story', False) search_form, cache_form = get_select_forms(request, initial, request_data, publisher=publisher, series=series, issue=issue, story=story) haystack_form = FacetedSearchForm() return render( request, 'oi/edit/select_object.html', { 'heading': data['heading'], 'select_key': select_key, 'cache_form': cache_form, 'search_form': search_form, 'haystack_form': haystack_form, 'publisher': publisher, 'series': series, 'issue': issue, 'story': story, 'target': data['target'] }) if 'cancel' in request.POST: return data['cancel'] elif 'select_object' in request.POST: try: choice = request.POST['object_choice'] object_type, selected_id = choice.split('_') if object_type == 'cover': object_type = 'story' except MultiValueDictKeyError: return render_error(request, 'You did not select a cached object. ' 'Please use the back button to return.', redirect=False) elif 'search_select' in request.POST: choice = request.POST['object_choice'] object_type, selected_id = choice.split('_') elif 'entered_issue_id' in request.POST: object_type = 'issue' try: selected_id = int(request.POST['entered_issue_id']) except ValueError: return render_error(request, 'Entered Id must be an integer number. ' 'Please use the back button to return.', redirect=False) elif 'entered_story_id' in request.POST: object_type = 'story' try: selected_id = int(request.POST['entered_story_id']) except ValueError: return render_error(request, 'Entered Id must be an integer number. ' 'Please use the back button to return.', redirect=False) else: raise ValueError return data['return'](request, data, object_type, selected_id)
def upload_image(request, model_name, id, image_type, image=None): from apps.oi.views import DISPLAY_CLASSES, REVISION_CLASSES """ Handles uploading of supporting images """ display_obj = get_object_or_404(DISPLAY_CLASSES[model_name], id=id, deleted=False) kwargs = None # replacement if image: if image.object != display_obj: return render_error( request, 'Image and object id do not match.', redirect=False, is_safe=True) kwargs = { 'upload_type': 'replacement', 'current_image': image, 'current_image_tag': get_generic_image_tag(image, 'current image')} # check if there is an image if image_type.unique is set else: img_type = get_object_or_404(ImageType, name=image_type) if img_type.unique: if Image.objects.filter( content_type=ContentType.objects.get_for_model(display_obj), object_id=display_obj.id, type=img_type, deleted=False).count(): return render_error( request, ('%s has an image. Further images cannot be added, ' 'only replaced.') % (esc(display_obj)), redirect=False, is_safe=True) # check if there is a pending object deletion filter_dict = {model_name: display_obj, 'deleted': True, 'changeset__state__in': states.ACTIVE} revision = REVISION_CLASSES[model_name].objects.filter(**filter_dict) if revision: revision = revision.get() return render_error( request, ('%s is <a href="%s">pending deletion</a>. Images ' 'cannot be added or modified.') % ( esc(display_obj), urlresolvers.reverse( 'compare', kwargs={'id': revision.changeset.id})), redirect=False, is_safe=True) # current request is an upload if request.method == 'POST': if image: return handle_uploaded_image(request, display_obj, model_name, image_type, current_image=image) else: return handle_uploaded_image(request, display_obj, model_name, image_type) # request is a GET for the form else: form = UploadImageForm() # display the form return _display_image_upload_form(request, form, display_obj, model_name, image_type, kwargs=kwargs)
def import_issue_from_file(request, issue_id, changeset_id, use_csv = False): changeset = get_object_or_404(Changeset, id=changeset_id) if request.user != changeset.indexer: return render_error(request, 'Only the reservation holder may import issue data.') try: # Process add form if this is a POST. if request.method == 'POST' and 'flatfile' in request.FILES: issue_revision = changeset.issuerevisions.get(issue=issue_id) if StoryRevision.objects.filter(changeset=changeset).count(): return render_error(request, 'There are already sequences present for %s in this' ' changeset. Back to the <a href="%s">editing page</a>.'\ % (esc(issue_revision), urlresolvers.reverse('edit', kwargs={'id': changeset.id})), is_safe=True) if 'csv' in request.POST: use_csv = True else: use_csv = False lines, failure = _process_file(request, changeset, is_issue=True, use_csv=use_csv) if failure: return lines # parse the issue line issue_fields = lines.pop(0) issue_revision.number = issue_fields[NUMBER].strip() issue_revision.volume, issue_revision.no_volume = \ _parse_volume(issue_fields[VOLUME].strip()) indicia_publisher_name, issue_revision.indicia_pub_not_printed = \ _check_for_none(issue_fields[INDICIA_PUBLISHER]) indicia_publisher, failure = _find_publisher_object(request, changeset, indicia_publisher_name, IndiciaPublisher.objects.all(), "Indicia publisher", issue_revision.issue.series.publisher) if failure: return indicia_publisher else: issue_revision.indicia_publisher = indicia_publisher brand_name, issue_revision.no_brand = \ _check_for_none(issue_fields[BRAND]) brand, failure = _find_publisher_object(request, changeset, brand_name, Brand.objects.all(), "Brand", issue_revision.issue.series.publisher) if failure: return brand else: issue_revision.brand = brand issue_revision.publication_date = issue_fields[PUBLICATION_DATE]\ .strip() issue_revision.key_date = issue_fields[KEY_DATE].strip()\ .replace('.', '-') if issue_revision.key_date and \ not re.search(KEY_DATE_REGEXP, issue_revision.key_date): return render_error(request, "key_date '%s' is invalid." % issue_revision.key_date) issue_revision.indicia_frequency, \ issue_revision.no_indicia_frequency = \ _check_for_none(issue_fields[INDICIA_FREQUENCY]) issue_revision.price = issue_fields[PRICE].strip() issue_revision.page_count, issue_revision.page_count_uncertain =\ _check_page_count(issue_fields[ISSUE_PAGE_COUNT]) issue_revision.editing, issue_revision.no_editing = \ _check_for_none(issue_fields[ISSUE_EDITING]) issue_revision.isbn, issue_revision.no_isbn = \ _check_for_none(issue_fields[ISBN]) issue_revision.barcode, issue_revision.no_barcode = \ _check_for_none(issue_fields[BARCODE]) on_sale_date = issue_fields[ON_SALE_DATE].strip() if on_sale_date: if on_sale_date[-1] == '?': issue_revision.on_sale_date_uncertain = True on_sale_date = on_sale_date[:-1].strip() else: issue_revision.on_sale_date_uncertain = False try: # only full dates can be imported sale_date = datetime.strptime(on_sale_date, '%Y-%m-%d') issue_revision.year_on_sale = sale_date.year issue_revision.month_on_sale = sale_date.month issue_revision.day_on_sale = sale_date.day except ValueError: return render_error(request, "on-sale_date '%s' is invalid." % \ on_sale_date) issue_revision.notes = issue_fields[ISSUE_NOTES].strip() if issue_revision.series.has_issue_title: issue_revision.title, issue_revision.no_title = \ _check_for_none(issue_fields[ISSUE_TITLE]) issue_revision.keywords = issue_fields[ISSUE_KEYWORDS].strip() issue_revision.save() running_number = 0 return _import_sequences(request, issue_id, changeset, lines, running_number) else: return HttpResponseRedirect(urlresolvers.reverse('edit', kwargs={ 'id': changeset.id })) except (Issue.DoesNotExist, Issue.MultipleObjectsReturned, IssueRevision.DoesNotExist): return render_error(request, 'Could not find issue for id %s and changeset %s' \ % (issue_id, changeset_id))
def download(request): if request.method == 'POST': form = DownloadForm(request.POST) if form.is_valid(): cd = form.cleaned_data # Note that the submit input is never present in the cleaned data. file = settings.MYSQL_DUMP if ('postgres' in request.POST): file = settings.POSTGRES_DUMP path = os.path.join(settings.MEDIA_ROOT, settings.DUMP_DIR, file) delta = settings.DOWNLOAD_DELTA recently = datetime.now() - timedelta(minutes=delta) if Download.objects.filter(user=request.user, description__contains=file, timestamp__gt=recently).count(): return render_error( request, ("You have started a download of this file within the " "last %d minutes. Please check your download window. " "If you need to start a new download, please wait at " "least %d minutes in order to avoid consuming excess " "bandwidth.") % (delta, delta)) desc = {'file': file, 'accepted license': True} if 'purpose' in cd and cd['purpose']: desc['purpose'] = cd['purpose'] if 'usage' in cd and cd['usage']: desc['usage'] = cd['usage'] record = Download(user=request.user, description=repr(desc)) record.save() response = FileResponse(open(path, 'rb'), content_type='application/zip') response['Content-Disposition'] = \ 'attachment; filename=current.zip' return response else: form = DownloadForm() m_path = os.path.join(settings.MEDIA_ROOT, settings.DUMP_DIR, settings.MYSQL_DUMP) p_path = os.path.join(settings.MEDIA_ROOT, settings.DUMP_DIR, settings.POSTGRES_DUMP) # Use a list of tuples because we want the MySQL dump (our primary format) # to be first. timestamps = [] for dump_info in (('MySQL', m_path), ('PostgreSQL-compatible', p_path)): try: timestamps.append( (dump_info[0], datetime.utcfromtimestamp( os.stat(dump_info[1])[stat.ST_MTIME]))) except OSError, ose: if ose.errno == errno.ENOENT: timestamps.append((dump_info[0], 'never')) else: raise
def upload_cover(request, cover_id=None, issue_id=None): """ Handles uploading of covers be it - first upload - replacement upload - variant upload """ # this cannot actually happen if cover_id and issue_id: raise ValueError # if cover_id is present it is a replacement upload if cover_id: cover = get_object_or_404(Cover, id=cover_id) issue = cover.issue # no cover_id, therefore upload a cover to an issue (first or variant) else: issue = get_object_or_404(Issue, id=issue_id) if not issue.can_have_cover(): return render_error(request, 'No covers for non-comics publications if the issue has less' ' than 10% indexed comics content.', redirect=False) cover = None # check if there is a pending issue deletion if IssueRevision.objects.filter(issue=issue, deleted=True, changeset__state__in=states.ACTIVE): revision = IssueRevision.objects.get(issue=issue, changeset__state__in=states.ACTIVE) return render_error(request, ('%s is <a href="%s">pending deletion</a>. Covers ' 'cannot be added or modified.') % (esc(issue), urlresolvers.reverse('compare', kwargs={'id': revision.changeset.id})), redirect=False, is_safe=True) # check if there is a pending change for the cover # if POST, get a lock if cover_id and request.method == 'POST': revision_lock = _get_revision_lock(cover) if not revision_lock: return render_error( request, u'Cannot replace %s as it is already reserved.' % cover.issue) # if GET, check for a lock elif cover_id and is_locked(cover): return render_error( request, ('There currently is a pending replacement for this cover of %s.') % (cover.issue), redirect=False, is_safe=True) else: revision_lock = None # current request is an upload if request.method == 'POST': return handle_uploaded_cover(request, cover, issue, revision_lock=revision_lock) # request is a GET for the form else: if 'oi_file_source' in request.session: vars = {'source' : request.session['oi_file_source'], 'remember_source' : True} else: vars = None form = UploadScanForm(initial=vars) # display the form return _display_cover_upload_form(request, form, cover, issue)
def handle_uploaded_cover(request, cover, issue, variant=False, revision_lock=None): ''' process the uploaded file and generate CoverRevision ''' try: if variant: form = UploadVariantScanForm(request.POST, request.FILES) else: form = UploadScanForm(request.POST, request.FILES) except IOError: # sometimes uploads misbehave. connection dropped ? error_text = 'Something went wrong with the upload. ' + \ 'Please <a href="' + request.path + '">try again</a>.' return render_error(request, error_text, redirect=False, is_safe=True) if not form.is_valid(): return _display_cover_upload_form(request, form, cover, issue, variant=variant) # process form if form.cleaned_data['is_gatefold']: return handle_gatefold_cover(request, cover, issue, form) scan = form.cleaned_data['scan'] file_source = form.cleaned_data['source'] marked = form.cleaned_data['marked'] # create OI records changeset = Changeset(indexer=request.user, state=states.OPEN, change_type=CTYPES['cover']) changeset.save() if cover: # upload_type is 'replacement': revision = CoverRevision(changeset=changeset, issue=issue, cover=cover, file_source=file_source, marked=marked, is_replacement = True) revision_lock.changeset = changeset revision_lock.save() revision.previous_revision = cover.revisions.get( next_revision=None, changeset__state=states.APPROVED, committed=True) else: revision = CoverRevision(changeset=changeset, issue=issue, file_source=file_source, marked=marked) revision.save() # if uploading a variant, generate an issue_revision for # the variant issue and copy the values which would not change # TODO are these reasonable assumptions below ? if variant: current_variants = issue.variant_set.all().order_by('-sort_code') if current_variants: add_after = current_variants[0] else: add_after = issue issue_revision = IssueRevision(changeset=changeset, after=add_after, number=issue.number, title=issue.title, no_title=issue.no_title, volume=issue.volume, no_volume=issue.no_volume, display_volume_with_number=issue.display_volume_with_number, variant_of=issue, variant_name=form.cleaned_data['variant_name'], page_count=issue.page_count, page_count_uncertain=issue.page_count_uncertain, series=issue.series, editing=issue.editing, no_editing=issue.no_editing, reservation_requested=form.cleaned_data['reservation_requested'] ) issue_revision.save() if form.cleaned_data['variant_artwork']: story_revision = StoryRevision(changeset=changeset, type=StoryType.objects.get(name='cover'), no_script=True, pencils='?', inks='?', colors='?', no_letters=True, no_editing=True, sequence_number=0, page_count=2 if form.cleaned_data['is_wraparound'] else 1, ) story_revision.save() # put new uploaded covers into # media/<LOCAL_NEW_SCANS>/<monthname>_<year>/ # with name # <revision_id>_<date>_<time>.<ext> scan_name = str(revision.id) + os.path.splitext(scan.name)[1] upload_dir = settings.MEDIA_ROOT + LOCAL_NEW_SCANS + \ changeset.created.strftime('%B_%Y').lower() destination_name = os.path.join(upload_dir, scan_name) try: # essentially only needed at beginning of the month check_cover_dir(upload_dir) except IOError: changeset.delete() error_text = "Problem with file storage for uploaded " + \ "cover, please report an error." return render_error(request, error_text, redirect=False) # write uploaded file destination = open(destination_name, 'wb') for chunk in scan.chunks(): destination.write(chunk) destination.close() try: # generate different sizes we are using im = pyImage.open(destination.name) large_enough = False if form.cleaned_data['is_wraparound']: # wraparounds need to have twice the width if im.size[0] >= 800 and im.size[1] >= 400: large_enough = True elif min(im.size) >= 400: large_enough = True if large_enough: if form.cleaned_data['is_wraparound']: revision.is_wraparound = True revision.front_left = im.size[0]/2 revision.front_right = im.size[0] revision.front_bottom = im.size[1] revision.front_top = 0 revision.save() generate_sizes(revision, im) else: changeset.delete() os.remove(destination.name) info_text = "Image is too small, only " + str(im.size) + \ " in size." return _display_cover_upload_form(request, form, cover, issue, info_text=info_text, variant=variant) except IOError as e: # just in case, django *should* have taken care of file type changeset.delete() os.remove(destination.name) info_text = 'Error: File \"' + scan.name + \ '" is not a valid picture.' return _display_cover_upload_form(request, form, cover, issue, info_text=info_text, variant=variant) # all done, we can save the state return finish_cover_revision(request, revision, form.cleaned_data)
def process_edited_gatefold_cover(request): ''' process the edited gatefold cover and generate CoverRevision ''' if request.method != 'POST': return render_error(request, 'This page may only be accessed through the proper form', redirect=False) form = GatefoldScanForm(request.POST) if not form.is_valid(): error_text = 'Error: Something went wrong in the file upload.' return render_error(request, error_text, redirect=False) cd = form.cleaned_data tmpdir = settings.MEDIA_ROOT + LOCAL_NEW_SCANS + 'tmp' scan_name = cd['scan_name'] tmp_name = os.path.join(tmpdir, scan_name) if 'discard' in request.POST: os.remove(tmp_name) return HttpResponseRedirect(urlresolvers.reverse('edit_covers', kwargs={'issue_id': cd['issue_id']} )) # create OI records changeset = Changeset(indexer=request.user, state=states.OPEN, change_type=CTYPES['cover']) if cd['cover_id']: cover = get_object_or_404(Cover, id=cd['cover_id']) issue = cover.issue # check if there is a pending change for the cover revision_lock = _get_revision_lock(cover) if not revision_lock: return render_error( request, u'Cannot replace %s as it is already reserved.' % cover.issue) changeset.save() revision_lock.changeset = changeset revision_lock.save() revision = CoverRevision(changeset=changeset, issue=issue, cover=cover, file_source=cd['source'], marked=cd['marked'], is_replacement = True) # no cover_id, therefore upload a cover to an issue (first or variant) else: changeset.save() issue = get_object_or_404(Issue, id=cd['issue_id']) cover = None revision = CoverRevision(changeset=changeset, issue=issue, file_source=cd['source'], marked=cd['marked']) revision.save() scan_name = str(revision.id) + os.path.splitext(tmp_name)[1] upload_dir = settings.MEDIA_ROOT + LOCAL_NEW_SCANS + \ changeset.created.strftime('%B_%Y').lower() destination_name = os.path.join(upload_dir, scan_name) try: # essentially only needed at beginning of the month check_cover_dir(upload_dir) except IOError: changeset.delete() error_text = "Problem with file storage for uploaded " + \ "cover, please report an error." return render_error(request, error_text, redirect=False) shutil.move(tmp_name, destination_name) im = pyImage.open(destination_name) revision.is_wraparound = True # convert from scaled to real values width = cd['width'] height = cd['height'] left = cd['left'] top = cd['top'] revision.front_left = left revision.front_right = left + width revision.front_top = top revision.front_bottom = top + height revision.save() generate_sizes(revision, im) return finish_cover_revision(request, revision, cd)
def _cant_get_key(request): return render_error( request, 'Internal data for selecting objects is corrupted. If this message ' 'persists try logging out and logging in.', redirect=False)
def process_edited_gatefold_cover(request): ''' process the edited gatefold cover and generate CoverRevision ''' if request.method != 'POST': return render_error( request, 'This page may only be accessed through the proper form', redirect=False) form = GatefoldScanForm(request.POST) if not form.is_valid(): error_text = 'Error: Something went wrong in the file upload.' return render_error(request, error_text, redirect=False) cd = form.cleaned_data tmpdir = settings.MEDIA_ROOT + LOCAL_NEW_SCANS + 'tmp' scan_name = cd['scan_name'] tmp_name = os.path.join(tmpdir, scan_name) if 'discard' in request.POST: os.remove(tmp_name) return HttpResponseRedirect( urlresolvers.reverse('edit_covers', kwargs={'issue_id': cd['issue_id']})) # create OI records changeset = Changeset(indexer=request.user, state=states.OPEN, change_type=CTYPES['cover']) if cd['cover_id']: cover = get_object_or_404(Cover, id=cd['cover_id']) issue = cover.issue # check if there is a pending change for the cover revision_lock = _get_revision_lock(cover) if not revision_lock: return render_error( request, 'Cannot replace %s as it is already reserved.' % cover.issue) changeset.save() revision_lock.changeset = changeset revision_lock.save() revision = CoverRevision(changeset=changeset, issue=issue, cover=cover, file_source=cd['source'], marked=cd['marked'], is_replacement=True) # no cover_id, therefore upload a cover to an issue (first or variant) else: changeset.save() issue = get_object_or_404(Issue, id=cd['issue_id']) cover = None revision = CoverRevision(changeset=changeset, issue=issue, file_source=cd['source'], marked=cd['marked']) revision.save() scan_name = str(revision.id) + os.path.splitext(tmp_name)[1] upload_dir = settings.MEDIA_ROOT + LOCAL_NEW_SCANS + \ changeset.created.strftime('%B_%Y').lower() destination_name = os.path.join(upload_dir, scan_name) try: # essentially only needed at beginning of the month check_cover_dir(upload_dir) except IOError: changeset.delete() error_text = "Problem with file storage for uploaded " + \ "cover, please report an error." return render_error(request, error_text, redirect=False) shutil.move(tmp_name, destination_name) im = pyImage.open(destination_name) revision.is_wraparound = True # convert from scaled to real values width = cd['width'] height = cd['height'] left = cd['left'] top = cd['top'] revision.front_left = left revision.front_right = left + width revision.front_top = top revision.front_bottom = top + height revision.save() generate_sizes(revision, im) return finish_cover_revision(request, revision, cd)
def handle_uploaded_cover(request, cover, issue, variant=False, revision_lock=None): ''' process the uploaded file and generate CoverRevision ''' try: if variant: form = UploadVariantScanForm(request.POST, request.FILES) else: form = UploadScanForm(request.POST, request.FILES) except IOError: # sometimes uploads misbehave. connection dropped ? error_text = 'Something went wrong with the upload. ' + \ 'Please <a href="' + request.path + '">try again</a>.' return render_error(request, error_text, redirect=False, is_safe=True) if not form.is_valid(): return _display_cover_upload_form(request, form, cover, issue, variant=variant) # process form if form.cleaned_data['is_gatefold']: return handle_gatefold_cover(request, cover, issue, form) scan = form.cleaned_data['scan'] file_source = form.cleaned_data['source'] marked = form.cleaned_data['marked'] # create OI records changeset = Changeset(indexer=request.user, state=states.OPEN, change_type=CTYPES['cover']) changeset.save() if cover: # upload_type is 'replacement': revision = CoverRevision(changeset=changeset, issue=issue, cover=cover, file_source=file_source, marked=marked, is_replacement=True) revision_lock.changeset = changeset revision_lock.save() revision.previous_revision = cover.revisions.get( next_revision=None, changeset__state=states.APPROVED, committed=True) else: revision = CoverRevision(changeset=changeset, issue=issue, file_source=file_source, marked=marked) revision.save() # if uploading a variant, generate an issue_revision for # the variant issue and copy the values which would not change # TODO are these reasonable assumptions below ? if variant: current_variants = issue.variant_set.all().order_by('-sort_code') if current_variants: add_after = current_variants[0] else: add_after = issue issue_revision = IssueRevision( changeset=changeset, after=add_after, number=issue.number, title=issue.title, no_title=issue.no_title, volume=issue.volume, no_volume=issue.no_volume, display_volume_with_number=issue.display_volume_with_number, variant_of=issue, variant_name=form.cleaned_data['variant_name'], page_count=issue.page_count, page_count_uncertain=issue.page_count_uncertain, series=issue.series, editing=issue.editing, no_editing=issue.no_editing, reservation_requested=form.cleaned_data['reservation_requested']) issue_revision.save() if form.cleaned_data['variant_artwork']: story_revision = StoryRevision( changeset=changeset, type=StoryType.objects.get(name='cover'), no_script=True, pencils='?', inks='?', colors='?', no_letters=True, no_editing=True, sequence_number=0, page_count=2 if form.cleaned_data['is_wraparound'] else 1, ) story_revision.save() # put new uploaded covers into # media/<LOCAL_NEW_SCANS>/<monthname>_<year>/ # with name # <revision_id>_<date>_<time>.<ext> scan_name = str(revision.id) + os.path.splitext(scan.name)[1] upload_dir = settings.MEDIA_ROOT + LOCAL_NEW_SCANS + \ changeset.created.strftime('%B_%Y').lower() destination_name = os.path.join(upload_dir, scan_name) try: # essentially only needed at beginning of the month check_cover_dir(upload_dir) except IOError: changeset.delete() error_text = "Problem with file storage for uploaded " + \ "cover, please report an error." return render_error(request, error_text, redirect=False) # write uploaded file destination = open(destination_name, 'wb') for chunk in scan.chunks(): destination.write(chunk) destination.close() try: # generate different sizes we are using im = pyImage.open(destination.name) large_enough = False if form.cleaned_data['is_wraparound']: # wraparounds need to have twice the width if im.size[0] >= 800 and im.size[1] >= 400: large_enough = True elif min(im.size) >= 400: large_enough = True if large_enough: if form.cleaned_data['is_wraparound']: revision.is_wraparound = True revision.front_left = im.size[0] / 2 revision.front_right = im.size[0] revision.front_bottom = im.size[1] revision.front_top = 0 revision.save() generate_sizes(revision, im) else: changeset.delete() os.remove(destination.name) info_text = "Image is too small, only " + str(im.size) + \ " in size." return _display_cover_upload_form(request, form, cover, issue, info_text=info_text, variant=variant) except IOError as e: # just in case, django *should* have taken care of file type changeset.delete() os.remove(destination.name) info_text = 'Error: File \"' + scan.name + \ '" is not a valid picture.' return _display_cover_upload_form(request, form, cover, issue, info_text=info_text, variant=variant) # all done, we can save the state return finish_cover_revision(request, revision, form.cleaned_data)
def vote(request): if request.method != 'POST': return render_error(request, 'Please access this page through the correct form.') first = True topic = None voter = None option_params = request.POST.getlist('option') ranks = {} if not option_params: # ranked_choice, collect all ranks values = request.POST.keys() for value in values: if value.startswith('option'): if request.POST[value]: try: ranks[int(value[7:])] = int(request.POST[value]) except ValueError: return render_error(request, 'You must enter a full number for the ranking.') else: ranks[int(value[7:])] = None options = Option.objects.filter(id__in=ranks.keys()) else: options = Option.objects.filter(id__in=option_params) # Get all of these now because we may need to iterate twice. options = options.select_related('topic__agenda', 'topic__vote_type').all() if not options: return render_error(request, 'You must choose at least one option to vote.') for option in options: if first is True: first = False topic = option.topic if not request.user.has_perm('%s.%s' % (topic.agenda.permission.content_type.app_label, topic.agenda.permission.codename)): return render_error(request, 'We are sorry, but you do not have permission to vote ' 'on this ballot. You must have the "%s" permission.' % topic.agenda.permission.name) if topic.token is not None and \ request.POST['token'].strip() != topic.token: return render_error(request, 'You must supply an authorization token in order ' 'to vote on this topic.') if topic.vote_type.max_votes is not None and \ len(option_params) > topic.vote_type.max_votes: plural = 's' if len(option_params) > 1 else '' return render_error(request, 'You may not vote for more than %d option%s' % (topic.vote_type.max_votes, plural)) if topic.agenda.secret_ballot: receipts = Receipt.objects.filter(topic=topic, voter=request.user) already_voted = receipts.count() > 0 else: voter = request.user already_voted = Vote.objects.filter(option__topic=topic, voter=request.user).count() > 0 if already_voted: return render_error(request, 'You have already voted on this ballot. If you wish to change ' 'your vote, please contact the voting administrator at ' + settings.EMAIL_VOTING_ADMIN) if not option_params: # for ranked choice options can be empty, set these last in ranking if option.id in ranks: rank = ranks[option.id] if not rank: rank = max(ranks.values()) + 1 else: rank = None vote = Vote(option=option, voter=voter, rank=rank) vote.save() if topic.agenda.secret_ballot: # We email the salt and the vote string to the voter, and store # the receipt key in the database. This way they can prove that # they voted a particular way by sending us back those two values, # and repair or change a vote. vote_ids = ', '.join(option_params) salt = hashlib.sha1(str(random())).hexdigest()[:5] key = hashlib.sha1(salt + vote_ids).hexdigest() receipt = Receipt(voter=request.user, topic=option.topic, vote_key=key) receipt.save() # Log the data as a backup in case of mail sending/receiving problems. # Technically, this makes it not a secret ballot, but it takes some # work to interpret the log, and there's always been a human counting # the ballots before, so this allows for at least as much secrecy # as before. # Use unbuffered appends because we don't want concurrent writes to get # spliced due to buffering. path = os.path.join(settings.VOTING_DIR, 'vote_record_%d' % topic.id) voting_record = open(path, 'a', 0) voting_record.write('%d | %s | %s | %s\n' % (request.user.id, salt, vote_ids, key)) voting_record.close() vote_values = "\n".join([unicode(o) for o in options]) send_mail(from_email=settings.EMAIL_VOTING_FROM, recipient_list=[request.user.email], subject="GCD secret ballot receipt", message=EMAIL_RECEIPT % (topic, vote_values, settings.EMAIL_VOTING_ADMIN, salt, vote_ids), fail_silently=(not settings.BETA)) return HttpResponseRedirect(urlresolvers.reverse('ballot', kwargs={ 'id': option.topic.id }))
def upload_cover(request, cover_id=None, issue_id=None): """ Handles uploading of covers be it - first upload - replacement upload - variant upload """ # this cannot actually happen if cover_id and issue_id: raise ValueError # if cover_id is present it is a replacement upload if cover_id: cover = get_object_or_404(Cover, id=cover_id) issue = cover.issue # no cover_id, therefore upload a cover to an issue (first or variant) else: issue = get_object_or_404(Issue, id=issue_id) if not issue.can_have_cover(): return render_error( request, 'No covers for non-comics publications if the issue has less' ' than 10% indexed comics content.', redirect=False) cover = None # check if there is a pending issue deletion if IssueRevision.objects.filter(issue=issue, deleted=True, changeset__state__in=states.ACTIVE): revision = IssueRevision.objects.get( issue=issue, changeset__state__in=states.ACTIVE) return render_error( request, ('%s is <a href="%s">pending deletion</a>. Covers ' 'cannot be added or modified.') % (esc(issue), urlresolvers.reverse('compare', kwargs={'id': revision.changeset.id})), redirect=False, is_safe=True) # check if there is a pending change for the cover # if POST, get a lock if cover_id and request.method == 'POST': revision_lock = _get_revision_lock(cover) if not revision_lock: return render_error( request, 'Cannot replace %s as it is already reserved.' % cover.issue) # if GET, check for a lock elif cover_id and is_locked(cover): return render_error( request, ('There currently is a pending replacement for this cover of %s.') % (cover.issue), redirect=False, is_safe=True) else: revision_lock = None # current request is an upload if request.method == 'POST': return handle_uploaded_cover(request, cover, issue, revision_lock=revision_lock) # request is a GET for the form else: if 'oi_file_source' in request.session: vars = { 'source': request.session['oi_file_source'], 'remember_source': True } else: vars = None form = UploadScanForm(initial=vars) # display the form return _display_cover_upload_form(request, form, cover, issue)
def select_object(request, select_key): try: data = get_select_data(request, select_key) except KeyError: return _cant_get_key(request) if request.method == 'GET': if 'refine_search' in request.GET or 'search_issue' in request.GET: request_data = request.GET else: request_data = None initial = data.get('initial', {}) initial['select_key'] = select_key publisher = data.get('publisher', False) series = data.get('series', False) issue = data.get('issue', False) story = data.get('story', False) search_form, cache_form = get_select_forms(request, initial, request_data, publisher=publisher, series=series, issue=issue, story=story) haystack_form = FacetedSearchForm() return render(request, 'oi/edit/select_object.html', {'heading': data['heading'], 'select_key': select_key, 'cache_form': cache_form, 'search_form': search_form, 'haystack_form': haystack_form, 'publisher': publisher, 'series': series, 'issue': issue, 'story': story, 'target': data['target'] }) if 'cancel' in request.POST: return data['cancel'] elif 'select_object' in request.POST: try: choice = request.POST['object_choice'] object_type, selected_id = choice.split('_') if object_type == 'cover': object_type = 'story' except MultiValueDictKeyError: return render_error(request, 'You did not select a cached object. ' 'Please use the back button to return.', redirect=False) elif 'search_select' in request.POST: choice = request.POST['object_choice'] object_type, selected_id = choice.split('_') elif 'entered_issue_id' in request.POST: object_type = 'issue' try: selected_id = int(request.POST['entered_issue_id']) except ValueError: return render_error(request, 'Entered Id must be an integer number. ' 'Please use the back button to return.', redirect=False) elif 'entered_story_id' in request.POST: object_type = 'story' try: selected_id = int(request.POST['entered_story_id']) except ValueError: return render_error(request, 'Entered Id must be an integer number. ' 'Please use the back button to return.', redirect=False) else: raise ValueError return data['return'](request, data, object_type, selected_id)