diff --git a/package.json b/package.json index 057cdbe06c..dc29f61aba 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "@ngx-translate/core": "8.0.0", "@ngx-translate/http-loader": "2.0.0", "body-parser": "1.18.2", - "bootstrap": "4.0.0-alpha.6", + "bootstrap": "v4.0.0-beta", "cerialize": "0.1.16", "compression": "1.7.1", "cookie-parser": "1.4.3", diff --git a/resources/i18n/en.json b/resources/i18n/en.json index 9714640287..c605d76961 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -69,5 +69,14 @@ "head": "Communities in DSpace", "help": "Select a community to browse its collections." } + }, + "search": { + "form": { + "search": "Search", + "search_dspace": "Search DSpace" + }, + "results": { + "title": "Search Results" + } } } diff --git a/src/app/+collection-page/collection-page.component.html b/src/app/+collection-page/collection-page.component.html index a8345ef23d..b1aae17f7c 100644 --- a/src/app/+collection-page/collection-page.component.html +++ b/src/app/+collection-page/collection-page.component.html @@ -32,13 +32,9 @@
-
+

{{'collection.page.browse.recent.head' | translate}}

- +
diff --git a/src/app/+collection-page/collection-page.component.ts b/src/app/+collection-page/collection-page.component.ts index 44c45edc4f..ca349ce5bc 100644 --- a/src/app/+collection-page/collection-page.component.ts +++ b/src/app/+collection-page/collection-page.component.ts @@ -14,90 +14,81 @@ import { ItemDataService } from '../core/data/item-data.service'; import { Item } from '../core/shared/item.model'; import { SortOptions, SortDirection } from '../core/cache/models/sort-options.model'; import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model'; -import { hasValue, isUndefined } from '../shared/empty.util'; +import { hasValue, isNotEmpty, isUndefined } from '../shared/empty.util'; import { PageInfo } from '../core/shared/page-info.model'; +import { Observable } from 'rxjs/Observable'; @Component({ selector: 'ds-collection-page', styleUrls: ['./collection-page.component.scss'], templateUrl: './collection-page.component.html', - changeDetection: ChangeDetectionStrategy.OnPush }) export class CollectionPageComponent implements OnInit, OnDestroy { collectionData: RemoteData; itemData: RemoteData; logoData: RemoteData; - config: PaginationComponentOptions; + paginationConfig: PaginationComponentOptions; sortConfig: SortOptions; private subs: Subscription[] = []; private collectionId: string; - private pageInfoState: PageInfo; - - constructor( - private collectionDataService: CollectionDataService, - private itemDataService: ItemDataService, - private ref: ChangeDetectorRef, - private route: ActivatedRoute - ) { + constructor(private collectionDataService: CollectionDataService, + private itemDataService: ItemDataService, + private route: ActivatedRoute) { + this.paginationConfig = new PaginationComponentOptions(); + this.paginationConfig.id = 'collection-page-pagination'; + this.paginationConfig.pageSizeOptions = [4]; + this.paginationConfig.pageSize = 4; + this.paginationConfig.currentPage = 1; + this.sortConfig = new SortOptions(); } ngOnInit(): void { - this.subs.push(this.route.params.map((params: Params) => params.id) - .subscribe((id: string) => { - this.collectionId = id; - this.collectionData = this.collectionDataService.findById(this.collectionId); - this.subs.push(this.collectionData.payload.subscribe((collection) => this.logoData = collection.logo)); + this.subs.push( + Observable.combineLatest( + this.route.params, + this.route.queryParams, + (params, queryParams,) => { + return Object.assign({}, params, queryParams); + }) + .subscribe((params) => { + this.collectionId = params.id; + this.collectionData = this.collectionDataService.findById(this.collectionId); + this.subs.push(this.collectionData.payload.subscribe((collection) => this.logoData = collection.logo)); - this.config = new PaginationComponentOptions(); - this.config.id = 'collection-browse'; - this.config.pageSizeOptions = [4]; - this.config.pageSize = 4; - this.sortConfig = new SortOptions(); + const page = +params.page || this.paginationConfig.currentPage; + const pageSize = +params.pageSize || this.paginationConfig.pageSize; + const sortDirection = +params.page || this.sortConfig.direction; + const pagination = Object.assign({}, + this.paginationConfig, + { currentPage: page, pageSize: pageSize } + ); + const sort = Object.assign({}, + this.sortConfig, + { direction: sortDirection, field: params.sortField } + ); + this.updatePage({ + pagination: pagination, + sort: sort + }); + })); - this.updateResults(); - })); + } + updatePage(searchOptions) { + this.itemData = this.itemDataService.findAll({ + scopeID: this.collectionId, + currentPage: searchOptions.pagination.currentPage, + elementsPerPage: searchOptions.pagination.pageSize, + sort: searchOptions.sort + }); } ngOnDestroy(): void { this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe()); } - onPageChange(currentPage: number): void { - this.config.currentPage = currentPage; - this.updateResults(); - } - - onPageSizeChange(elementsPerPage: number): void { - this.config.pageSize = elementsPerPage; - this.updateResults(); - } - - onSortDirectionChange(sortDirection: SortDirection): void { - this.sortConfig = new SortOptions(this.sortConfig.field, sortDirection); - this.updateResults(); - } - - onSortFieldChange(field: string): void { - this.sortConfig = new SortOptions(field, this.sortConfig.direction); - this.updateResults(); - } - - updateResults() { - this.itemData = null; - this.ref.markForCheck(); - this.itemData = this.itemDataService.findAll({ - scopeID: this.collectionId, - currentPage: this.config.currentPage, - elementsPerPage: this.config.pageSize, - sort: this.sortConfig - }); - this.subs.push(this.itemData.pageInfo.subscribe((pageInfo) => { - if (isUndefined(this.pageInfoState) || this.pageInfoState !== pageInfo) { - this.pageInfoState = pageInfo; - this.ref.detectChanges(); - } - })); + isNotEmpty(object: any) { + return isNotEmpty(object); } } diff --git a/src/app/+home/home-news/home-news.component.scss b/src/app/+home/home-news/home-news.component.scss index a1b1c66516..c1c16995c5 100644 --- a/src/app/+home/home-news/home-news.component.scss +++ b/src/app/+home/home-news/home-news.component.scss @@ -1,7 +1,10 @@ -@import '../../../styles/mixins.scss'; +@import '../../../styles/variables.scss'; :host { display: block; - @include negate-gutters(); + margin-right: ($grid-gutter-width / -2); + margin-left: ($grid-gutter-width / -2); + margin-top: -$content-spacing; + margin-bottom: -$content-spacing; } .dspace-logo-container { diff --git a/src/app/+home/home.component.html b/src/app/+home/home.component.html index fd7d4b6309..acd6f0d7eb 100644 --- a/src/app/+home/home.component.html +++ b/src/app/+home/home.component.html @@ -1,2 +1,3 @@ + diff --git a/src/app/+home/top-level-community-list/top-level-community-list.component.html b/src/app/+home/top-level-community-list/top-level-community-list.component.html index 625cb5118d..772eb66012 100644 --- a/src/app/+home/top-level-community-list/top-level-community-list.component.html +++ b/src/app/+home/top-level-community-list/top-level-community-list.component.html @@ -2,9 +2,5 @@

{{'home.top-level-communities.head' | translate}}

{{'home.top-level-communities.help' | translate}}

+ [objects]="topLevelCommunities" [hideGear]="true" (paginationChange)="updatePage($event)"> diff --git a/src/app/+home/top-level-community-list/top-level-community-list.component.ts b/src/app/+home/top-level-community-list/top-level-community-list.component.ts index 259ea4cf5a..a3882d7036 100644 --- a/src/app/+home/top-level-community-list/top-level-community-list.component.ts +++ b/src/app/+home/top-level-community-list/top-level-community-list.component.ts @@ -5,58 +5,40 @@ import { CommunityDataService } from '../../core/data/community-data.service'; import { Community } from '../../core/shared/community.model'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { SortOptions, SortDirection } from '../../core/cache/models/sort-options.model'; +import { ActivatedRoute } from '@angular/router'; @Component({ selector: 'ds-top-level-community-list', styleUrls: ['./top-level-community-list.component.scss'], templateUrl: './top-level-community-list.component.html', - changeDetection: ChangeDetectionStrategy.OnPush }) -export class TopLevelCommunityListComponent implements OnInit { + +export class TopLevelCommunityListComponent { topLevelCommunities: RemoteData; config: PaginationComponentOptions; sortConfig: SortOptions; - constructor( - private cds: CommunityDataService, - private ref: ChangeDetectorRef - ) { - - } - - ngOnInit(): void { + constructor(private cds: CommunityDataService) { this.config = new PaginationComponentOptions(); this.config.id = 'top-level-pagination'; this.config.pageSizeOptions = [4]; this.config.pageSize = 4; + this.config.currentPage = 1; this.sortConfig = new SortOptions(); - this.updateResults(); + this.updatePage({ + page: this.config.currentPage, + pageSize: this.config.pageSize, + sortField: this.sortConfig.field, + direction: this.sortConfig.direction + }); } - onPageChange(currentPage: number): void { - this.config.currentPage = currentPage; - this.updateResults(); - } - - onPageSizeChange(elementsPerPage: number): void { - this.config.pageSize = elementsPerPage; - this.updateResults(); - } - - onSortDirectionChange(sortDirection: SortDirection): void { - this.sortConfig = new SortOptions(this.sortConfig.field, sortDirection); - this.updateResults(); - } - - onSortFieldChange(field: string): void { - this.sortConfig = new SortOptions(field, this.sortConfig.direction); - this.updateResults(); - } - - updateResults() { - this.topLevelCommunities = undefined; - this.topLevelCommunities = this.cds.findAll({ currentPage: this.config.currentPage, elementsPerPage: this.config.pageSize, sort: this.sortConfig }); - // this.ref.detectChanges(); + updatePage(data) { + this.topLevelCommunities = this.cds.findAll({ + currentPage: data.page, + elementsPerPage: data.pageSize, + sort: { field: data.sortField, direction: data.sortDirection } + }); } } diff --git a/src/app/+item-page/full/field-components/file-section/full-file-section.component.scss b/src/app/+item-page/full/field-components/file-section/full-file-section.component.scss index 4597c711ff..77db5d97cf 100644 --- a/src/app/+item-page/full/field-components/file-section/full-file-section.component.scss +++ b/src/app/+item-page/full/field-components/file-section/full-file-section.component.scss @@ -1,5 +1,5 @@ -@import '../../../../../styles/variables.scss'; -@import '../../../../../../node_modules/bootstrap/scss/_variables.scss'; +@import '../../../../../styles/variables'; +@import '../../../../../styles/mixins'; @media screen and (min-width: map-get($grid-breakpoints, md)) { dt { text-align: right; diff --git a/src/app/+item-page/full/full-item-page.component.html b/src/app/+item-page/full/full-item-page.component.html index b0b1f98037..59511d7d9d 100644 --- a/src/app/+item-page/full/full-item-page.component.html +++ b/src/app/+item-page/full/full-item-page.component.html @@ -2,7 +2,7 @@ diff --git a/src/app/+item-page/simple/item-page.component.html b/src/app/+item-page/simple/item-page.component.html index 9378c3839b..94ab99482f 100644 --- a/src/app/+item-page/simple/item-page.component.html +++ b/src/app/+item-page/simple/item-page.component.html @@ -16,7 +16,7 @@
- + {{"item.page.link.full" | translate}}
diff --git a/src/app/app.component.scss b/src/app/app.component.scss index a000423c34..d274f5fe4a 100644 --- a/src/app/app.component.scss +++ b/src/app/app.component.scss @@ -23,4 +23,6 @@ html { .main-content { flex: 1 0 auto; + margin-top: $content-spacing; + margin-bottom: $content-spacing; } diff --git a/src/app/core/cache/builders/remote-data-build.service.ts b/src/app/core/cache/builders/remote-data-build.service.ts index d01959d3dd..f170e342c0 100644 --- a/src/app/core/cache/builders/remote-data-build.service.ts +++ b/src/app/core/cache/builders/remote-data-build.service.ts @@ -14,6 +14,7 @@ import { GenericConstructor } from '../../shared/generic-constructor'; import { getMapsTo, getRelationMetadata, getRelationships } from './build-decorators'; import { NormalizedObjectFactory } from '../models/normalized-object-factory'; import { Request } from '../../data/request.models'; +import { PageInfo } from '../../shared/page-info.model'; @Injectable() export class RemoteDataBuildService { @@ -21,13 +22,12 @@ export class RemoteDataBuildService { protected objectCache: ObjectCacheService, protected responseCache: ResponseCacheService, protected requestService: RequestService + ) { } - buildSingle( - href: string, - normalizedType: GenericConstructor - ): RemoteData { + buildSingle(href: string, + normalizedType: GenericConstructor): RemoteData { const requestHrefObs = this.objectCache.getRequestHrefBySelfLink(href); const requestObs = Observable.race( @@ -61,6 +61,13 @@ export class RemoteDataBuildService { const pageInfo = responseCacheObs .filter((entry: ResponseCacheEntry) => hasValue(entry.response) && hasValue(entry.response['pageInfo'])) .map((entry: ResponseCacheEntry) => (entry.response as SuccessResponse).pageInfo) + .map((pInfo: PageInfo) => { + if (isNotEmpty(pageInfo) && pInfo.currentPage >= 0) { + return Object.assign({}, pInfo, {currentPage: pInfo.currentPage + 1}); + } else { + return pInfo; + } + }) .distinctUntilChanged(); /* tslint:enable:no-string-literal */ @@ -104,10 +111,8 @@ export class RemoteDataBuildService { ); } - buildList( - href: string, - normalizedType: GenericConstructor - ): RemoteData { + buildList(href: string, + normalizedType: GenericConstructor): RemoteData { const requestObs = this.requestService.get(href) .filter((entry) => hasValue(entry)); const responseCacheObs = this.responseCache.get(href).filter((entry) => hasValue(entry)); @@ -237,7 +242,7 @@ export class RemoteDataBuildService { }) .filter((e) => hasValue(e)) .join(', ') - ); + ); const statusCode = Observable.combineLatest( ...input.map((rd) => rd.statusCode), @@ -249,7 +254,7 @@ export class RemoteDataBuildService { }) .filter((c) => hasValue(c)) .join(', ') - ); + ); const pageInfo = Observable.of(undefined); diff --git a/src/app/core/shared/dspace-object.model.ts b/src/app/core/shared/dspace-object.model.ts index ad3c2dac8c..8584c179dc 100644 --- a/src/app/core/shared/dspace-object.model.ts +++ b/src/app/core/shared/dspace-object.model.ts @@ -3,11 +3,12 @@ import { isEmpty, isNotEmpty } from '../../shared/empty.util'; import { CacheableObject } from '../cache/object-cache.reducer'; import { RemoteData } from '../data/remote-data'; import { ResourceType } from './resource-type'; +import { ListableObject } from '../../object-list/listable-object/listable-object.model'; /** * An abstract model class for a DSpaceObject. */ -export abstract class DSpaceObject implements CacheableObject { +export abstract class DSpaceObject implements CacheableObject, ListableObject { self: string; diff --git a/src/app/footer/footer.component.scss b/src/app/footer/footer.component.scss index 4a409af122..fec6473f68 100644 --- a/src/app/footer/footer.component.scss +++ b/src/app/footer/footer.component.scss @@ -1,6 +1,5 @@ @import '../../styles/variables.scss'; -@import '../../../node_modules/bootstrap/scss/_variables.scss'; -$footer-bg: $gray-lighter; +$footer-bg: $gray-100; $footer-border: 1px solid darken($footer-bg, 10%); $footer-padding: $spacer * 1.5; diff --git a/src/app/header/header.component.html b/src/app/header/header.component.html index 0d8362698a..0cdb443b8a 100644 --- a/src/app/header/header.component.html +++ b/src/app/header/header.component.html @@ -1,11 +1,11 @@
-