Skip to content

Commit

Permalink
ufal/fe-22-54-download-preview-item (#362)
Browse files Browse the repository at this point in the history
* create new component and add model file and service file

* build the user interface and calling the api from backend

* add logic for handling data from BE and refactor the UI

* fix the lint error and fix the collapse/hide behaviour

* add UI for download buttons and fix UI for the ZIP Preview

* getting data from backend and make the download feature

* handle with the dynamic input

* fix the collapese/hide behaviour and handle more cases

* add more conditions for checking permission to preview and download files

* fix lint error

* fix failed test

* Lock/download preview (#311)

* create new component and add model file and service file

* build the user interface and calling the api from backend

* add logic for handling data from BE and refactor the UI

* fix the lint error and fix the collapse/hide behaviour

---------

Co-authored-by: HuynhKhoa1601 <[email protected]>

* Resolved conflicts

* Revert unnecessary changes

* Refactoring and removed font awesome loading from node modules

* Updated preview file box look and feel

* Fixed file preview icons

* Fixed html preview scrolling

* Show scrollbar in txt preview

* Some refactoring

* Refactoring and the dspace BE is loaded from configuration property.

* Added messages

* Fixed tests

* Added download buttons and fixed downloading

* Added messages

* Added image to the assetstore instead of url from lindat

---------

Co-authored-by: HuynhKhoa1601 <[email protected]>
  • Loading branch information
milanmajchrak and HuynhKhoa1601 authored Oct 4, 2023
1 parent 774e409 commit 80394f1
Show file tree
Hide file tree
Showing 33 changed files with 1,293 additions and 44 deletions.
4 changes: 4 additions & 0 deletions src/app/core/core.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import { EPerson } from './eperson/models/eperson.model';
import { Group } from './eperson/models/group.model';
import { JsonPatchOperationsBuilder } from './json-patch/builder/json-patch-operations-builder';
import { MetadataField } from './metadata/metadata-field.model';
import { MetadataBitstream } from './metadata/metadata-bitstream.model';
import { MetadataSchema } from './metadata/metadata-schema.model';
import { MetadataService } from './metadata/metadata.service';
import { RegistryService } from './registry/registry.service';
Expand Down Expand Up @@ -133,6 +134,7 @@ import {
import { Registration } from './shared/registration.model';
import { MetadataSchemaDataService } from './data/metadata-schema-data.service';
import { MetadataFieldDataService } from './data/metadata-field-data.service';
import { MetadataBitstreamDataService } from './data/metadata-bitstream-data.service';
import { DsDynamicTypeBindRelationService } from '../shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service';
import { TokenResponseParsingService } from './auth/token-response-parsing.service';
import { SubmissionCcLicenseDataService } from './submission/submission-cc-license-data.service';
Expand Down Expand Up @@ -298,6 +300,7 @@ const PROVIDERS = [
SiteRegisterGuard,
MetadataSchemaDataService,
MetadataFieldDataService,
MetadataBitstreamDataService,
TokenResponseParsingService,
ReloadGuard,
EndUserAgreementCurrentUserGuard,
Expand Down Expand Up @@ -341,6 +344,7 @@ export const models =
ResourcePolicy,
MetadataSchema,
MetadataField,
MetadataBitstream,
License,
WorkflowItem,
WorkspaceItem,
Expand Down
76 changes: 76 additions & 0 deletions src/app/core/data/metadata-bitstream-data.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { Injectable } from '@angular/core';
import { hasValue } from '../../shared/empty.util';
import { RemoteData } from './remote-data';
import { RequestService } from './request.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { HALEndpointService } from '../shared/hal-endpoint.service';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { Observable } from 'rxjs';
import { RequestParam } from '../cache/models/request-param.model';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { ObjectCacheService } from '../cache/object-cache.service';
import { METADATA_BITSTREAM } from '../metadata/metadata-bitstream.resource-type';
import { MetadataBitstream } from '../metadata/metadata-bitstream.model';
import { HttpClient } from '@angular/common/http';
import { Store } from '@ngrx/store';
import { ChangeAnalyzer } from './change-analyzer';
import { IdentifiableDataService } from './base/identifiable-data.service';
import { SearchData, SearchDataImpl } from './base/search-data';
import { CoreState } from '../core-state.model';
import { dataService } from './base/data-service.decorator';
import { FindListOptions } from './find-list-options.model';
import { linkName } from './clarin/clarin-license-data.service';
import { PaginatedList } from './paginated-list.model';

/**
* A service responsible for fetching/sending data from/to the REST API on the metadatafields endpoint
*/
@Injectable()
@dataService(METADATA_BITSTREAM)
export class MetadataBitstreamDataService extends IdentifiableDataService<MetadataBitstream> implements SearchData<MetadataBitstream> {
protected store: Store<CoreState>;
protected http: HttpClient;
protected comparator: ChangeAnalyzer<MetadataBitstream>;
protected linkPath = 'metadatabitstreams';
protected searchByHandleLinkPath = 'byHandle';
private searchData: SearchData<MetadataBitstream>;

constructor(
protected requestService: RequestService,
protected rdbService: RemoteDataBuildService,
protected objectCache: ObjectCacheService,
protected halService: HALEndpointService,
protected notificationsService: NotificationsService
) {
super(linkName, requestService, rdbService, objectCache, halService, undefined);
this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
}

/**
* Find metadata fields with either the partial metadata field name (e.g. "dc.ti") as query or an exact match to
* at least the schema, element or qualifier
* @param handle optional; an exact match of the prefix of the item identifier (e.g. "123456789/1126")
* @param fileGrpType optional; an exact match of the type of the file(e.g. "TEXT", "THUMBNAIL")
* @param options The options info used to retrieve the fields
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
*/
searchByHandleParams(handle: string, fileGrpType: string, options: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<any>[]): Observable<RemoteData<any>> {
const optionParams = Object.assign(new FindListOptions(), options, {
searchParams: [
new RequestParam('handle', hasValue(handle) ? handle : ''),
new RequestParam(
'fileGrpType',
hasValue(fileGrpType) ? fileGrpType : ''
),
],
});
return this.searchBy(this.searchByHandleLinkPath, optionParams, useCachedVersionIfAvailable, reRequestOnStale,
...linksToFollow);
}

searchBy(searchMethod: string, options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<MetadataBitstream>[]): Observable<RemoteData<PaginatedList<MetadataBitstream>>> {
return this.searchData.searchBy(searchMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
}
12 changes: 12 additions & 0 deletions src/app/core/metadata/file-info.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { autoserialize, autoserializeAs } from 'cerialize';

/**
* This class is used to store the information about a file or a directory
*/
export class FileInfo {
@autoserialize name: string;
@autoserialize content: any;
@autoserialize size: string;
@autoserialize isDirectory: boolean;
@autoserializeAs('sub') sub: { [key: string]: FileInfo };
}
97 changes: 97 additions & 0 deletions src/app/core/metadata/metadata-bitstream.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import {
autoserialize,
autoserializeAs,
deserialize,
} from 'cerialize';
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
import { typedObject } from '../cache/builders/build-decorators';
import { GenericConstructor } from '../shared/generic-constructor';
import { HALLink } from '../shared/hal-link.model';
import { HALResource } from '../shared/hal-resource.model';
import { ResourceType } from '../shared/resource-type';
import { excludeFromEquals } from '../utilities/equals.decorators';
import { METADATA_BITSTREAM } from './metadata-bitstream.resource-type';
import { FileInfo } from './file-info.model';

/**
* Class that represents a MetadataBitstream
*/
@typedObject
export class MetadataBitstream extends ListableObject implements HALResource {
static type = METADATA_BITSTREAM;

/**
* The object type
*/
@excludeFromEquals
@autoserialize
type: ResourceType;

/**
* The identifier of this metadata field
*/
@autoserialize
id: number;

/**
* The name of this bitstream
*/
@autoserialize
name: string;

/**
* The description of this bitstream
*/
@autoserialize
description: string;

/**
* The fileSize of this bitstream
*/
@autoserialize
fileSize: string;

/**
* The checksum of this bitstream
*/
@autoserialize
checksum: string;

/**
* The fileInfo of this bitstream
*/
@autoserializeAs(FileInfo, 'fileInfo') fileInfo: FileInfo[];

/**
* The format of this bitstream
*/
@autoserialize
format: string;

/**
* The href of this bitstream
*/
@autoserialize
href: string;

/**
* The canPreview of this bitstream
*/
@autoserialize
canPreview: boolean;

/**
* The {@link HALLink}s for this MetadataField
*/
@deserialize
_links: {
self: HALLink;
schema: HALLink;
};

getRenderTypes(): (string | GenericConstructor<ListableObject>)[] {
return [this.constructor as GenericConstructor<ListableObject>];
}
}
export { FileInfo };

9 changes: 9 additions & 0 deletions src/app/core/metadata/metadata-bitstream.resource-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ResourceType } from '../shared/resource-type';

/**
* The resource type for MetadataBitstream
*
* Needs to be in a separate file to prevent circular
* dependencies in webpack.
*/
export const METADATA_BITSTREAM = new ResourceType('metadatabitstream');
14 changes: 13 additions & 1 deletion src/app/core/registry/registry.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Component } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { Store, StoreModule } from '@ngrx/store';
import { TranslateModule } from '@ngx-translate/core';
import { Observable, of as observableOf } from 'rxjs';
import { Observable, of as observableOf, of } from 'rxjs';
import {
MetadataRegistryCancelFieldAction,
MetadataRegistryCancelSchemaAction,
Expand All @@ -30,6 +30,7 @@ import { createPaginatedList } from '../../shared/testing/utils.test';
import { RemoteData } from '../data/remote-data';
import { NoContent } from '../shared/NoContent.model';
import { FindListOptions } from '../data/find-list-options.model';
import { MetadataBitstreamDataService } from '../data/metadata-bitstream-data.service';

@Component({ template: '' })
class DummyComponent {
Expand All @@ -40,6 +41,7 @@ describe('RegistryService', () => {
let mockStore;
let metadataSchemaService: MetadataSchemaDataService;
let metadataFieldService: MetadataFieldDataService;
let metadataBitstreamDataService: MetadataBitstreamDataService;

let options: FindListOptions;
let mockSchemasList: MetadataSchema[];
Expand Down Expand Up @@ -139,6 +141,14 @@ describe('RegistryService', () => {
delete: createNoContentRemoteDataObject$(),
clearRequests: observableOf('href')
});
metadataBitstreamDataService = jasmine.createSpyObj(
'metadataBitstreamDataService',
{
searchByHandleParams: of({
/* Your Mock Data */
}),
}
);
}

beforeEach(() => {
Expand All @@ -153,6 +163,8 @@ describe('RegistryService', () => {
{ provide: NotificationsService, useValue: new NotificationsServiceStub() },
{ provide: MetadataSchemaDataService, useValue: metadataSchemaService },
{ provide: MetadataFieldDataService, useValue: metadataFieldService },
{ provide: MetadataFieldDataService, useValue: metadataFieldService },
{ provide: MetadataBitstreamDataService, useValue: metadataBitstreamDataService },
RegistryService
]
});
Expand Down
22 changes: 20 additions & 2 deletions src/app/core/registry/registry.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@ import { MetadataSchema } from '../metadata/metadata-schema.model';
import { MetadataField } from '../metadata/metadata-field.model';
import { MetadataSchemaDataService } from '../data/metadata-schema-data.service';
import { MetadataFieldDataService } from '../data/metadata-field-data.service';
import { MetadataBitstreamDataService } from '../data/metadata-bitstream-data.service';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { RequestParam } from '../cache/models/request-param.model';
import { NoContent } from '../shared/NoContent.model';
import { FindListOptions } from '../data/find-list-options.model';
import { MetadataBitstream } from '../metadata/metadata-bitstream.model';

const metadataRegistryStateSelector = (state: AppState) => state.metadataRegistry;
const editMetadataSchemaSelector = createSelector(metadataRegistryStateSelector, (metadataState: MetadataRegistryState) => metadataState.editSchema);
Expand All @@ -42,12 +44,12 @@ const selectedMetadataFieldsSelector = createSelector(metadataRegistryStateSelec
*/
@Injectable()
export class RegistryService {

constructor(private store: Store<AppState>,
private notificationsService: NotificationsService,
private translateService: TranslateService,
private metadataSchemaService: MetadataSchemaDataService,
private metadataFieldService: MetadataFieldDataService) {
private metadataFieldService: MetadataFieldDataService,
private metadataBitstreamDataService: MetadataBitstreamDataService) {

}

Expand Down Expand Up @@ -104,6 +106,22 @@ export class RegistryService {
return this.metadataFieldService.findBySchema(schema, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}

/**
* retrieves all metadatabistream that belong to a certain metadata
* @param schema The schema to filter by
* @param options The options info used to retrieve the fields
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
*/
public getMetadataBitstream(handle: string, fileGrpType: string, options: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<MetadataBitstream>[]): Observable<RemoteData<any>> {
return this.metadataBitstreamDataService.searchByHandleParams(handle, fileGrpType, options,
useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}

public editMetadataSchema(schema: MetadataSchema) {
this.store.dispatch(new MetadataRegistryEditSchemaAction(schema));
}
Expand Down
1 change: 0 additions & 1 deletion src/app/core/shared/clarin/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,3 @@ export const DOWNLOAD_TOKEN_EXPIRED_EXCEPTION = 'DownloadTokenExpiredException';
export const HTTP_STATUS_UNAUTHORIZED = 401;
export const USER_WITHOUT_EMAIL_EXCEPTION = 'UserWithoutEmailException';
export const MISSING_HEADERS_FROM_IDP_EXCEPTION = 'MissingHeadersFromIpd';

Loading

0 comments on commit 80394f1

Please sign in to comment.