diff --git a/frontend/src/metadata/metadata-view/store/index.js b/frontend/src/metadata/metadata-view/store/index.js index e2657003e7..2eb31609d6 100644 --- a/frontend/src/metadata/metadata-view/store/index.js +++ b/frontend/src/metadata/metadata-view/store/index.js @@ -264,6 +264,7 @@ class Store { let valid_id_original_row_updates = {}; let valid_id_old_row_data = {}; let valid_id_original_old_row_data = {}; + let id_obj_id = {}; originalRows.forEach(row => { if (!row || !this.context.canModifyRow(row)) { return; @@ -271,6 +272,7 @@ class Store { const rowId = row._id; valid_row_ids.push(rowId); valid_id_row_updates[rowId] = id_row_updates[rowId]; + id_obj_id[rowId] = row._obj_id; valid_id_original_row_updates[rowId] = id_original_row_updates[rowId]; valid_id_old_row_data[rowId] = id_old_row_data[rowId]; valid_id_original_old_row_data[rowId] = id_original_old_row_data[rowId]; @@ -286,6 +288,7 @@ class Store { id_old_row_data: valid_id_old_row_data, id_original_old_row_data: valid_id_original_old_row_data, is_copy_paste, + id_obj_id: id_obj_id }); this.applyOperation(operation); } diff --git a/frontend/src/metadata/metadata-view/store/operations/constants.js b/frontend/src/metadata/metadata-view/store/operations/constants.js index e35d0b90cc..08b92233be 100644 --- a/frontend/src/metadata/metadata-view/store/operations/constants.js +++ b/frontend/src/metadata/metadata-view/store/operations/constants.js @@ -19,7 +19,7 @@ export const OPERATION_TYPE = { export const OPERATION_ATTRIBUTES = { [OPERATION_TYPE.MODIFY_RECORD]: ['repo_id', 'row_id', 'updates', 'old_row_data', 'original_updates', 'original_old_row_data'], - [OPERATION_TYPE.MODIFY_RECORDS]: ['repo_id', 'row_ids', 'id_row_updates', 'id_original_row_updates', 'id_old_row_data', 'id_original_old_row_data', 'is_copy_paste'], + [OPERATION_TYPE.MODIFY_RECORDS]: ['repo_id', 'row_ids', 'id_row_updates', 'id_original_row_updates', 'id_old_row_data', 'id_original_old_row_data', 'is_copy_paste', 'id_obj_id'], [OPERATION_TYPE.RESTORE_RECORDS]: ['repo_id', 'rows_data', 'original_rows', 'link_infos', 'upper_row_ids'], [OPERATION_TYPE.RELOAD_RECORDS]: ['repo_id', 'row_ids'], [OPERATION_TYPE.MODIFY_FILTERS]: ['repo_id', 'view_id', 'filter_conjunction', 'filters'], diff --git a/frontend/src/metadata/metadata-view/store/server-operator.js b/frontend/src/metadata/metadata-view/store/server-operator.js index 7566fa5e18..0d5fd7777f 100644 --- a/frontend/src/metadata/metadata-view/store/server-operator.js +++ b/frontend/src/metadata/metadata-view/store/server-operator.js @@ -20,9 +20,9 @@ class ServerOperator { break; } case OPERATION_TYPE.MODIFY_RECORDS: { - const { repo_id, row_ids, id_row_updates, is_copy_paste } = operation; + const { repo_id, row_ids, id_row_updates, is_copy_paste, id_obj_id } = operation; const rowsData = row_ids.map(rowId => { - return { record_id: rowId, record: id_row_updates[rowId] }; + return { record_id: rowId, record: id_row_updates[rowId], obj_id: id_obj_id[rowId] }; }); window.sfMetadataContext.modifyRecords(repo_id, rowsData, is_copy_paste).then(res => { callback({ operation }); diff --git a/seahub/api2/endpoints/metadata_manage.py b/seahub/api2/endpoints/metadata_manage.py index 4ddc184964..3f60c76b6f 100644 --- a/seahub/api2/endpoints/metadata_manage.py +++ b/seahub/api2/endpoints/metadata_manage.py @@ -29,7 +29,7 @@ class MetadataManage(APIView): def get(self, request, repo_id): """ check the repo has enabled the metadata manage or not - """ + """ # recource check repo = seafile_api.get_repo(repo_id) if not repo: @@ -41,7 +41,7 @@ class MetadataManage(APIView): if not permission: error_msg = 'Permission denied.' return api_error(status.HTTP_403_FORBIDDEN, error_msg) - + try: record = RepoMetadata.objects.filter(repo_id=repo_id).first() if record and record.enabled: @@ -52,7 +52,7 @@ class MetadataManage(APIView): logger.error(e) error_msg = 'Internal Server Error' return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) - + return Response({'enabled': is_enabled}) def put(self, request, repo_id): @@ -119,13 +119,13 @@ class MetadataManage(APIView): if not permission: error_msg = 'Permission denied.' return api_error(status.HTTP_403_FORBIDDEN, error_msg) - + # check dose the repo have opened metadata manage record = RepoMetadata.objects.filter(repo_id=repo_id).first() if not record or not record.enabled: error_msg = f'The repo {repo_id} has disabledd the metadata manage.' return api_error(status.HTTP_409_CONFLICT, error_msg) - + metadata_server_api = MetadataServerAPI(repo_id, request.user.username) try: metadata_server_api.delete_base() @@ -133,7 +133,7 @@ class MetadataManage(APIView): logger.error(err) error_msg = 'Internal Server Error' return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) - + try: record.enabled = False record.save() @@ -142,7 +142,7 @@ class MetadataManage(APIView): logger.error(e) error_msg = 'Internal Server Error' return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) - + return Response({'success': True}) class MetadataRecords(APIView): @@ -161,7 +161,7 @@ class MetadataRecords(APIView): is_dir: optional, True or False order_by: list with string, like ['`parent_dir` ASC'] """ - + #args check view_id = request.GET.get('view_id', '') start = request.GET.get('start', 0) @@ -177,7 +177,7 @@ class MetadataRecords(APIView): if start < 0: error_msg = 'start invalid' return api_error(status.HTTP_400_BAD_REQUEST, error_msg) - + if limit < 0: error_msg = 'limit invalid' return api_error(status.HTTP_400_BAD_REQUEST, error_msg) @@ -185,7 +185,7 @@ class MetadataRecords(APIView): if not view_id: error_msg = 'view_id is invalid.' return api_error(status.HTTP_400_BAD_REQUEST, error_msg) - + # metadata enable check metadata = RepoMetadata.objects.filter(repo_id=repo_id).first() if not metadata or not metadata.enabled: @@ -203,21 +203,21 @@ class MetadataRecords(APIView): if not permission: error_msg = 'Permission denied.' return api_error(status.HTTP_403_FORBIDDEN, error_msg) - + try: view = RepoMetadataViews.objects.get_view(repo_id, view_id) except Exception as e: logger.exception(e) error_msg = 'Internal Server Error' return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) - + try: results = list_metadata_view_records(repo_id, request.user.username, view, start, limit) except Exception as err: logger.error(err) error_msg = 'Internal Server Error' return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) - + return Response(results) def put(self, request, repo_id): @@ -242,9 +242,8 @@ class MetadataRecords(APIView): return api_error(status.HTTP_404_NOT_FOUND, error_msg) metadata_server_api = MetadataServerAPI(repo_id, request.user.username) - + from seafevents.repo_metadata.utils import METADATA_TABLE - columns = [] try: columns_data = metadata_server_api.list_columns(METADATA_TABLE.id) columns = columns_data.get('columns', []) @@ -255,33 +254,70 @@ class MetadataRecords(APIView): sys_column_names = [column.get('name') for column in get_sys_columns()] - rows = [] + record_id_to_record = {} + obj_id_to_record = {} + sql = f'SELECT `_id`, `_obj_id`, `_file_modifier` FROM `{METADATA_TABLE.name}` WHERE ' + parameters = [] for record_data in records_data: - record_id = record_data.get('record_id', '') record = record_data.get('record', {}) - if record_id: - flag = False - update = { - METADATA_TABLE.columns.id.name: record_id, - } - for column_name, value in record.items(): - if column_name not in sys_column_names: - try: - column = next(column for column in columns if column['name'] == column_name) - flag = True - if value and column['type'] == 'date': - column_data = column.get('data', {}) - format = column_data.get('format', 'YYYY-MM-DD') - datetime_obj = datetime.strptime(value, '%Y-%m-%d %H:%M' if 'HH:mm' in format else '%Y-%m-%d') - update[column_name] = datetime_to_isoformat_timestr(datetime_obj) - elif column['type'] == 'single-select' and not value: - update[column_name] = None - else: - update[column_name] = value - except Exception as e: - pass - if flag: - rows.append(update) + obj_id = record_data.get('obj_id', '') + record_id = record_data.get('record_id', '') + if not record_id: + error_msg = 'record_id invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + sql += f' `{METADATA_TABLE.columns.id.name}` = ? OR ' + parameters.append(record_id) + record_id_to_record[record_id] = record + + if obj_id and obj_id != '0000000000000000000000000000000000000000': + sql += f' `{METADATA_TABLE.columns.obj_id.name}` = ? OR ' + parameters.append(obj_id) + obj_id_to_record[obj_id] = record + + sql = sql.rstrip('OR ') + sql += ';' + + try: + query_result = metadata_server_api.query_rows(sql, parameters) + except Exception as e: + logger.exception(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + results = query_result.get('results') + if not results: + # file or folder has been deleted + return Response({'success': True}) + + rows = [] + for record in results: + obj_id = record.get('_obj_id') + record_id = record.get('_id') + to_updated_record = record_id_to_record.get(record_id) + if not to_updated_record: + to_updated_record = obj_id_to_record.get(obj_id) + + update = { + METADATA_TABLE.columns.id.name: record_id, + } + for column_name, value in to_updated_record.items(): + if column_name not in sys_column_names: + try: + column = next(column for column in columns if column['name'] == column_name) + if value and column['type'] == 'date': + column_data = column.get('data', {}) + format = column_data.get('format', 'YYYY-MM-DD') + datetime_obj = datetime.strptime(value, + '%Y-%m-%d %H:%M' if 'HH:mm' in format else '%Y-%m-%d') + update[column_name] = datetime_to_isoformat_timestr(datetime_obj) + elif column['type'] == 'single-select' and not value: + update[column_name] = None + else: + update[column_name] = value + rows.append(update) + except Exception as e: + pass if rows: try: metadata_server_api.update_rows(METADATA_TABLE.id, rows) @@ -411,7 +447,7 @@ class MetadataColumns(APIView): return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) return Response({'column': column}) - + def put(self, request, repo_id): column_key = request.data.get('column_key', '') @@ -421,11 +457,11 @@ class MetadataColumns(APIView): if not column_key: error_msg = 'column_key invalid.' return api_error(status.HTTP_400_BAD_REQUEST, error_msg) - + if not column_name and not column_data: error_msg = 'params invalid.' return api_error(status.HTTP_400_BAD_REQUEST, error_msg) - + metadata = RepoMetadata.objects.filter(repo_id=repo_id).first() if not metadata or not metadata.enabled: error_msg = f'The metadata module is disabled for repo {repo_id}.' @@ -435,12 +471,12 @@ class MetadataColumns(APIView): if not repo: error_msg = 'Library %s not found.' % repo_id return api_error(status.HTTP_404_NOT_FOUND, error_msg) - + permission = check_folder_permission(request, repo_id, '/') if permission != 'rw': error_msg = 'Permission denied.' return api_error(status.HTTP_403_FORBIDDEN, error_msg) - + from seafevents.repo_metadata.utils import METADATA_TABLE, MetadataColumn metadata_server_api = MetadataServerAPI(repo_id, request.user.username) columns = metadata_server_api.list_columns(METADATA_TABLE.id).get('columns') @@ -495,7 +531,7 @@ class MetadataColumns(APIView): except Exception as e: error_msg = 'Column not found' return api_error(status.HTTP_404_NOT_FOUND, error_msg) - + try: metadata_server_api.delete_column(METADATA_TABLE.id, column_key) except Exception as e: @@ -512,7 +548,7 @@ class MetadataViews(APIView): throttle_classes = (UserRateThrottle,) def get(self, request, repo_id): - + repo = seafile_api.get_repo(repo_id) if not repo: error_msg = 'Library %s not found.' % repo_id @@ -522,7 +558,7 @@ class MetadataViews(APIView): if permission != 'rw': error_msg = 'Permission denied.' return api_error(status.HTTP_403_FORBIDDEN, error_msg) - + try: metadata_views = RepoMetadataViews.objects.list_views(repo_id) except Exception as e: @@ -531,24 +567,24 @@ class MetadataViews(APIView): return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) return Response(metadata_views) - + def post(self, request, repo_id): # Add a metadata view view_name = request.data.get('name') if not view_name: error_msg = 'view name is invalid.' return api_error(status.HTTP_400_BAD_REQUEST, error_msg) - + record = RepoMetadata.objects.filter(repo_id=repo_id).first() if not record or not record.enabled: error_msg = f'The metadata module is disabled for repo {repo_id}.' return api_error(status.HTTP_404_NOT_FOUND, error_msg) - + repo = seafile_api.get_repo(repo_id) if not repo: error_msg = 'Library %s not found.' % repo_id return api_error(status.HTTP_404_NOT_FOUND, error_msg) - + permission = check_folder_permission(request, repo_id, '/') if permission != 'rw': error_msg = 'Permission denied.' @@ -562,8 +598,8 @@ class MetadataViews(APIView): return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) return Response({'view': new_view}) - - + + def put(self, request, repo_id): # Update a metadata view, including rename, change filters and so on # by a json data @@ -575,19 +611,19 @@ class MetadataViews(APIView): if not view_data: error_msg = 'view_data is invalid.' return api_error(status.HTTP_400_BAD_REQUEST, error_msg) - + record = RepoMetadata.objects.filter(repo_id=repo_id).first() if not record or not record.enabled: error_msg = f'The metadata module is disabled for repo {repo_id}.' return api_error(status.HTTP_404_NOT_FOUND, error_msg) - + views = RepoMetadataViews.objects.filter( repo_id = repo_id, ).first() if not views: error_msg = 'The metadata views does not exists.' return api_error(status.HTTP_404_NOT_FOUND, error_msg) - + if view_id not in views.view_ids: error_msg = 'view_id %s does not exists.' % view_id return api_error(status.HTTP_400_BAD_REQUEST, error_msg) @@ -601,7 +637,7 @@ class MetadataViews(APIView): if permission != 'rw': error_msg = 'Permission denied.' return api_error(status.HTTP_403_FORBIDDEN, error_msg) - + try: result = RepoMetadataViews.objects.update_view(repo_id, view_id, view_data) except Exception as e: @@ -618,40 +654,40 @@ class MetadataViews(APIView): if not view_id: error_msg = 'view_id is invalid.' return api_error(status.HTTP_400_BAD_REQUEST, error_msg) - + record = RepoMetadata.objects.filter(repo_id=repo_id).first() if not record or not record.enabled: error_msg = f'The metadata module is disabled for repo {repo_id}.' return api_error(status.HTTP_404_NOT_FOUND, error_msg) - + views = RepoMetadataViews.objects.filter( repo_id=repo_id ).first() if not views: error_msg = 'The metadata views does not exists.' return api_error(status.HTTP_404_NOT_FOUND, error_msg) - + if view_id not in views.view_ids: error_msg = 'view_id %s does not exists.' % view_id return api_error(status.HTTP_400_BAD_REQUEST, error_msg) - + repo = seafile_api.get_repo(repo_id) if not repo: error_msg = 'Library %s not found.' % repo_id return api_error(status.HTTP_404_NOT_FOUND, error_msg) - + permission = check_folder_permission(request, repo_id, '/') if permission != 'rw': error_msg = 'Permission denied.' return api_error(status.HTTP_403_FORBIDDEN, error_msg) - + try: result = RepoMetadataViews.objects.delete_view(repo_id, view_id) except Exception as e: logger.exception(e) error_msg = 'Internal Server Error' return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) - + return Response({'success': True}) @@ -659,50 +695,50 @@ class MetadataViewsDetailView(APIView): authentication_classes = (TokenAuthentication, SessionAuthentication) permission_classes = (IsAuthenticated,) throttle_classes = (UserRateThrottle,) - + def get(self, request, repo_id, view_id): record = RepoMetadata.objects.filter(repo_id=repo_id).first() if not record or not record.enabled: error_msg = f'The metadata module is disabled for repo {repo_id}.' return api_error(status.HTTP_404_NOT_FOUND, error_msg) - + repo = seafile_api.get_repo(repo_id) if not repo: error_msg = 'Library %s not found.' % repo_id return api_error(status.HTTP_404_NOT_FOUND, error_msg) - + permission = check_folder_permission(request, repo_id, '/') if permission != 'rw': error_msg = 'Permission denied.' return api_error(status.HTTP_403_FORBIDDEN, error_msg) - + try: view = RepoMetadataViews.objects.get_view(repo_id, view_id) except Exception as e: logger.exception(e) error_msg = 'Internal Server Error' return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) - + return Response({'view': view}) - + class MetadataViewsMoveView(APIView): authentication_classes = (TokenAuthentication, SessionAuthentication) permission_classes = (IsAuthenticated,) throttle_classes = (UserRateThrottle,) - + def post(self, request, repo_id): # put view_id in front of target_view_id view_id = request.data.get('view_id') if not view_id: error_msg = 'view_id is invalid.' return api_error(status.HTTP_400_BAD_REQUEST, error_msg) - + target_view_id = request.data.get('target_view_id') if not target_view_id: error_msg = 'target_view_id is invalid.' return api_error(status.HTTP_400_BAD_REQUEST, error_msg) - + record = RepoMetadata.objects.filter(repo_id=repo_id).first() if not record or not record.enabled: error_msg = f'The metadata module is disabled for repo {repo_id}.' @@ -718,25 +754,25 @@ class MetadataViewsMoveView(APIView): if view_id not in views.view_ids: error_msg = 'view_id %s does not exists.' % view_id return api_error(status.HTTP_400_BAD_REQUEST, error_msg) - + if target_view_id not in views.view_ids: error_msg = 'target_view_id %s does not exists.' % target_view_id return api_error(status.HTTP_400_BAD_REQUEST, error_msg) - + repo = seafile_api.get_repo(repo_id) if not repo: error_msg = 'Library %s not found.' % repo_id return api_error(status.HTTP_404_NOT_FOUND, error_msg) - + permission = check_folder_permission(request, repo_id, '/') if permission != 'rw': error_msg = 'Permission denied.' return api_error(status.HTTP_403_FORBIDDEN, error_msg) - + try: results = RepoMetadataViews.objects.move_view(repo_id, view_id, target_view_id) except Exception as e: error_msg = 'Internal Server Error' return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) - + return Response({'navigation': results['navigation']}) diff --git a/seahub/repo_metadata/utils.py b/seahub/repo_metadata/utils.py index f977fa2cdd..e5f50e2280 100644 --- a/seahub/repo_metadata/utils.py +++ b/seahub/repo_metadata/utils.py @@ -43,7 +43,11 @@ def get_sys_columns(): METADATA_TABLE.columns.file_name.to_dict(), METADATA_TABLE.columns.is_dir.to_dict(), METADATA_TABLE.columns.file_type.to_dict(), - METADATA_TABLE.columns.location.to_dict() + METADATA_TABLE.columns.location.to_dict(), + METADATA_TABLE.columns.obj_id.to_dict(), + METADATA_TABLE.columns.size.to_dict(), + METADATA_TABLE.columns.suffix.to_dict(), + METADATA_TABLE.columns.file_details.to_dict(), ] return columns