diff --git a/README-docker-compose.md b/README-docker-compose.md index caf025d005f6..98f9da23e947 100644 --- a/README-docker-compose.md +++ b/README-docker-compose.md @@ -158,7 +158,7 @@ - Needed for api v2 citation style rendering. - `docker-compose run --rm web python3 -m scripts.parse_citation_styles` - Start ember_osf_web - - Needed for quickfiles feature: + - Needed for ember front-end: - `docker-compose up -d ember_osf_web` - OPTIONAL: Register OAuth Scopes - Needed for things such as the ember-osf dummy app diff --git a/addons/base/views.py b/addons/base/views.py index 2488ad91ee99..effab2b8c8d8 100644 --- a/addons/base/views.py +++ b/addons/base/views.py @@ -22,7 +22,6 @@ from api.caching.tasks import update_storage_usage_with_size from addons.base.models import BaseStorageAddon -from addons.osfstorage.models import OsfStorageFile from addons.osfstorage.models import OsfStorageFileNode from addons.osfstorage.utils import update_analytics @@ -33,7 +32,7 @@ from framework.auth.decorators import collect_auth, must_be_logged_in, must_be_signed from framework.exceptions import HTTPError from framework.sentry import log_exception -from framework.routing import json_renderer, proxy_url +from framework.routing import json_renderer from framework.transactions.handlers import no_auto_transaction from website import mails from website import settings @@ -403,7 +402,7 @@ def get_auth(auth, **kwargs): @must_be_signed @no_auto_transaction -@must_be_valid_project(quickfiles_valid=True, preprints_valid=True) +@must_be_valid_project(preprints_valid=True) def create_waterbutler_log(payload, **kwargs): with transaction.atomic(): try: @@ -523,7 +522,7 @@ def create_waterbutler_log(payload, **kwargs): metadata = payload.get('metadata') or payload.get('destination') target_node = AbstractNode.load(metadata.get('nid')) - if target_node and not target_node.is_quickfiles and payload['action'] != 'download_file': + if target_node and payload['action'] != 'download_file': update_storage_usage_with_size(payload) with transaction.atomic(): @@ -847,16 +846,6 @@ def persistent_file_download(auth, **kwargs): ) -def addon_view_or_download_quickfile(**kwargs): - fid = kwargs.get('fid', 'NOT_AN_FID') - file_ = OsfStorageFile.load(fid) - if not file_: - raise HTTPError(http_status.HTTP_404_NOT_FOUND, data={ - 'message_short': 'File Not Found', - 'message_long': 'The requested file could not be found.' - }) - return proxy_url('/project/{}/files/osfstorage/{}/'.format(file_.target._id, fid)) - def addon_view_file(auth, node, file_node, version): # TODO: resolve circular import issue from addons.wiki import settings as wiki_settings diff --git a/addons/osfstorage/tests/test_views.py b/addons/osfstorage/tests/test_views.py index 65bc74b5feca..51d8d428d254 100644 --- a/addons/osfstorage/tests/test_views.py +++ b/addons/osfstorage/tests/test_views.py @@ -26,7 +26,7 @@ from framework.auth import cas from osf import features -from osf.models import Tag, QuickFilesNode +from osf.models import Tag from osf.models import files as models from addons.osfstorage.apps import osf_storage_root from addons.osfstorage import utils diff --git a/addons/osfstorage/views.py b/addons/osfstorage/views.py index 467f25101042..578fc62b9825 100644 --- a/addons/osfstorage/views.py +++ b/addons/osfstorage/views.py @@ -314,9 +314,6 @@ def osfstorage_create_child(file_node, payload, **kwargs): if not (name or user) or '/' in name: raise HTTPError(http_status.HTTP_400_BAD_REQUEST) - if getattr(file_node.target, 'is_quickfiles', False) and is_folder: - raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={'message_long': 'You may not create a folder for QuickFiles'}) - try: # Create a save point so that we can rollback and unlock # the parent record diff --git a/api/caching/tasks.py b/api/caching/tasks.py index f03ded8252b0..23ed0c40d002 100644 --- a/api/caching/tasks.py +++ b/api/caching/tasks.py @@ -138,7 +138,7 @@ def update_storage_usage_cache(target_id, target_guid, per_page=500000): def update_storage_usage(target): Preprint = apps.get_model('osf.preprint') - if settings.ENABLE_STORAGE_USAGE_CACHE and not isinstance(target, Preprint) and not target.is_quickfiles: + if settings.ENABLE_STORAGE_USAGE_CACHE and not isinstance(target, Preprint): enqueue_postcommit_task(update_storage_usage_cache, (target.id, target._id,), {}, celery=True) def update_storage_usage_with_size(payload): @@ -151,9 +151,6 @@ def update_storage_usage_with_size(payload): return target_node = AbstractNode.load(metadata['nid']) - if target_node.is_quickfiles: - return - action = payload['action'] provider = metadata.get('provider', 'osfstorage') @@ -182,7 +179,7 @@ def update_storage_usage_with_size(payload): source_provider = payload['source']['provider'] if target_node == source_node and source_provider == provider: return # Its not going anywhere. - if source_provider == 'osfstorage' and not source_node.is_quickfiles: + if source_provider == 'osfstorage': if source_node.storage_limit_status is settings.StorageLimits.NOT_CALCULATED: return update_storage_usage(source_node) diff --git a/api/files/serializers.py b/api/files/serializers.py index 9c1eaa7e2c9e..b3e8423c1bd2 100644 --- a/api/files/serializers.py +++ b/api/files/serializers.py @@ -417,18 +417,6 @@ class FileDetailSerializer(FileSerializer): id = IDField(source='_id', required=True) -class QuickFilesSerializer(BaseFileSerializer): - user = RelationshipField( - related_view='users:user-detail', - related_view_kwargs={'user_id': ''}, - help_text='The user who uploaded this file', - ) - - -class QuickFilesDetailSerializer(QuickFilesSerializer): - id = IDField(source='_id', required=True) - - class FileVersionSerializer(JSONAPISerializer): filterable_fields = frozenset([ 'id', diff --git a/api/files/views.py b/api/files/views.py index da076e216af8..122e605f093a 100644 --- a/api/files/views.py +++ b/api/files/views.py @@ -12,7 +12,6 @@ Guid, BaseFileNode, FileVersion, - QuickFilesNode, ) from api.base.exceptions import Gone @@ -27,7 +26,7 @@ from api.files.permissions import CheckedOutOrAdmin from api.files.permissions import FileMetadataRecordPermission from api.files.serializers import FileSerializer -from api.files.serializers import FileDetailSerializer, QuickFilesDetailSerializer +from api.files.serializers import FileDetailSerializer from api.files.serializers import FileMetadataRecordSerializer from api.files.serializers import FileVersionSerializer from osf.utils.permissions import ADMIN @@ -54,10 +53,6 @@ def get_file(self, check_permissions=True): if getattr(obj.target, 'deleted', None): raise Gone(detail='The requested file is no longer available') - if getattr(obj.target, 'is_quickfiles', False) and getattr(obj.target, 'creator'): - if obj.target.creator.is_disabled: - raise Gone(detail='This user has been deactivated and their quickfiles are no longer available.') - if check_permissions: # May raise a permission denied self.check_object_permissions(self.request, obj) @@ -86,12 +81,10 @@ class FileDetail(JSONAPIBaseView, generics.RetrieveUpdateAPIView, FileMixin): def get_serializer_class(self): try: - target = self.get_target() + self.get_target() except (NotFound, Gone, PermissionDenied): return FileDetailSerializer else: - if isinstance(target, QuickFilesNode): - return QuickFilesDetailSerializer return FileDetailSerializer def get_target(self): @@ -103,8 +96,8 @@ def get_object(self): file = self.get_file() if self.request.GET.get('create_guid', False): - # allows quickfiles to be given guids when another user wants a permanent link to it - if (self.get_target().has_permission(user, ADMIN) and utils.has_admin_scope(self.request)) or getattr(file.target, 'is_quickfiles', False): + # allows files to be given guids when another user wants a permanent link to it + if self.get_target().has_permission(user, ADMIN) and utils.has_admin_scope(self.request): file.get_guid(create=True) return file diff --git a/api/users/serializers.py b/api/users/serializers.py index 3a74866a0342..21f0d996b734 100644 --- a/api/users/serializers.py +++ b/api/users/serializers.py @@ -13,22 +13,19 @@ VersionedDateTimeField, HideIfDisabled, IDField, - Link, LinksField, TypeField, RelationshipField, JSONAPIListField, - WaterbutlerLink, ShowIfCurrentUser, ) from api.base.utils import default_node_list_queryset from osf.models import Registration, Node -from api.base.utils import absolute_reverse, get_user_auth, waterbutler_api_url_for, is_deprecated, hashids -from api.files.serializers import QuickFilesSerializer +from api.base.utils import absolute_reverse, get_user_auth, is_deprecated, hashids from osf.models import Email from osf.exceptions import ValidationValueError, ValidationError, BlockedEmailError -from osf.models import OSFUser, QuickFilesNode, Preprint, AbstractNode +from osf.models import OSFUser, Preprint from osf.utils.requests import string_type_request_headers from website.settings import MAILCHIMP_GENERAL_LIST, OSF_HELP_LIST, CONFIRM_REGISTRATIONS_BY_EMAIL from osf.models.provider import AbstractProviderGroupObjectPermission @@ -39,31 +36,6 @@ from api.base.versioning import get_kebab_snake_case_field -class QuickFilesRelationshipField(RelationshipField): - - def to_representation(self, value): - relationship_links = super().to_representation(value) - try: - quickfiles_guid = value.nodes_created.get( - type=QuickFilesNode._typedmodels_type, - ).values_list( - 'guids___id', flat=True - ).get() - except AbstractNode.DoesNotExist: - return relationship_links - - upload_url = waterbutler_api_url_for(quickfiles_guid, 'osfstorage') - relationship_links['links']['upload'] = { - 'href': upload_url, - 'meta': {}, - } - relationship_links['links']['download'] = { - 'href': '{}?zip='.format(upload_url), - 'meta': {}, - } - return relationship_links - - class SocialField(ser.DictField): def __init__(self, min_version, **kwargs): super(SocialField, self).__init__(**kwargs) @@ -139,12 +111,6 @@ class UserSerializer(JSONAPISerializer): related_view_kwargs={'user_id': '<_id>'}, )) - quickfiles = HideIfDisabled(QuickFilesRelationshipField( - related_view='users:user-quickfiles', - related_view_kwargs={'user_id': '<_id>'}, - related_meta={'count': 'get_quickfiles_count'}, - )) - registrations = HideIfDisabled(RelationshipField( related_view='users:user-registrations', related_view_kwargs={'user_id': '<_id>'}, @@ -211,9 +177,6 @@ def get_node_count(self, obj): return Node.objects.get_nodes_for_user(auth.user, base_queryset=default_queryset, include_public=True).count() return default_queryset.count() - def get_quickfiles_count(self, obj): - return QuickFilesNode.objects.get(contributor__user__id=obj.id).files.filter(type='osf.osfstoragefile').count() - def get_registration_count(self, obj): auth = get_user_auth(self.context['request']) user_registration = default_node_list_queryset(model_cls=Registration).filter(contributor__user__id=obj.id) @@ -346,16 +309,6 @@ class UserDetailSerializer(UserSerializer): id = IDField(source='_id', required=True) -class UserQuickFilesSerializer(QuickFilesSerializer): - links = LinksField({ - 'info': Link('files:file-detail', kwargs={'file_id': '<_id>'}), - 'upload': WaterbutlerLink(), - 'delete': WaterbutlerLink(), - 'move': WaterbutlerLink(), - 'download': WaterbutlerLink(must_be_file=True), - }) - - class ReadEmailUserDetailSerializer(UserDetailSerializer): email = ser.CharField(source='username', read_only=True) diff --git a/api/users/urls.py b/api/users/urls.py index 009baf4927e0..98360467dd5c 100644 --- a/api/users/urls.py +++ b/api/users/urls.py @@ -18,7 +18,6 @@ url(r'^(?P\w+)/preprints/$', views.UserPreprints.as_view(), name=views.UserPreprints.view_name), url(r'^(?P\w+)/registrations/$', views.UserRegistrations.as_view(), name=views.UserRegistrations.view_name), url(r'^(?P\w+)/settings/$', views.UserSettings.as_view(), name=views.UserSettings.view_name), - url(r'^(?P\w+)/quickfiles/$', views.UserQuickFiles.as_view(), name=views.UserQuickFiles.view_name), url(r'^(?P\w+)/relationships/institutions/$', views.UserInstitutionsRelationship.as_view(), name=views.UserInstitutionsRelationship.view_name), url(r'^(?P\w+)/settings/emails/$', views.UserEmailsList.as_view(), name=views.UserEmailsList.view_name), url(r'^(?P\w+)/settings/emails/(?P\w+)/$', views.UserEmailsDetail.as_view(), name=views.UserEmailsDetail.view_name), diff --git a/api/users/views.py b/api/users/views.py index 88643e045afd..995dca0ab06d 100644 --- a/api/users/views.py +++ b/api/users/views.py @@ -24,7 +24,7 @@ hashids, is_truthy, ) -from api.base.views import JSONAPIBaseView, WaterButlerMixin +from api.base.views import JSONAPIBaseView from api.base.throttling import SendEmailThrottle, SendEmailDeactivationThrottle, NonCookieAuthThrottle, BurstRateThrottle from api.institutions.serializers import InstitutionSerializer from api.nodes.filters import NodesFilterMixin, UserNodesFilterMixin @@ -51,7 +51,6 @@ UserNodeSerializer, UserSettingsSerializer, UserSettingsUpdateSerializer, - UserQuickFilesSerializer, UserAccountExportSerializer, ReadEmailUserDetailSerializer, UserChangePasswordSerializer, @@ -71,12 +70,11 @@ from rest_framework import generics from rest_framework import status from rest_framework.response import Response -from rest_framework.exceptions import NotAuthenticated, NotFound, ValidationError, Throttled, PermissionDenied +from rest_framework.exceptions import NotAuthenticated, NotFound, ValidationError, Throttled from osf.models import ( Contributor, ExternalAccount, Guid, - QuickFilesNode, AbstractNode, Preprint, Node, @@ -363,45 +361,6 @@ def get_queryset(self): return self.get_queryset_from_request() -class UserQuickFiles(JSONAPIBaseView, generics.ListAPIView, WaterButlerMixin, UserMixin, ListFilterMixin): - - permission_classes = ( - drf_permissions.IsAuthenticatedOrReadOnly, - base_permissions.TokenHasScope, - ) - - ordering = ('-last_touched') - - required_read_scopes = [CoreScopes.USERS_READ] - required_write_scopes = [CoreScopes.USERS_WRITE] - - serializer_class = UserQuickFilesSerializer - view_category = 'users' - view_name = 'user-quickfiles' - - def get_node(self, check_object_permissions): - user = self.get_user() - node = QuickFilesNode.objects.get_for_user(user) - if not node: - raise NotFound() - - if not node.is_public or node.deleted: - raise PermissionDenied() - - return node - - def get_default_queryset(self): - self.kwargs[self.path_lookup_url_kwarg] = '/' - self.kwargs[self.provider_lookup_url_kwarg] = 'osfstorage' - files_list = self.fetch_from_waterbutler() - - return files_list.children.prefetch_related('versions', 'tags').include('guids') - - # overrides ListAPIView - def get_queryset(self): - return self.get_queryset_from_request() - - class UserPreprints(JSONAPIBaseView, generics.ListAPIView, UserMixin, PreprintFilterMixin): """The documentation for this endpoint can be found [here](https://developer.osf.io/#operation/users_preprints_list). """ diff --git a/api_tests/files/views/test_file_detail.py b/api_tests/files/views/test_file_detail.py index 3d64607ff80d..4cafcc86b011 100644 --- a/api_tests/files/views/test_file_detail.py +++ b/api_tests/files/views/test_file_detail.py @@ -13,7 +13,7 @@ from api.base.settings.defaults import API_BASE from api_tests import utils as api_utils from framework.auth.core import Auth -from osf.models import NodeLog, Session, QuickFilesNode +from osf.models import NodeLog, Session from osf.utils.permissions import WRITE, READ from osf.utils.workflows import DefaultStates from osf_tests.factories import ( @@ -48,10 +48,6 @@ class TestFileView: def node(self, user): return ProjectFactory(creator=user, comment_level='public') - @pytest.fixture() - def quickfiles_node(self, user): - return QuickFilesNode.objects.get(creator=user) - @pytest.fixture() def file(self, user, node): return api_utils.create_test_file(node, user, create_guid=False) diff --git a/api_tests/wb/views/test_wb_hooks.py b/api_tests/wb/views/test_wb_hooks.py index 8a87991e0eb0..556991b2ae46 100644 --- a/api_tests/wb/views/test_wb_hooks.py +++ b/api_tests/wb/views/test_wb_hooks.py @@ -2,7 +2,6 @@ import pytest -from addons.osfstorage.models import OsfStorageFolder from framework.auth import signing from osf_tests.factories import ( @@ -11,25 +10,12 @@ PreprintFactory ) from api_tests.utils import create_test_file, create_test_preprint_file -from osf.models import QuickFilesNode @pytest.fixture() def user(): return AuthUserFactory() -@pytest.fixture() -def quickfiles_node(user): - return QuickFilesNode.objects.get_for_user(user) - -@pytest.fixture() -def quickfiles_file(user, quickfiles_node): - file = create_test_file(quickfiles_node, user, filename='road_dogg.mp3') - return file - -@pytest.fixture() -def quickfiles_folder(quickfiles_node): - return OsfStorageFolder.objects.get_root(target=quickfiles_node) @pytest.fixture() def node(user): @@ -74,10 +60,6 @@ class TestMove(): def move_url(self, node): return '/_/wb/hooks/{}/move/'.format(node._id) - @pytest.fixture() - def quickfiles_move_url(self, quickfiles_node): - return '/_/wb/hooks/{}/move/'.format(quickfiles_node._id) - @pytest.fixture() def payload(self, file, folder, root_node, user): return { @@ -570,10 +552,6 @@ class TestCopy(): def copy_url(self, node): return '/_/wb/hooks/{}/copy/'.format(node._id) - @pytest.fixture() - def quickfiles_copy_url(self, quickfiles_node): - return '/_/wb/hooks/{}/copy/'.format(quickfiles_node._id) - @pytest.fixture() def payload(self, file, folder, root_node, user): return { diff --git a/osf/management/commands/data_storage_usage.py b/osf/management/commands/data_storage_usage.py index 7476fac87604..dce62940f541 100644 --- a/osf/management/commands/data_storage_usage.py +++ b/osf/management/commands/data_storage_usage.py @@ -140,23 +140,6 @@ GROUP BY node.type, node.is_public """ -# Aggregation of non-deleted quick file sizes (NOTE: This will break when QuickFolders is merged) -ND_QUICK_FILE_SIZE_SUM_SQL = """ - SELECT - node.type, sum(size) - FROM osf_basefileversionsthrough AS obfnv - LEFT JOIN osf_basefilenode file ON obfnv.basefilenode_id = file.id - LEFT JOIN osf_fileversion version ON obfnv.fileversion_id = version.id - LEFT JOIN osf_abstractnode node ON file.target_object_id = node.id - WHERE file.provider = 'osfstorage' AND file.target_content_type_id = %s - AND node.type = 'osf.quickfilesnode' - AND node.is_deleted = False - AND file.deleted_on IS NULL - AND obfnv.id >= %s AND obfnv.id <= %s - GROUP BY node.type - - """ - # Aggregation of size of non-deleted files in preprint supplemental nodes based on the node query above ND_PREPRINT_SUPPLEMENT_SIZE_SUM_SQL = """ SELECT @@ -320,17 +303,6 @@ def gather_usage_data(start, end, dry_run, zip_file): end=end, cursor=cursor, )) - - # TODO: Move the next when Quick Folders is done - logger.debug('Gathering quickfile summary at {}'.format(datetime.datetime.now())) - summary_data = combine_summary_data(summary_data, summarize( - sql=ND_QUICK_FILE_SIZE_SUM_SQL, - content_type=abstractnode_content_type, - start=start, - end=end, - cursor=cursor, - )) - logger.debug('Gathering supplement summary at {}'.format(datetime.datetime.now())) summary_data = combine_summary_data(summary_data, summarize( sql=ND_PREPRINT_SUPPLEMENT_SIZE_SUM_SQL, diff --git a/osf/management/commands/export_user_account.py b/osf/management/commands/export_user_account.py index 36886cf3efb5..4f0096683d65 100644 --- a/osf/management/commands/export_user_account.py +++ b/osf/management/commands/export_user_account.py @@ -24,7 +24,6 @@ OSFUser, Preprint, Registration, - QuickFilesNode ) from osf.utils.workflows import DefaultStates from scripts.utils import Progress @@ -149,7 +148,7 @@ def export_resource(node, user, current_dir): def export_resources(nodes_to_export, user, dir, nodes_type): """ Creates appropriate directory structure and exports a given set of resources - (projects, registrations, quickfiles or preprints) by calling export helper functions. + (projects, registrations, or preprints) by calling export helper functions. """ progress = Progress() @@ -162,7 +161,7 @@ def export_resources(nodes_to_export, user, dir, nodes_type): progress.stop() def get_usage(user): - # includes nodes, registrations, quickfiles + # includes nodes, registrations nodes = user.nodes.filter(is_deleted=False).exclude(type='osf.collection').values_list('id', flat=True) node_ctype = ContentType.objects.get_for_model(AbstractNode) node_files = get_resource_files(nodes, node_ctype) @@ -216,13 +215,6 @@ def export_account(user_id, path, only_private=False, only_admin=False, export_f ... registrations/ *same as projects* - - quickfiles/ - / - metadata.json - files/ - osfstorage-archive.zip - """ user = OSFUser.objects.get(guids___id=user_id, guids___id__isnull=False) proceed = input('\nUser has {:.2f} GB of data in OSFStorage that will be exported.\nWould you like to continue? [y/n] '.format(get_usage(user))) @@ -234,13 +226,11 @@ def export_account(user_id, path, only_private=False, only_admin=False, export_f preprints_dir = os.path.join(base_dir, 'preprints') projects_dir = os.path.join(base_dir, 'projects') registrations_dir = os.path.join(base_dir, 'registrations') - quickfiles_dir = os.path.join(base_dir, 'quickfiles') os.mkdir(base_dir) os.mkdir(preprints_dir) os.mkdir(projects_dir) os.mkdir(registrations_dir) - os.mkdir(quickfiles_dir) preprints_to_export = get_preprints_to_export(user) @@ -254,14 +244,9 @@ def export_account(user_id, path, only_private=False, only_admin=False, export_f .get_roots() ) - quickfiles_to_export = ( - QuickFilesNode.objects.filter(creator=user) - ) - export_resources(projects_to_export, user, projects_dir, 'projects') export_resources(preprints_to_export, user, preprints_dir, 'preprints') export_resources(registrations_to_export, user, registrations_dir, 'registrations') - export_resources(quickfiles_to_export, user, quickfiles_dir, 'quickfiles') timestamp = dt.datetime.fromtimestamp(time.time()).strftime('%Y%m%d%H%M%S') output = os.path.join(path, '{user_id}-export-{timestamp}'.format(**locals())) diff --git a/osf/management/commands/transfer_quickfiles_to_projects.py b/osf/management/commands/transfer_quickfiles_to_projects.py deleted file mode 100644 index 006975cde33a..000000000000 --- a/osf/management/commands/transfer_quickfiles_to_projects.py +++ /dev/null @@ -1,100 +0,0 @@ -import logging - -from django.db import connection -from django.core.management.base import BaseCommand - -from osf.models import ( - OSFUser, - QuickFilesNode, - NodeLog -) - -from addons.osfstorage.models import OsfStorageFile - -logger = logging.getLogger(__name__) - - -def remove_quickfiles(dry_run=False): - quick_files_ids = QuickFilesNode.objects.values_list('id', flat=True) - quick_files_node_with_files_ids = OsfStorageFile.objects.filter( - target_object_id__in=quick_files_ids - ).values_list( - 'target_object_id', - flat=True - ) - quick_files_nodes = QuickFilesNode.objects.filter(id__in=quick_files_node_with_files_ids) - - if not dry_run: - NodeLog.objects.bulk_create( - [ - NodeLog( - node=quick_files_node, - action=NodeLog.MIGRATED_QUICK_FILES - ) for quick_files_node in quick_files_nodes - ] - ) - - logger.info(f'{quick_files_nodes.count()} quickfiles nodes were projectified.') - if not dry_run: - quick_files_nodes.update( - type='osf.node', - description='This Project was created because...' - ) - result = QuickFilesNode.objects.all().delete() - logger.info(f'Quickfiles deleted {result}') - with connection.cursor() as cursor: - cursor.execute("""DROP INDEX IF EXISTS one_quickfiles_per_user RESTRICT;""") - logger.info('`one_quickfiles_per_user` constraint dropped.') - - -def reverse_remove_quickfiles(dry_run=False): - users = OSFUser.objects.all() - for user in users: - type_swapped_qf = user.nodes.filter(logs__action=NodeLog.MIGRATED_QUICK_FILES) - if type_swapped_qf: - if not dry_run: - type_swapped_qf.update(type='osf.quickfilesnode') - else: - if not dry_run: - QuickFilesNode.objects.create_for_user(user) - - with connection.cursor() as cursor: - cursor.execute( - """ - CREATE UNIQUE INDEX one_quickfiles_per_user ON osf_abstractnode (creator_id, type, is_deleted) - WHERE type='osf.quickfilesnode' AND is_deleted=FALSE; - """ - ) - logger.info('`one_quickfiles_per_user` constraint was reinstated.') - - NodeLog.objects.filter(action=NodeLog.MIGRATED_QUICK_FILES).delete() - - logger.info(f'{users.count()} quickfiles were restored.') - -class Command(BaseCommand): - """ - Puts all Quickfiles into projects or reverses the effect. - """ - def add_arguments(self, parser): - super().add_arguments(parser) - parser.add_argument( - '--dry', - action='store_true', - dest='dry_run', - help='Run migration and roll back changes to db', - required=False, - ) - parser.add_argument( - '--reverse', - type=bool, - help='is the reverse to be run?.', - required=False, - ) - - def handle(self, *args, **options): - dry_run = options.get('dry_run', None) - reverse = options.get('reverse', None) - if reverse: - reverse_remove_quickfiles(dry_run) - else: - remove_quickfiles(dry_run) diff --git a/osf/management/commands/update_storage_usage.py b/osf/management/commands/update_storage_usage.py index 8012ff5406c7..1bf7b958227f 100644 --- a/osf/management/commands/update_storage_usage.py +++ b/osf/management/commands/update_storage_usage.py @@ -20,7 +20,7 @@ def update_storage_usage(dry_run=False, days=DAYS): recently_modified = AbstractNode.objects.filter(modified__gt=modified_limit) for modified_node in recently_modified: file_op_occurred = modified_node.logs.filter(action__contains='file', created__gt=modified_limit).exists() - if not modified_node.is_quickfiles and file_op_occurred: + if file_op_occurred: update_storage_usage_cache(modified_node.id, modified_node._id) if dry_run: diff --git a/osf/migrations/0053_add_quickfiles.py b/osf/migrations/0053_add_quickfiles.py index 23f39590765c..4915d8ca7cde 100644 --- a/osf/migrations/0053_add_quickfiles.py +++ b/osf/migrations/0053_add_quickfiles.py @@ -2,82 +2,7 @@ # Generated by Django 1.11 on 2017-06-13 17:35 from __future__ import unicode_literals -import logging - from django.db import migrations, models -from django.core.paginator import Paginator - -from addons.osfstorage.models import NodeSettings as OSFSNodeSettings, OsfStorageFolder -from osf.models import OSFUser, QuickFilesNode, Contributor -from osf.models.base import ensure_guid -from osf.models.quickfiles import get_quickfiles_project_title - -logger = logging.getLogger(__name__) -logging.basicConfig(level=logging.INFO) - - -def add_quickfiles(*args, **kwargs): - ids_without_quickfiles = list(OSFUser.objects.exclude(nodes_created__type=QuickFilesNode._typedmodels_type).values_list('id', flat=True)) - - users_without_quickfiles = OSFUser.objects.filter(id__in=ids_without_quickfiles).order_by('id') - total_quickfiles_to_create = users_without_quickfiles.count() - - logger.info('About to add a QuickFilesNode for {} users.'.format(total_quickfiles_to_create)) - - paginated_users = Paginator(users_without_quickfiles, 1000) - - total_created = 0 - for page_num in paginated_users.page_range: - quickfiles_to_create = [] - for user in paginated_users.page(page_num).object_list: - quickfiles_to_create.append( - QuickFilesNode( - title=get_quickfiles_project_title(user), - creator=user - ) - ) - total_created += 1 - - all_quickfiles = QuickFilesNode.objects.bulk_create(quickfiles_to_create) - logger.info('Created {}/{} QuickFilesNodes'.format(total_created, total_quickfiles_to_create)) - logger.info('Preparing to create contributors and folders') - - contributors_to_create = [] - osfs_folders_to_create = [] - for quickfiles in all_quickfiles: - ensure_guid(QuickFilesNode, quickfiles, True) - osfs_folders_to_create.append( - OsfStorageFolder(provider='osfstorage', name='', node=quickfiles) - ) - - contributors_to_create.append( - Contributor( - user=quickfiles.creator, - node=quickfiles, - visible=True, - read=True, - write=True, - admin=True, - _order=0 - ) - ) - - Contributor.objects.bulk_create(contributors_to_create) - OsfStorageFolder.objects.bulk_create(osfs_folders_to_create) - - logger.info('Contributors and addons folders') - logger.info('Adding storage addons') - osfs_to_create = [] - for folder in osfs_folders_to_create: - osfs_to_create.append( - OSFSNodeSettings(owner=folder.node, root_node=folder) - ) - - OSFSNodeSettings.objects.bulk_create(osfs_to_create) - -def remove_quickfiles(*args, **kwargs): - QuickFilesNode.objects.all().delete() - class Migration(migrations.Migration): @@ -101,7 +26,7 @@ class Migration(migrations.Migration): name='type', field=models.CharField(choices=[('osf.node', 'node'), ('osf.collection', 'collection'), ('osf.registration', 'registration'), ('osf.quickfilesnode', 'quickfilesnode')], db_index=True, max_length=255), ), - migrations.RunPython(add_quickfiles, remove_quickfiles), + migrations.RunPython(migrations.RunPython.noop, migrations.RunPython.noop), migrations.RunSQL( [ """ diff --git a/osf/migrations/0242_auto_20220201_1927.py b/osf/migrations/0242_auto_20220201_1927.py new file mode 100644 index 000000000000..04f03e7265e4 --- /dev/null +++ b/osf/migrations/0242_auto_20220201_1927.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2022-02-01 19:27 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('osf', '0241_abstractprovider_allow_bulk_uploads'), + ] + + operations = [ + migrations.DeleteModel( + name='QuickFilesNode', + ), + migrations.AlterField( + model_name='abstractnode', + name='type', + field=models.CharField(choices=[('osf.node', 'node'), ('osf.draftnode', 'draft node'), ('osf.registration', 'registration')], db_index=True, max_length=255), + ), + ] diff --git a/osf/models/__init__.py b/osf/models/__init__.py index f70f2f9abb45..d5cb176eeec2 100644 --- a/osf/models/__init__.py +++ b/osf/models/__init__.py @@ -43,7 +43,6 @@ from osf.models.admin_log_entry import AdminLogEntry # noqa from osf.models.maintenance_state import MaintenanceState # noqa from osf.models.banner import ScheduledBanner # noqa -from osf.models.quickfiles import QuickFilesNode # noqa from osf.models.dismissed_alerts import DismissedAlert # noqa from osf.models.action import ReviewAction # noqa from osf.models.action import NodeRequestAction, PreprintRequestAction, ReviewAction, RegistrationAction, SchemaResponseAction, BaseAction # noqa diff --git a/osf/models/mixins.py b/osf/models/mixins.py index 1c1496f2281d..418a7d9de1e7 100644 --- a/osf/models/mixins.py +++ b/osf/models/mixins.py @@ -2132,7 +2132,7 @@ def suspend_spam_user(self, user, train_akismet=False): # Make public nodes private from this contributor for node in user.all_nodes: - if self._id != node._id and len(node.contributors) == 1 and node.is_public and not node.is_quickfiles: + if self._id != node._id and len(node.contributors) == 1 and node.is_public: node.confirm_spam(save=True, train_akismet=train_akismet) node.set_privacy('private', log=False, save=True) diff --git a/osf/models/node.py b/osf/models/node.py index 2d4399329279..914e3414e726 100644 --- a/osf/models/node.py +++ b/osf/models/node.py @@ -90,7 +90,7 @@ class AbstractNodeQuerySet(GuidMixinQuerySet): def get_roots(self): - return self.filter(id__in=self.exclude(type__in=['osf.collection', 'osf.quickfilesnode', 'osf.draftnode']).values_list('root_id', flat=True)) + return self.filter(id__in=self.exclude(type__in=['osf.collection', 'osf.draftnode']).values_list('root_id', flat=True)) def get_children(self, root, active=False, include_root=False): # If `root` is a root node, we can use the 'descendants' related name @@ -480,10 +480,6 @@ def is_registration(self): """For v1 compat.""" return False - @property - def is_quickfiles(self): - return False - @property def is_original(self): return not self.is_registration and not self.is_fork @@ -2104,10 +2100,10 @@ def update(self, fields, auth=None, save=True): continue # Title, description, and category have special methods for logging purposes if key == 'title': - if not self.is_bookmark_collection or not self.is_quickfiles: + if not self.is_bookmark_collection: self.set_title(title=value, auth=auth, save=False) else: - raise NodeUpdateError(reason='Bookmark collections or QuickFilesNodes cannot be renamed.', key=key) + raise NodeUpdateError(reason='Bookmark collections or cannot be renamed.', key=key) elif key == 'description': self.set_description(description=value, auth=auth, save=False) elif key == 'category': @@ -2486,7 +2482,6 @@ def add_default_node_addons(sender, instance, created, **kwargs): @receiver(post_save, sender=Node) @receiver(post_save, sender='osf.Registration') -@receiver(post_save, sender='osf.QuickFilesNode') @receiver(post_save, sender='osf.DraftNode') def set_parent_and_root(sender, instance, created, *args, **kwargs): if getattr(instance, '_parent', None): diff --git a/osf/models/private_link.py b/osf/models/private_link.py index 9f752eab7afa..de837f6c6dd7 100644 --- a/osf/models/private_link.py +++ b/osf/models/private_link.py @@ -1,7 +1,4 @@ from django.db import models -from django.dispatch import receiver -from django.core.exceptions import ValidationError - from framework.utils import iso8601format from osf.models.base import BaseModel, ObjectIDMixin @@ -42,13 +39,3 @@ def to_json(self): for x in self.nodes.filter(is_deleted=False)], 'anonymous': self.anonymous } - - -##### Signal listeners ##### -@receiver(models.signals.m2m_changed, sender=PrivateLink.nodes.through) -def check_if_private_link_is_to_quickfiles(sender, instance, action, reverse, model, pk_set, **kwargs): - from osf.models.node import AbstractNode - - if action == 'pre_add' and pk_set: - if model == AbstractNode and model.objects.get(id=list(pk_set)[0]).is_quickfiles: - raise ValidationError('A private link cannot be added to a QuickFilesNode') diff --git a/osf/models/quickfiles.py b/osf/models/quickfiles.py deleted file mode 100644 index abc10b307ae2..000000000000 --- a/osf/models/quickfiles.py +++ /dev/null @@ -1,95 +0,0 @@ -from __future__ import unicode_literals - -import logging - -from osf.models.node import ( - AbstractNode, - AbstractNodeManager, - NodeLog -) - -from osf.exceptions import NodeStateError - - -logger = logging.getLogger(__name__) - - -class QuickFilesNodeManager(AbstractNodeManager): - - def create_for_user(self, user): - possessive_title = get_quickfiles_project_title(user) - - quickfiles, created = QuickFilesNode.objects.get_or_create( - title=possessive_title, - creator=user - ) - - if not created: - raise NodeStateError('Users may only have one quickfiles project') - - quickfiles.add_addon('osfstorage', auth=None, log=False) - - return quickfiles - - def get_for_user(self, user): - try: - return QuickFilesNode.objects.get(creator=user) - except QuickFilesNode.DoesNotExist: - try: - return user.nodes.get(logs__action=NodeLog.MIGRATED_QUICK_FILES) - except AbstractNode.DoesNotExist: - return None - - - -class QuickFilesNode(AbstractNode): - __guid_min_length__ = 10 - - objects = QuickFilesNodeManager() - - def __init__(self, *args, **kwargs): - kwargs['is_public'] = True - super(QuickFilesNode, self).__init__(*args, **kwargs) - - def remove_node(self, auth, date=None): - # QuickFilesNodes are only delete-able for disabled users - # This is only done when doing a GDPR-delete - if auth.user.is_disabled: - super(QuickFilesNode, self).remove_node(auth=auth, date=date) - else: - raise NodeStateError('A QuickFilesNode may not be deleted.') - - def set_privacy(self, permissions, *args, **kwargs): - raise NodeStateError('You may not set privacy for a QuickFilesNode.') - - def add_contributor(self, contributor, *args, **kwargs): - if contributor == self.creator: - return super(QuickFilesNode, self).add_contributor(contributor, *args, **kwargs) - raise NodeStateError('A QuickFilesNode may not have additional contributors.') - - def clone(self): - raise NodeStateError('A QuickFilesNode may not be forked, used as a template, or registered.') - - def add_addon(self, name, auth, log=True): - if name != 'osfstorage': - raise NodeStateError('A QuickFilesNode can only have the osfstorage addon.') - return super(QuickFilesNode, self).add_addon(name, auth, log) - - @property - def is_registration(self): - """For v1 compat.""" - return False - - @property - def is_collection(self): - """For v1 compat.""" - return False - - @property - def is_quickfiles(self): - return True - - -def get_quickfiles_project_title(user): - possessive_title_name = user.fullname + "'s" if user.fullname[-1] != 's' else user.fullname + "'" - return '{} Quick Files'.format(possessive_title_name) diff --git a/osf/models/user.py b/osf/models/user.py index dbc1429ae789..986908d315fd 100644 --- a/osf/models/user.py +++ b/osf/models/user.py @@ -60,7 +60,6 @@ logger = logging.getLogger(__name__) -MAX_QUICKFILES_MERGE_RENAME_ATTEMPTS = 1000 def get_default_mailing_lists(): return {'Open Science Framework Help': True} @@ -785,9 +784,6 @@ def merge_user(self, user): # - projects where the user was a contributor (group member only are not included). for node in user.contributed: - # Skip quickfiles - if node.is_quickfiles: - continue user_perms = Contributor(node=node, user=user).permission # if both accounts are contributor of the same project if node.is_contributor(self) and node.is_contributor(user): @@ -811,11 +807,10 @@ def merge_user(self, user): # Skip bookmark collections user.collection_set.exclude(is_bookmark_collection=True).update(creator=self) - from osf.models import QuickFilesNode from osf.models import BaseFileNode # - projects where the user was the creator - user.nodes_created.exclude(type=QuickFilesNode._typedmodels_type).update(creator=self) + user.nodes_created.update(creator=self) # - file that the user has checked_out, import done here to prevent import error for file_node in BaseFileNode.files_checked_out(user=user): @@ -1006,13 +1001,6 @@ def save(self, *args, **kwargs): if self.SEARCH_UPDATE_FIELDS.intersection(dirty_fields) and self.is_confirmed: self.update_search() self.update_search_nodes_contributors() - if 'fullname' in dirty_fields: - from osf.models.quickfiles import get_quickfiles_project_title, QuickFilesNode - - quickfiles = QuickFilesNode.objects.filter(creator=self).first() - if quickfiles: - quickfiles.title = get_quickfiles_project_title(self) - quickfiles.save() if 'username' in dirty_fields: for list_name, subscription in self.mailchimp_mailing_lists.items(): if subscription: @@ -1394,14 +1382,14 @@ def confirm_email(self, token, merge=False): def confirm_spam(self, save=True): super().confirm_spam(save=save) - for node in self.nodes.filter(is_public=True, is_deleted=False).exclude(type='osf.quickfilesnode'): + for node in self.nodes.filter(is_public=True, is_deleted=False): node.confirm_spam(train_akismet=False) for preprint in self.preprints.filter(is_public=True, deleted__isnull=True): preprint.confirm_spam(train_akismet=False) def confirm_ham(self, save=False): super().confirm_ham(save=save) - for node in self.nodes.filter(logs__action=NodeLog.CONFIRM_SPAM).exclude(type='osf.quickfilesnode'): + for node in self.nodes.filter(logs__action=NodeLog.CONFIRM_SPAM): node.confirm_ham(save=save, train_akismet=False) for preprint in self.preprints.filter(logs__action=PreprintLog.CONFIRM_SPAM): preprint.confirm_ham(save=save, train_akismet=False) @@ -1799,7 +1787,7 @@ def gdpr_delete(self): ) shared_nodes = user_nodes.exclude(id__in=personal_nodes.values_list('id')) - for node in shared_nodes.exclude(type__in=['osf.quickfilesnode', 'osf.draftnode']): + for node in shared_nodes.exclude(type__in=['osf.draftnode']): alternate_admins = OSFUser.objects.filter(groups__name=node.format_group(ADMIN)).filter(is_active=True).exclude(id=self.id) if not alternate_admins: raise UserStateError( @@ -1823,7 +1811,7 @@ def gdpr_delete(self): # This is doesn't to remove identifying info, but ensures other users can't see the deleted user's profile etc. self.disable_account() - # delete all personal nodes (one contributor), bookmarks, quickfiles etc. + # delete all personal nodes (one contributor), bookmarks for node in personal_nodes.all(): logger.info('Soft-deleting node (pk: {node_id})...'.format(node_id=node.pk)) node.remove_node(auth=Auth(self)) @@ -1876,18 +1864,14 @@ def has_resources(self): If a user only has no resources or only deleted resources this will return false and they can safely be deactivated otherwise they must delete or transfer their outstanding resources. - :return bool: does the user have any active node, preprints, groups, quickfiles etc? + :return bool: does the user have any active node, preprints, groups etc? """ from osf.models import Preprint - - # TODO: Update once quickfolders in merged - - nodes = self.nodes.exclude(type='osf.quickfilesnode').exclude(is_deleted=True).exists() - quickfiles = self.nodes.get(type='osf.quickfilesnode').files.exists() + nodes = self.nodes.exclude(is_deleted=True).exists() groups = self.osf_groups.exists() preprints = Preprint.objects.filter(_contributors=self, ever_public=True, deleted__isnull=True).exists() - return groups or nodes or quickfiles or preprints + return groups or nodes or preprints class Meta: # custom permissions for use in the OSF Admin App diff --git a/scripts/analytics/file_summary.py b/scripts/analytics/file_summary.py index b3844f2a9456..19dd79322531 100644 --- a/scripts/analytics/file_summary.py +++ b/scripts/analytics/file_summary.py @@ -51,7 +51,7 @@ def get_events(self, date): 'timestamp': timestamp_datetime.isoformat() }, # OsfStorageFiles - the number of files on OsfStorage - 'osfstorage_files_including_quickfiles': { + 'osfstorage_files': { 'total': file_qs.count(), 'public': file_qs.filter(public_query).count(), 'private': file_qs.filter(private_query).count(), @@ -63,7 +63,7 @@ def get_events(self, date): logger.info( 'OsfStorage Files counted. Files: {}'.format( - totals['osfstorage_files_including_quickfiles']['total'], + totals['osfstorage_files']['total'], ) ) diff --git a/scripts/fix_merged_user_quickfiles.py b/scripts/fix_merged_user_quickfiles.py deleted file mode 100644 index 3ac558c779e2..000000000000 --- a/scripts/fix_merged_user_quickfiles.py +++ /dev/null @@ -1,34 +0,0 @@ -import logging -import sys - -from django.db import transaction -from django.db.models import F, Count - -from website.app import setup_django -setup_django() -from osf.models import QuickFilesNode -from scripts import utils as script_utils - - -logger = logging.getLogger(__name__) - -def main(): - dry = '--dry' in sys.argv - if not dry: - # If we're not running in dry mode log everything to a file - script_utils.add_file_logger(logger, __file__) - with transaction.atomic(): - qs = QuickFilesNode.objects.exclude(_contributors=F('creator')).annotate(contrib_count=Count('_contributors')).exclude(contrib_count=0) - logger.info('Found {} quickfiles nodes with mismatched creator and _contributors'.format(qs.count())) - - for node in qs: - bad_contrib = node._contributors.get() - logger.info('Fixing {} (quickfiles node): Replacing {} (bad contributor) with {} (creator)'.format(node._id, bad_contrib._id, node.creator._id)) - node.contributor_set.filter(user=bad_contrib).update(user=node.creator) - node.save() - if dry: - raise Exception('Abort Transaction - Dry Run') - print('Done') - -if __name__ == '__main__': - main() diff --git a/scripts/generate_sitemap.py b/scripts/generate_sitemap.py index 8655984f1956..ef17e5ad1242 100644 --- a/scripts/generate_sitemap.py +++ b/scripts/generate_sitemap.py @@ -184,7 +184,7 @@ def generate(self): # AbstractNode urls (Nodes and Registrations, no Collections) objs = (AbstractNode.objects .filter(is_public=True, is_deleted=False, retraction_id__isnull=True) - .exclude(type__in=['osf.collection', 'osf.quickfilesnode']) + .exclude(type__in=['osf.collection']) .values('guids___id', 'modified')) progress.start(objs.count(), 'NODE: ') for obj in objs: diff --git a/scripts/remove_after_use/verify_groups_guardian_migration.py b/scripts/remove_after_use/verify_groups_guardian_migration.py index e1c56d2e2ca6..6fce6f965b46 100644 --- a/scripts/remove_after_use/verify_groups_guardian_migration.py +++ b/scripts/remove_after_use/verify_groups_guardian_migration.py @@ -11,8 +11,8 @@ from django.apps import apps from django.contrib.auth.models import Permission, Group -from osf.utils.permissions import PERMISSIONS, reduce_permissions -from osf.models import AbstractNode, Contributor, Preprint, Node, Registration, QuickFilesNode +from osf.utils.permissions import PERMISSIONS +from osf.models import AbstractNode, Contributor, Preprint, Node, Registration from osf.models.node import NodeGroupObjectPermission from osf.models.preprint import PreprintGroupObjectPermission from osf.utils.permissions import READ, WRITE, ADMIN @@ -73,7 +73,7 @@ def verify_preprint_foreign_key_migration(): check_expected(expected_preprintgroupobjperm_count, actual_preprintgroupobjperm_count, 'Discrepancy in PreprintGroupObjectPermission table.') def verify_random_objects(): - resources = [Node, Registration, QuickFilesNode] + resources = [Node, Registration] for resource in resources: for i in range(1,10): random_resource = _get_random_object(resource) diff --git a/tests/test_addons.py b/tests/test_addons.py index e395cd7e62d6..ccaa69fe8fcf 100644 --- a/tests/test_addons.py +++ b/tests/test_addons.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- -import os import datetime -from rest_framework import status as http_status import time import functools @@ -13,12 +11,10 @@ import mock import pytest from django.utils import timezone -from django.contrib.auth.models import Permission from framework.auth import cas, signing from framework.auth.core import Auth from framework.exceptions import HTTPError from nose.tools import * # noqa -from osf_tests import factories from tests.base import OsfTestCase, get_default_metaschema from api_tests.utils import create_test_file from osf_tests.factories import (AuthUserFactory, ProjectFactory, @@ -30,9 +26,9 @@ from addons.github.tests.factories import GitHubAccountFactory from addons.osfstorage.models import OsfStorageFileNode, OsfStorageFolder, OsfStorageFile from addons.osfstorage.tests.factories import FileVersionFactory -from osf.models import Session, RegistrationSchema, QuickFilesNode +from osf.models import Session from osf.models import files as file_models -from osf.models.files import BaseFileNode, TrashedFileNode, FileVersion +from osf.models.files import BaseFileNode, TrashedFileNode from osf.utils.permissions import WRITE, READ from website.project import new_private_link from website.project.views.node import _view_project as serialize_node @@ -111,6 +107,7 @@ def test_auth_render_action_returns_200(self): url = self.build_url(action='render') res = self.app.get(url, auth=self.user.auth) assert_equal(res.status_code, 200) + def test_auth_render_action_requires_read_permission(self): node = ProjectFactory(is_public=False) url = self.build_url(action='render', nid=node._id) diff --git a/tests/test_views.py b/tests/test_views.py index 4d974737e7b8..21a4d133a90b 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -39,7 +39,7 @@ from framework.transactions.handlers import no_auto_transaction from website import mailchimp_utils, mails, settings, language from addons.osfstorage import settings as osfstorage_settings -from osf.models import AbstractNode, NodeLog, QuickFilesNode +from osf.models import AbstractNode, NodeLog from website.profile.utils import add_contributor_json, serialize_unregistered from website.profile.views import update_osf_help_mails_subscription from website.project.decorators import check_can_access @@ -75,7 +75,7 @@ pytestmark = pytest.mark.django_db -from osf.models import NodeRelation, QuickFilesNode, NotableEmailDomain +from osf.models import NodeRelation, NotableEmailDomain from osf_tests.factories import ( fake_email, ApiOAuth2ApplicationFactory, diff --git a/website/ember_osf_web/views.py b/website/ember_osf_web/views.py index 1947439baf08..659a916ac0d1 100644 --- a/website/ember_osf_web/views.py +++ b/website/ember_osf_web/views.py @@ -11,8 +11,6 @@ ember_osf_web_dir = os.path.abspath(os.path.join(os.getcwd(), EXTERNAL_EMBER_APPS['ember_osf_web']['path'])) routes = [ - '/quickfiles/', - '//quickfiles/', '/institutions/', '/support/', ] diff --git a/website/notifications/utils.py b/website/notifications/utils.py index f59f91f92dbe..23afccd6e950 100644 --- a/website/notifications/utils.py +++ b/website/notifications/utils.py @@ -205,7 +205,6 @@ def get_configured_projects(user): configured_projects = set() user_subscriptions = get_all_user_subscriptions(user, extra=( ~Q(node__type='osf.collection') & - ~Q(node__type='osf.quickfilesnode') & Q(node__is_deleted=False) )) diff --git a/website/project/decorators.py b/website/project/decorators.py index af16941afefc..8f8b64a7a9ee 100644 --- a/website/project/decorators.py +++ b/website/project/decorators.py @@ -76,7 +76,7 @@ def wrapped(*args, **kwargs): return wrapped -def must_be_valid_project(func=None, retractions_valid=False, quickfiles_valid=False, preprints_valid=False, groups_valid=False): +def must_be_valid_project(func=None, retractions_valid=False, preprints_valid=False, groups_valid=False): """ Ensures permissions to retractions are never implicitly granted. """ # TODO: Check private link @@ -95,7 +95,7 @@ def wrapped(*args, **kwargs): _inject_nodes(kwargs) - if getattr(kwargs['node'], 'is_collection', True) or (getattr(kwargs['node'], 'is_quickfiles', True) and not quickfiles_valid): + if getattr(kwargs['node'], 'is_collection', True): raise HTTPError( http_status.HTTP_404_NOT_FOUND ) diff --git a/website/project/tasks.py b/website/project/tasks.py index 6fb8d72f5a19..08cee74de7d1 100644 --- a/website/project/tasks.py +++ b/website/project/tasks.py @@ -16,7 +16,7 @@ def on_node_updated(node_id, user_id, first_save, saved_fields, request_headers= AbstractNode = apps.get_model('osf.AbstractNode') node = AbstractNode.load(node_id) - if node.is_collection or node.archiving or node.is_quickfiles: + if node.is_collection or node.archiving: return need_update = bool(node.SEARCH_UPDATE_FIELDS.intersection(saved_fields)) diff --git a/website/project/views/node.py b/website/project/views/node.py index 4c54c3c11cc0..90a9cf2363b0 100644 --- a/website/project/views/node.py +++ b/website/project/views/node.py @@ -1220,8 +1220,7 @@ def search_node(auth, **kwargs): title__icontains=query, is_deleted=False) .exclude(id__in=nin) - .exclude(type='osf.collection') - .exclude(type='osf.quickfilesnode')) + .exclude(type='osf.collection')) count = nodes.count() pages = math.ceil(count / size) diff --git a/website/routes.py b/website/routes.py index 90625c24a9d9..d99890a9c60a 100644 --- a/website/routes.py +++ b/website/routes.py @@ -1420,14 +1420,6 @@ def make_url_map(app): 'get', addon_views.addon_view_or_download_file_legacy, json_renderer - ), - Rule( - [ - '/quickfiles//' - ], - 'get', - addon_views.addon_view_or_download_quickfile, - json_renderer ) ]) diff --git a/website/search/elastic_search.py b/website/search/elastic_search.py index f85995901955..286a4ac5abbd 100644 --- a/website/search/elastic_search.py +++ b/website/search/elastic_search.py @@ -25,7 +25,6 @@ from osf.models import BaseFileNode from osf.models import Institution from osf.models import OSFGroup -from osf.models import QuickFilesNode from osf.models import Preprint from osf.models import SpamStatus from addons.wiki.models import WikiPage @@ -517,7 +516,7 @@ def update_node(node, index=None, bulk=False, async_update=False): update_file(file_, index=index) is_qa_node = bool(set(settings.DO_NOT_INDEX_LIST['tags']).intersection(node.tags.all().values_list('name', flat=True))) or any(substring in node.title for substring in settings.DO_NOT_INDEX_LIST['titles']) - if node.is_deleted or not node.is_public or node.archiving or node.is_spam or (node.spam_status == SpamStatus.FLAGGED and settings.SPAM_FLAGGED_REMOVE_FROM_SEARCH) or node.is_quickfiles or is_qa_node: + if node.is_deleted or not node.is_public or node.archiving or node.is_spam or (node.spam_status == SpamStatus.FLAGGED and settings.SPAM_FLAGGED_REMOVE_FROM_SEARCH) or is_qa_node: delete_doc(node._id, node, index=index) else: category = get_doctype_from_node(node) @@ -669,17 +668,6 @@ def update_user(user, index=None): if not user.is_active: try: client().delete(index=index, doc_type='user', id=user._id, refresh=True, ignore=[404]) - # update files in their quickfiles node if the user has been marked as spam - if user.spam_status == SpamStatus.SPAM: - quickfiles = QuickFilesNode.objects.get_for_user(user) - for quickfile_id in quickfiles.files.values_list('_id', flat=True): - client().delete( - index=index, - doc_type='file', - id=quickfile_id, - refresh=True, - ignore=[404] - ) except NotFoundError: pass return @@ -761,10 +749,7 @@ def update_file(file_, index=None, delete=False): provider=file_.provider, path=file_.path, ) - if getattr(target, 'is_quickfiles', None): - node_url = '/{user_id}/quickfiles/'.format(user_id=target.creator._id) - else: - node_url = '/{target_id}/'.format(target_id=target._id) + node_url = '/{target_id}/'.format(target_id=target._id) guid_url = None file_guid = file_.get_guid(create=False) diff --git a/website/search_migration/__init__.py b/website/search_migration/__init__.py index d0dcc8e1a2d7..edcfe414497d 100644 --- a/website/search_migration/__init__.py +++ b/website/search_migration/__init__.py @@ -441,7 +441,7 @@ AND name != '' AND target_object_id = ANY (SELECT id FROM osf_abstractnode - WHERE (TYPE = 'osf.node' OR TYPE = 'osf.registration' OR TYPE = 'osf.quickfilesnode') + WHERE (TYPE = 'osf.node' OR TYPE = 'osf.registration') AND is_public IS TRUE AND is_deleted IS FALSE AND (spam_status IS NULL OR NOT (spam_status = 2 or (spam_status = 1 AND {spam_flagged_removed_from_search}))) @@ -627,7 +627,7 @@ AND content_type_id = (SELECT id FROM django_content_type WHERE model = 'abstractnode') LIMIT 1 ) PARENT_GUID ON TRUE -WHERE NOT ((TYPE = 'osf.node' OR TYPE = 'osf.registration' OR TYPE = 'osf.quickfilesnode') +WHERE NOT ((TYPE = 'osf.node' OR TYPE = 'osf.registration') AND N.is_public IS TRUE AND N.is_deleted IS FALSE AND (spam_status IS NULL OR NOT (spam_status = 2 or (spam_status = 1 AND {spam_flagged_removed_from_search}))) diff --git a/website/static/js/components/quickFiles.js b/website/static/js/components/quickFiles.js deleted file mode 100644 index d339e4f3ebe9..000000000000 --- a/website/static/js/components/quickFiles.js +++ /dev/null @@ -1,150 +0,0 @@ -'use strict'; - -var m = require('mithril'); // exposes mithril methods, useful for redraw etc. -var $osf = require('js/osfHelpers'); -var iconmap = require('js/iconmap'); -var lodashFind = require('lodash.find'); -var mHelpers = require('js/mithrilHelpers'); -var Raven = require('raven-js'); - -var withPagination = require('js/components/pagination').withPagination; - -var QUICKFILES_PAGE_SIZE = 10; - - -var _buildUrl = function(page, user) { - - var query = { - 'page[size]': QUICKFILES_PAGE_SIZE, - 'page': page || 1, - 'version': '2.2', - }; - - return $osf.apiV2Url('users/' + user + '/quickfiles/', { query: query}); -}; - - -var _getNextItems = function(ctrl, url, updatePagination) { - if(ctrl.requestPending()) { - return; - } - - ctrl.quickFiles([]); - ctrl.requestPending(true); - - var promise = m.request({ - method : 'GET', - url : url, - background : true, - config: mHelpers.apiV2Config({withCredentials: window.contextVars.isOnRootDomain}) - }); - - promise.then( - function(result) { - ctrl.requestPending(false); - ctrl.quickFiles(result.data); - updatePagination(result, url); - m.redraw(); - return promise; - }, function(xhr, textStatus, error) { - ctrl.failed = true; - ctrl.requestPending(false); - m.redraw(); - Raven.captureMessage('Error retrieving quickfiles', { - extra: { - url: url, - textStatus: textStatus, - error: error - } - }); - } - ); -}; - - -var QuickFile = { - - controller: function(options) { - var self = this; - self.file = options.file; - self.icon = iconmap.file; - }, - - view: function(ctrl) { - var viewBase = window.location.origin; - var viewUrl = ctrl.file.attributes.guid ? viewBase + '/' + ctrl.file.attributes.guid : viewBase + '/quickfiles' + ctrl.file.attributes.path; - return m('div', [ - m('li.project list-group-item list-group-item-node cite-container', [ - m('h4.list-group-item-heading', [ - m('span.component-overflow.f-w-lg', {style: {lineHeight: 1.5, width: '100%'}}, [ - m('span.col-md-8.project-statuses-lg', [ - m('span', {class: ctrl.icon, style: 'padding-right: 5px;'}, ''), - m('a', {'href': viewUrl, - onclick : function () { - $osf.trackClick('QuickFiles', 'view', 'view-quickfile-from-profile-page'); - } - }, ctrl.file.attributes.name), - ]) - ]) - ]) - ]) - ]); - } -}; - -var QuickFiles = { - - controller: function (options) { - var self = this; - self.failed = false; - self.user = options.user._id; - self.isProfile = options.user.is_profile; - - self.quickFiles = m.prop([]); - self.requestPending = m.prop(false); - - self.getCurrentQuickFiles = function _getCurrentQuickFiles(page) { - if (!self.requestPending()) { - var url = _buildUrl(page, self.user); - return _getNextItems(self, url, options.updatePagination); - } - }; - self.getCurrentQuickFiles(); - }, - - view: function (ctrl) { - - return m('ul.list-group m-md', [ - // Error message if the request fails - ctrl.failed ? m('p', [ - 'Unable to retrieve quickfiles at this time. Please refresh the page or contact ', - m('a', {'href': 'mailto:support@osf.io'}, 'support@osf.io'), - ' if the problem persists.' - ]) : - - // Show laoding icon while there is a pending request - ctrl.requestPending() ? m('.ball-pulse.ball-scale-blue.text-center', [m(''), m(''), m('')]) : - - // Display each quickfile - [ - ctrl.quickFiles().length !== 0 ? ctrl.quickFiles().map(function(file) { - return m.component(QuickFile, {file: file}); - }) : ctrl.isProfile ? - m('div.help-block', {}, 'You have no public quickfiles') - : m('div.help-block', {}, 'This user has no public quickfiles.') - ] - ]); - } -}; - -var PaginationWrapper = withPagination({ - buildUrl: _buildUrl, - getNextItems: _getNextItems -}); - -QuickFiles = new PaginationWrapper(QuickFiles); - - -module.exports = { - QuickFiles: QuickFiles -}; diff --git a/website/static/js/pages/profile-page.js b/website/static/js/pages/profile-page.js index bb457f9a4975..2df42fb3aaac 100644 --- a/website/static/js/pages/profile-page.js +++ b/website/static/js/pages/profile-page.js @@ -10,7 +10,6 @@ require('../project.js'); // Needed for nodelists to work require('../components/logFeed.js'); // Needed for nodelists to work var profile = require('../profile.js'); // Social, Job, Education classes var publicNodes = require('../components/publicNodes.js'); -var quickFiles = require('../components/quickFiles.js'); var ctx = window.contextVars; // Instantiate all the profile modules diff --git a/website/views.py b/website/views.py index c039f45efcbe..6094b583247b 100644 --- a/website/views.py +++ b/website/views.py @@ -311,7 +311,7 @@ def resolve_guid(guid, suffix=None): if isinstance(referent, DraftNode): raise HTTPError(http_status.HTTP_404_NOT_FOUND) - if isinstance(referent, BaseFileNode) and referent.is_file and (getattr(referent.target, 'is_quickfiles', False)): + if isinstance(referent, BaseFileNode) and referent.is_file: if referent.is_deleted: raise HTTPError(http_status.HTTP_410_GONE) if PROXY_EMBER_APPS: