def post(self, request, format=None): """ Create new link. """ data = request.data capture_job = CaptureJob( human=request.data.get('human', False), submitted_url=request.data.get('url', ''), created_by=request.user ) if settings.ENABLE_BATCH_LINKS: # Batch is set directly on the request object by the LinkBatch api, # to prevent abuse of this feature by those POSTing directly to this route. if getattr(request, 'batch', None): capture_job.link_batch = LinkBatch.objects.get(id=request.batch) capture_job.save() # Set target folder, in order of preference: # - 'folder' key in data # - parent folder, if posting to /folders/:parent_id/archives # - user's personal folder try: folder = self.get_folder_from_request(request) or request.parent or request.user.root_folder except ValidationError as e: raise_invalid_capture_job(capture_job, e.detail) # Disallow creation of links in top-level sponsored folder if folder.is_sponsored_root_folder: error = "You can't make links directly in your Sponsored Links folder. Select a folder belonging to a sponsor." raise_invalid_capture_job(capture_job, error) # Make sure a limited user has links left to create if not folder.organization and not folder.sponsored_by: if not request.user.link_creation_allowed(): if request.user.nonpaying: raise_invalid_capture_job(capture_job, "You've already reached your limit.") error = "Perma.cc cannot presently make additional Perma Links on your behalf. Visit your subscription settings page for more information." raise_invalid_capture_job(capture_job, error) else: registrar = folder.sponsored_by if folder.sponsored_by else folder.organization.registrar msg = None if folder.read_only: registrar_users = [user.email for user in registrar.active_registrar_users()] msg = f"Your registrar has made this folder read-only. For assistance, contact: {', '.join(registrar_users)}." if not registrar.link_creation_allowed(): error = 'Perma.cc cannot presently make links on behalf of {}. '.format(registrar.name) if request.user.registrar: contact = 'Visit your settings for subscription information.' else: registrar_users = [user.email for user in registrar.active_registrar_users()] contact = 'For assistance with your subscription, contact: {}.'.format(", ".join(registrar_users)) msg = error + contact if msg: raise_invalid_capture_job(capture_job, msg) serializer = self.serializer_class(data=data, context={'request': request}) if serializer.is_valid(): with transaction.atomic(): # Technique from https://github.com/harvard-lil/capstone/blob/0f7fb80f26e753e36e0c7a6a199b8fdccdd318be/capstone/capapi/serializers.py#L121 # # Fetch the current user data here inside a transaction, using select_for_update # to lock the row so we don't collide with any simultaneous requests user = request.user.__class__.objects.select_for_update().get(pk=request.user.pk) # If this is a Personal Link, and if the user only has bonus links left, decrement bonus links bonus_link = False if not folder.organization and not folder.sponsored_by: links_remaining, _ , bonus_links = user.get_links_remaining() if bonus_links and not links_remaining: # (this works because it's part of the same transaction with the select_for_update -- # we don't have to use the same object) request.user.bonus_links = bonus_links - 1 request.user.save(update_fields=['bonus_links']) bonus_link = True link = serializer.save(created_by=request.user, bonus_link=bonus_link) # put link in folder and handle Org settings based on folder if folder.organization and folder.organization.default_to_private: link.is_private = True link.save() link.move_to_folder_for_user(folder, request.user) # also sets link.organization # handle uploaded file uploaded_file = request.data.get('file') if uploaded_file: link.write_uploaded_file(uploaded_file) # handle submitted url else: # create primary capture placeholder Capture( link=link, role='primary', status='pending', record_type='response', url=link.submitted_url, ).save() # create screenshot placeholder Capture( link=link, role='screenshot', status='pending', record_type='resource', url="file:///%s/cap.png" % link.guid, content_type='image/png', ).save() # kick off capture tasks -- no need for guid since it'll work through the queue capture_job.status = 'pending' capture_job.link = link capture_job.save(update_fields=['status', 'link']) run_task(run_next_capture.s()) return Response(serializer.data, status=status.HTTP_201_CREATED) raise_invalid_capture_job(capture_job, serializer.errors)
def post(self, request, format=None): """ Create new link. """ data = request.data capture_job = CaptureJob(human=request.data.get('human', False), submitted_url=request.data.get('url', ''), created_by=request.user) if settings.ENABLE_BATCH_LINKS: # Batch is set directly on the request object by the LinkBatch api, # to prevent abuse of this feature by those POSTing directly to this route. if getattr(request, 'batch', None): capture_job.link_batch = LinkBatch.objects.get( id=request.batch) capture_job.save() # Set target folder, in order of preference: # - 'folder' key in data # - parent folder, if posting to /folders/:parent_id/archives # - user's personal folder try: folder = self.get_folder_from_request( request) or request.parent or request.user.root_folder except ValidationError as e: raise_invalid_capture_job(capture_job, e.detail) # Make sure a limited user has links left to create if not folder.organization: if not request.user.link_creation_allowed(): if request.user.nonpaying: raise_invalid_capture_job( capture_job, "You've already reached your limit.") error = "Perma.cc cannot presently make additional Perma Links on your behalf. Visit your subscription settings page for more information." raise_invalid_capture_job(capture_job, error) else: registrar = folder.organization.registrar if not registrar.link_creation_allowed(): error = 'Perma.cc cannot presently make links on behalf of {}. '.format( registrar.name) if request.user.registrar: contact = 'Visit your settings for subscription information.' else: registrar_users = [ user.email for user in registrar.active_registrar_users() ] contact = 'For assistance with your subscription, contact: {}.'.format( ", ".join(registrar_users)) raise_invalid_capture_job(capture_job, error + contact) serializer = self.serializer_class(data=data, context={'request': request}) if serializer.is_valid(): link = serializer.save(created_by=request.user) # put link in folder and handle Org settings based on folder if folder.organization and folder.organization.default_to_private: link.is_private = True link.save() link.move_to_folder_for_user( folder, request.user) # also sets link.organization # handle uploaded file uploaded_file = request.data.get('file') if uploaded_file: link.write_uploaded_file(uploaded_file) link.warc_size = default_storage.size(link.warc_storage_file()) link.save() # handle submitted url else: # create primary capture placeholder Capture( link=link, role='primary', status='pending', record_type='response', url=link.submitted_url, ).save() # create screenshot placeholder Capture( link=link, role='screenshot', status='pending', record_type='resource', url="file:///%s/cap.png" % link.guid, content_type='image/png', ).save() # kick off capture tasks -- no need for guid since it'll work through the queue capture_job.status = 'pending' capture_job.link = link capture_job.save(update_fields=['status', 'link']) run_task(run_next_capture.s()) return Response(serializer.data, status=status.HTTP_201_CREATED) raise_invalid_capture_job(capture_job, serializer.errors)
def post(self, request, format=None): """ Create new link. """ data = request.data capture_job = CaptureJob( human=request.data.get('human', False), submitted_url=request.data.get('url', ''), created_by=request.user ) if settings.ENABLE_BATCH_LINKS: # Batch is set directly on the request object by the LinkBatch api, # to prevent abuse of this feature by those POSTing directly to this route. if getattr(request, 'batch', None): capture_job.link_batch = LinkBatch.objects.get(id=request.batch) capture_job.save() # Set target folder, in order of preference: # - 'folder' key in data # - parent folder, if posting to /folders/:parent_id/archives # - user's personal folder try: folder = self.get_folder_from_request(request) or request.parent or request.user.root_folder except ValidationError as e: raise_invalid_capture_job(capture_job, e.detail) # Make sure a limited user has links left to create if not folder.organization: if not request.user.link_creation_allowed(): if request.user.nonpaying: raise_invalid_capture_job(capture_job, "You've already reached your limit.") error = "Perma.cc cannot presently make additional Perma Links on your behalf. Visit your subscription settings page for more information." raise_invalid_capture_job(capture_job, error) else: registrar = folder.organization.registrar if not registrar.link_creation_allowed(): error = 'Perma.cc cannot presently make links on behalf of {}. '.format(registrar.name) if request.user.registrar: contact = 'Visit your settings for subscription information.' else: registrar_users = [user.email for user in registrar.active_registrar_users()] contact = 'For assistance with your subscription, contact: {}.'.format(", ".join(registrar_users)) raise_invalid_capture_job(capture_job, error + contact) serializer = self.serializer_class(data=data, context={'request': request}) if serializer.is_valid(): link = serializer.save(created_by=request.user) # put link in folder and handle Org settings based on folder if folder.organization and folder.organization.default_to_private: link.is_private = True link.save() link.move_to_folder_for_user(folder, request.user) # also sets link.organization # handle uploaded file uploaded_file = request.data.get('file') if uploaded_file: link.write_uploaded_file(uploaded_file) # handle submitted url else: # create primary capture placeholder Capture( link=link, role='primary', status='pending', record_type='response', url=link.submitted_url, ).save() # create screenshot placeholder Capture( link=link, role='screenshot', status='pending', record_type='resource', url="file:///%s/cap.png" % link.guid, content_type='image/png', ).save() # kick off capture tasks -- no need for guid since it'll work through the queue capture_job.status = 'pending' capture_job.link = link capture_job.save(update_fields=['status', 'link']) run_task(run_next_capture.s()) return Response(serializer.data, status=status.HTTP_201_CREATED) raise_invalid_capture_job(capture_job, serializer.errors)