mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
Merge remote-tracking branch 'origin/main' into CST-5668
This commit is contained in:
@@ -25,7 +25,7 @@ services:
|
||||
### OVERRIDE default 'entrypoint' in 'docker-compose-rest.yml' ####
|
||||
# Ensure that the database is ready BEFORE starting tomcat
|
||||
# 1. While a TCP connection to dspacedb port 5432 is not available, continue to sleep
|
||||
# 2. Then, run database migration to init database tables
|
||||
# 2. Then, run database migration to init database tables (including any out-of-order ignored migrations, if any)
|
||||
# 3. (Custom for Entities) enable Entity-specific collection submission mappings in item-submission.xml
|
||||
# This 'sed' command inserts the sample configurations specific to the Entities data set, see:
|
||||
# https://github.com/DSpace/DSpace/blob/main/dspace/config/item-submission.xml#L36-L49
|
||||
@@ -35,7 +35,7 @@ services:
|
||||
- '-c'
|
||||
- |
|
||||
while (!</dev/tcp/dspacedb/5432) > /dev/null 2>&1; do sleep 1; done;
|
||||
/dspace/bin/dspace database migrate
|
||||
/dspace/bin/dspace database migrate ignored
|
||||
sed -i '/name-map collection-handle="default".*/a \\n <name-map collection-handle="123456789/3" submission-name="Publication"/> \
|
||||
<name-map collection-handle="123456789/4" submission-name="Publication"/> \
|
||||
<name-map collection-handle="123456789/281" submission-name="Publication"/> \
|
||||
|
@@ -46,14 +46,14 @@ services:
|
||||
- solr_configs:/dspace/solr
|
||||
# Ensure that the database is ready BEFORE starting tomcat
|
||||
# 1. While a TCP connection to dspacedb port 5432 is not available, continue to sleep
|
||||
# 2. Then, run database migration to init database tables
|
||||
# 2. Then, run database migration to init database tables (including any out-of-order ignored migrations, if any)
|
||||
# 3. Finally, start Tomcat
|
||||
entrypoint:
|
||||
- /bin/bash
|
||||
- '-c'
|
||||
- |
|
||||
while (!</dev/tcp/dspacedb/5432) > /dev/null 2>&1; do sleep 1; done;
|
||||
/dspace/bin/dspace database migrate
|
||||
/dspace/bin/dspace database migrate ignored
|
||||
catalina.sh run
|
||||
# DSpace database container
|
||||
# NOTE: This is customized to use our loadsql image, so that we are using a database with existing test data
|
||||
|
@@ -18,8 +18,8 @@ import { ItemAdminSearchResultGridElementComponent } from './item-admin-search-r
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/remote-data.utils';
|
||||
import { getMockThemeService } from '../../../../../shared/mocks/theme-service.mock';
|
||||
import { ThemeService } from '../../../../../shared/theme-support/theme.service';
|
||||
import { AccessStatusDataService } from 'src/app/core/data/access-status-data.service';
|
||||
import { AccessStatusObject } from 'src/app/shared/object-list/access-status-badge/access-status.model';
|
||||
import { AccessStatusDataService } from '../../../../../core/data/access-status-data.service';
|
||||
import { AccessStatusObject } from '../../../../../shared/object-list/access-status-badge/access-status.model';
|
||||
|
||||
describe('ItemAdminSearchResultGridElementComponent', () => {
|
||||
let component: ItemAdminSearchResultGridElementComponent;
|
||||
|
@@ -7,13 +7,11 @@
|
||||
class="lead"
|
||||
[innerHTML]="firstMetadataValue('organization.legalName')"></span>
|
||||
<span class="text-muted">
|
||||
<ds-truncatable-part [id]="dso.id" [minLines]="3">
|
||||
<span *ngIf="dso.allMetadata(['dc.description']).length > 0"
|
||||
class="item-list-org-unit-description">
|
||||
<ds-truncatable-part [id]="dso.id" [minLines]="3"><span
|
||||
[innerHTML]="firstMetadataValue('dc.description')"></span>
|
||||
</ds-truncatable-part>
|
||||
</span>
|
||||
</ds-truncatable-part>
|
||||
</span>
|
||||
</ds-truncatable>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { NgbTooltipModule, NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
import { SharedModule } from '../../shared/shared.module';
|
||||
import { EditItemPageRoutingModule } from './edit-item-page.routing.module';
|
||||
@@ -48,7 +48,8 @@ import { ResourcePoliciesModule } from '../../shared/resource-policies/resource-
|
||||
EditItemPageRoutingModule,
|
||||
SearchPageModule,
|
||||
DragDropModule,
|
||||
ResourcePoliciesModule
|
||||
ResourcePoliciesModule,
|
||||
NgbModule
|
||||
],
|
||||
declarations: [
|
||||
EditItemPageComponent,
|
||||
|
@@ -1,13 +1,33 @@
|
||||
<div class="container">
|
||||
<ds-alert [type]="'alert-info'" [content]="'item.edit.authorizations.heading'"></ds-alert>
|
||||
<ds-resource-policies [resourceType]="'item'" [resourceUUID]="(getItemUUID() | async)"></ds-resource-policies>
|
||||
<ng-container *ngFor="let bundle of (getItemBundles() | async); trackById">
|
||||
<ds-resource-policies [resourceType]="'bundle'"
|
||||
[resourceUUID]="bundle.id"></ds-resource-policies>
|
||||
<ng-container *ngFor="let bitstream of (bundleBitstreamsMap.get(bundle.id) | async)?.page; trackById">
|
||||
<ds-resource-policies [resourceType]="'bitstream'"
|
||||
[resourceUUID]="bitstream.id"></ds-resource-policies>
|
||||
<ds-resource-policies [resourceType]="'item'" [resourceName]="(getItemName() | async)"
|
||||
[resourceUUID]="(getItemUUID() | async)">
|
||||
</ds-resource-policies>
|
||||
<ng-container *ngFor="let bundle of (bundles$ | async); trackById">
|
||||
<ds-resource-policies [resourceType]="'bundle'" [resourceUUID]="bundle.id" [resourceName]="bundle.name">
|
||||
</ds-resource-policies>
|
||||
<ng-container *ngIf="(bundleBitstreamsMap.get(bundle.id)?.bitstreams | async)?.length > 0">
|
||||
<div class="card auth-bitstream-container">
|
||||
<div class="card-header">
|
||||
<button type="button" class="btn btn-outline-primary" (click)="collapseArea(bundle.id)"
|
||||
[attr.aria-expanded]="false" [attr.aria-controls]="bundle.id">
|
||||
{{ 'collection.edit.item.authorizations.show-bitstreams-button' | translate }} {{bundle.name}}
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body" [id]="bundle.id" [ngbCollapse]="bundleBitstreamsMap.get(bundle.id).isCollapsed">
|
||||
<ng-container
|
||||
*ngFor="let bitstream of (bundleBitstreamsMap.get(bundle.id).bitstreams | async); trackById">
|
||||
<ds-resource-policies [resourceType]="'bitstream'" [resourceUUID]="bitstream.id"
|
||||
[resourceName]="bitstream.name"></ds-resource-policies>
|
||||
</ng-container>
|
||||
<div class="row justify-content-center" *ngIf="!bundleBitstreamsMap.get(bundle.id).allBitstreamsLoaded">
|
||||
<button type="button" class="btn btn-link" (click)="onBitstreamsLoad(bundle)">{{ 'collection.edit.item.authorizations.load-more-button' | translate }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<div class="row justify-content-center" *ngIf="!allBundlesLoaded">
|
||||
<button type="button" class="btn btn-link" (click)="onBundleLoad()">{{ 'collection.edit.item.authorizations.load-bundle-button' | translate }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@@ -0,0 +1,4 @@
|
||||
.auth-bitstream-container {
|
||||
margin-top: -1em;
|
||||
margin-bottom: 1.5em;
|
||||
}
|
@@ -1,11 +1,12 @@
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { waitForAsync, ComponentFixture, inject, TestBed } from '@angular/core/testing';
|
||||
import { Component, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { of as observableOf, of } from 'rxjs';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { cold } from 'jasmine-marbles';
|
||||
import { ItemAuthorizationsComponent } from './item-authorizations.component';
|
||||
import { ItemAuthorizationsComponent, BitstreamMapValue } from './item-authorizations.component';
|
||||
import { Bitstream } from '../../../core/shared/bitstream.model';
|
||||
import { Bundle } from '../../../core/shared/bundle.model';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
@@ -57,8 +58,6 @@ describe('ItemAuthorizationsComponent test suite', () => {
|
||||
bitstreams: createSuccessfulRemoteDataObject$(createPaginatedList([bitstream3, bitstream4]))
|
||||
});
|
||||
const bundles = [bundle1, bundle2];
|
||||
const bitstreamList1: PaginatedList<Bitstream> = buildPaginatedList(new PageInfo(), [bitstream1, bitstream2]);
|
||||
const bitstreamList2: PaginatedList<Bitstream> = buildPaginatedList(new PageInfo(), [bitstream3, bitstream4]);
|
||||
|
||||
const item = Object.assign(new Item(), {
|
||||
uuid: 'item',
|
||||
@@ -142,13 +141,12 @@ describe('ItemAuthorizationsComponent test suite', () => {
|
||||
expect(compAsAny.bundleBitstreamsMap.has('bundle1')).toBeTruthy();
|
||||
expect(compAsAny.bundleBitstreamsMap.has('bundle2')).toBeTruthy();
|
||||
let bitstreamList = compAsAny.bundleBitstreamsMap.get('bundle1');
|
||||
expect(bitstreamList).toBeObservable(cold('(a|)', {
|
||||
a: bitstreamList1
|
||||
expect(bitstreamList.bitstreams).toBeObservable(cold('(a|)', {
|
||||
a : [bitstream1, bitstream2]
|
||||
}));
|
||||
|
||||
bitstreamList = compAsAny.bundleBitstreamsMap.get('bundle2');
|
||||
expect(bitstreamList).toBeObservable(cold('(a|)', {
|
||||
a: bitstreamList2
|
||||
expect(bitstreamList.bitstreams).toBeObservable(cold('(a|)', {
|
||||
a: [bitstream3, bitstream4]
|
||||
}));
|
||||
});
|
||||
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import { isEqual } from 'lodash';
|
||||
import { DSONameService } from '../../../core/breadcrumbs/dso-name.service';
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
@@ -6,7 +8,8 @@ import { catchError, filter, first, map, mergeMap, take } from 'rxjs/operators';
|
||||
|
||||
import { buildPaginatedList, PaginatedList } from '../../../core/data/paginated-list.model';
|
||||
import {
|
||||
getFirstSucceededRemoteDataPayload, getFirstSucceededRemoteDataWithNotEmptyPayload,
|
||||
getFirstSucceededRemoteDataPayload,
|
||||
getFirstSucceededRemoteDataWithNotEmptyPayload,
|
||||
} from '../../../core/shared/operators';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { followLink } from '../../../shared/utils/follow-link-config.model';
|
||||
@@ -25,7 +28,8 @@ interface BundleBitstreamsMapEntry {
|
||||
|
||||
@Component({
|
||||
selector: 'ds-item-authorizations',
|
||||
templateUrl: './item-authorizations.component.html'
|
||||
templateUrl: './item-authorizations.component.html',
|
||||
styleUrls:['./item-authorizations.component.scss']
|
||||
})
|
||||
/**
|
||||
* Component that handles the item Authorizations
|
||||
@@ -36,13 +40,13 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy {
|
||||
* A map that contains all bitstream of the item's bundles
|
||||
* @type {Observable<Map<string, Observable<PaginatedList<Bitstream>>>>}
|
||||
*/
|
||||
public bundleBitstreamsMap: Map<string, Observable<PaginatedList<Bitstream>>> = new Map<string, Observable<PaginatedList<Bitstream>>>();
|
||||
public bundleBitstreamsMap: Map<string, BitstreamMapValue> = new Map<string, BitstreamMapValue>();
|
||||
|
||||
/**
|
||||
* The list of bundle for the item
|
||||
* The list of all bundles for the item
|
||||
* @type {Observable<PaginatedList<Bundle>>}
|
||||
*/
|
||||
private bundles$: BehaviorSubject<Bundle[]> = new BehaviorSubject<Bundle[]>([]);
|
||||
bundles$: BehaviorSubject<Bundle[]> = new BehaviorSubject<Bundle[]>([]);
|
||||
|
||||
/**
|
||||
* The target editing item
|
||||
@@ -56,15 +60,48 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy {
|
||||
*/
|
||||
private subs: Subscription[] = [];
|
||||
|
||||
/**
|
||||
* The size of the bundles to be loaded on demand
|
||||
* @type {number}
|
||||
*/
|
||||
bundlesPerPage = 6;
|
||||
|
||||
/**
|
||||
* The number of current page
|
||||
* @type {number}
|
||||
*/
|
||||
bundlesPageSize = 1;
|
||||
|
||||
/**
|
||||
* The flag to show or not the 'Load more' button
|
||||
* based on the condition if all the bundles are loaded or not
|
||||
* @type {boolean}
|
||||
*/
|
||||
allBundlesLoaded = false;
|
||||
|
||||
/**
|
||||
* Initial size of loaded bitstreams
|
||||
* The size of incrementation used in bitstream pagination
|
||||
*/
|
||||
bitstreamSize = 4;
|
||||
|
||||
/**
|
||||
* The size of the loaded bitstremas at a certain moment
|
||||
* @private
|
||||
*/
|
||||
private bitstreamPageSize = 4;
|
||||
|
||||
/**
|
||||
* Initialize instance variables
|
||||
*
|
||||
* @param {LinkService} linkService
|
||||
* @param {ActivatedRoute} route
|
||||
* @param nameService
|
||||
*/
|
||||
constructor(
|
||||
private linkService: LinkService,
|
||||
private route: ActivatedRoute
|
||||
private route: ActivatedRoute,
|
||||
private nameService: DSONameService
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -72,12 +109,49 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy {
|
||||
* Initialize the component, setting up the bundle and bitstream within the item
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
this.getBundlesPerItem();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the item's UUID
|
||||
*/
|
||||
getItemUUID(): Observable<string> {
|
||||
return this.item$.pipe(
|
||||
map((item: Item) => item.id),
|
||||
first((UUID: string) => isNotEmpty(UUID))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the item's name
|
||||
*/
|
||||
getItemName(): Observable<string> {
|
||||
return this.item$.pipe(
|
||||
map((item: Item) => this.nameService.getName(item))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all item's bundles
|
||||
*
|
||||
* @return an observable that emits all item's bundles
|
||||
*/
|
||||
getItemBundles(): Observable<Bundle[]> {
|
||||
return this.bundles$.asObservable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all bundles per item
|
||||
* and all the bitstreams per bundle
|
||||
* @param page number of current page
|
||||
*/
|
||||
getBundlesPerItem(page: number = 1) {
|
||||
this.item$ = this.route.data.pipe(
|
||||
map((data) => data.dso),
|
||||
getFirstSucceededRemoteDataWithNotEmptyPayload(),
|
||||
map((item: Item) => this.linkService.resolveLink(
|
||||
item,
|
||||
followLink('bundles', {}, followLink('bitstreams'))
|
||||
followLink('bundles', {findListOptions: {currentPage : page, elementsPerPage: this.bundlesPerPage}}, followLink('bitstreams'))
|
||||
))
|
||||
) as Observable<Item>;
|
||||
|
||||
@@ -96,37 +170,36 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy {
|
||||
take(1),
|
||||
map((list: PaginatedList<Bundle>) => list.page)
|
||||
).subscribe((bundles: Bundle[]) => {
|
||||
if (isEqual(bundles.length,0) || bundles.length < this.bundlesPerPage) {
|
||||
this.allBundlesLoaded = true;
|
||||
}
|
||||
if (isEqual(page, 1)) {
|
||||
this.bundles$.next(bundles);
|
||||
} else {
|
||||
this.bundles$.next(this.bundles$.getValue().concat(bundles));
|
||||
}
|
||||
}),
|
||||
bundles$.pipe(
|
||||
take(1),
|
||||
mergeMap((list: PaginatedList<Bundle>) => list.page),
|
||||
map((bundle: Bundle) => ({ id: bundle.id, bitstreams: this.getBundleBitstreams(bundle) }))
|
||||
).subscribe((entry: BundleBitstreamsMapEntry) => {
|
||||
this.bundleBitstreamsMap.set(entry.id, entry.bitstreams);
|
||||
let bitstreamMapValues: BitstreamMapValue = {
|
||||
isCollapsed: true,
|
||||
allBitstreamsLoaded: false,
|
||||
bitstreams: null
|
||||
};
|
||||
bitstreamMapValues.bitstreams = entry.bitstreams.pipe(
|
||||
map((b: PaginatedList<Bitstream>) => {
|
||||
bitstreamMapValues.allBitstreamsLoaded = b?.page.length < this.bitstreamSize;
|
||||
return [...b.page.slice(0, this.bitstreamSize)];
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the item's UUID
|
||||
*/
|
||||
getItemUUID(): Observable<string> {
|
||||
return this.item$.pipe(
|
||||
map((item: Item) => item.id),
|
||||
first((UUID: string) => isNotEmpty(UUID))
|
||||
this.bundleBitstreamsMap.set(entry.id, bitstreamMapValues);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all item's bundles
|
||||
*
|
||||
* @return an observable that emits all item's bundles
|
||||
*/
|
||||
getItemBundles(): Observable<Bundle[]> {
|
||||
return this.bundles$.asObservable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all bundle's bitstreams
|
||||
*
|
||||
@@ -142,6 +215,46 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the collapsible state of the area that contains the bitstream list
|
||||
* @param bundleId Id of bundle responsible for the requested bitstreams
|
||||
*/
|
||||
collapseArea(bundleId: string) {
|
||||
this.bundleBitstreamsMap.get(bundleId).isCollapsed = !this.bundleBitstreamsMap.get(bundleId).isCollapsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads as much bundles as initial value of bundleSize to be displayed
|
||||
*/
|
||||
onBundleLoad(){
|
||||
this.bundlesPageSize ++;
|
||||
this.getBundlesPerItem(this.bundlesPageSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the bitstreams that are going to be loaded on demand,
|
||||
* based on the number configured on this.bitstreamSize.
|
||||
* @param bundle parent of bitstreams that are requested to be shown
|
||||
* @returns Subscription
|
||||
*/
|
||||
onBitstreamsLoad(bundle: Bundle) {
|
||||
return this.getBundleBitstreams(bundle).subscribe((res: PaginatedList<Bitstream>) => {
|
||||
let nextBitstreams = res?.page.slice(this.bitstreamPageSize, this.bitstreamPageSize + this.bitstreamSize);
|
||||
let bitstreamsToShow = this.bundleBitstreamsMap.get(bundle.id).bitstreams.pipe(
|
||||
map((existingBits: Bitstream[])=> {
|
||||
return [... existingBits, ...nextBitstreams];
|
||||
})
|
||||
);
|
||||
this.bitstreamPageSize = this.bitstreamPageSize + this.bitstreamSize;
|
||||
let bitstreamMapValues: BitstreamMapValue = {
|
||||
bitstreams: bitstreamsToShow ,
|
||||
isCollapsed: this.bundleBitstreamsMap.get(bundle.id).isCollapsed,
|
||||
allBitstreamsLoaded: res?.page.length <= this.bitstreamPageSize
|
||||
};
|
||||
this.bundleBitstreamsMap.set(bundle.id, bitstreamMapValues);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from all subscriptions
|
||||
*/
|
||||
@@ -151,3 +264,9 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy {
|
||||
.forEach((subscription) => subscription.unsubscribe());
|
||||
}
|
||||
}
|
||||
|
||||
export interface BitstreamMapValue {
|
||||
bitstreams: Observable<Bitstream[]>;
|
||||
isCollapsed: boolean;
|
||||
allBitstreamsLoaded: boolean;
|
||||
}
|
||||
|
@@ -1,5 +1,4 @@
|
||||
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
|
||||
<ds-truncatable [id]="dso.id">
|
||||
<div class="position-absolute ml-1">
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
@@ -19,28 +18,28 @@
|
||||
<div class="card-body">
|
||||
<ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge>
|
||||
<ds-access-status-badge [item]="dso"></ds-access-status-badge>
|
||||
<ds-truncatable [id]="dso.id">
|
||||
<ds-truncatable-part [id]="dso.id" [minLines]="3" type="h4">
|
||||
<h4 class="card-title" [innerHTML]="firstMetadataValue('dc.title')"></h4>
|
||||
</ds-truncatable-part>
|
||||
<p *ngIf="dso.hasMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*'])"
|
||||
class="item-authors card-text text-muted">
|
||||
<ds-truncatable-part [id]="dso.id" [minLines]="1">
|
||||
<ds-truncatable-part [id]="dso.id" [minLines]="1" *ngIf="dso.hasMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*'])">
|
||||
<p class="item-authors card-text text-muted">
|
||||
<span *ngIf="dso.hasMetadata('dc.date.issued')" class="item-date">{{firstMetadataValue('dc.date.issued')}}</span>
|
||||
<span *ngFor="let author of allMetadataValues(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']);">,
|
||||
<span [innerHTML]="author"></span>
|
||||
</span>
|
||||
</ds-truncatable-part>
|
||||
</p>
|
||||
<p *ngIf="dso.hasMetadata('dc.description.abstract')" class="item-abstract card-text">
|
||||
<ds-truncatable-part [id]="dso.id" [minLines]="3">
|
||||
</ds-truncatable-part>
|
||||
<ds-truncatable-part *ngIf="dso.hasMetadata('dc.description.abstract')" [id]="dso.id" [minLines]="3">
|
||||
<p class="item-abstract card-text">
|
||||
<span [innerHTML]="firstMetadataValue('dc.description.abstract')"></span>
|
||||
</ds-truncatable-part>
|
||||
</p>
|
||||
</ds-truncatable-part>
|
||||
</ds-truncatable>
|
||||
<div *ngIf="linkType != linkTypes.None" class="text-center">
|
||||
<a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
|
||||
class="lead btn btn-primary viewButton">{{ 'search.results.view-result' | translate}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</ds-truncatable>
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
|
@@ -1,13 +1,13 @@
|
||||
<ds-truncatable-part [maxLines]="1" [background]="isCurrent() ? 'primary' : 'default'">
|
||||
<ds-truncatable-part [maxLines]="1" [background]="isCurrent() ? 'primary' : 'default'" [showToggle]="false">
|
||||
<div [ngClass]="isCurrent() ? 'text-light' : 'text-body'"
|
||||
[innerHTML]="(parentTitle$ && parentTitle$ | async) ? (parentTitle$ | async) : ('home.breadcrumbs' | translate)"></div>
|
||||
</ds-truncatable-part>
|
||||
<ds-truncatable-part *ngIf="title" [maxLines]="1" [background]="isCurrent() ? 'primary' : 'default'">
|
||||
<ds-truncatable-part *ngIf="title" [maxLines]="1" [background]="isCurrent() ? 'primary' : 'default'" [showToggle]="false">
|
||||
<div class="font-weight-bold"
|
||||
[ngClass]="isCurrent() ? 'text-light' : 'text-primary'"
|
||||
[innerHTML]="title"></div>
|
||||
</ds-truncatable-part>
|
||||
<ds-truncatable-part *ngIf="description" [maxLines]="1" [background]="isCurrent() ? 'primary' : 'default'">
|
||||
<ds-truncatable-part *ngIf="description" [maxLines]="1" [background]="isCurrent() ? 'primary' : 'default'" [showToggle]="false">
|
||||
<div class="text-secondary"
|
||||
[ngClass]="isCurrent() ? 'text-light' : 'text-secondary'"
|
||||
[innerHTML]="description"></div>
|
||||
|
@@ -4,9 +4,15 @@
|
||||
<tr>
|
||||
<th colspan="10">
|
||||
<div class="d-flex justify-content-between align-items-center m-0">
|
||||
{{ 'resource-policies.table.headers.title.for.' + resourceType | translate }} {{resourceUUID}}
|
||||
<span>
|
||||
{{ 'resource-policies.table.headers.title.for.' + resourceType | translate }}
|
||||
<span class="text-info"> {{resourceName}} </span>
|
||||
<ng-container *ngIf="resourceType != 'item'">
|
||||
({{resourceUUID}})
|
||||
</ng-container>
|
||||
</span>
|
||||
<div class="space-children-mr">
|
||||
<button class="btn btn-danger"
|
||||
<button class="btn btn-danger p-1"
|
||||
[disabled]="(!(canDelete() | async)) || (isProcessingDelete() | async)"
|
||||
[title]="'resource-policies.delete.btn.title' | translate"
|
||||
(click)="deleteSelectedResourcePolicies()">
|
||||
@@ -18,7 +24,7 @@
|
||||
{{'resource-policies.delete.btn' | translate}}
|
||||
</span>
|
||||
</button>
|
||||
<button class="btn btn-success"
|
||||
<button class="btn btn-success p-1"
|
||||
[disabled]="(isProcessingDelete() | async)"
|
||||
[title]="'resource-policies.add.for.' + resourceType | translate"
|
||||
(click)="redirectToResourcePolicyCreatePage()">
|
||||
|
@@ -62,6 +62,12 @@ export class ResourcePoliciesComponent implements OnInit, OnDestroy {
|
||||
*/
|
||||
@Input() public resourceType: string;
|
||||
|
||||
/**
|
||||
* The resource name
|
||||
* @type {string}
|
||||
*/
|
||||
@Input() public resourceName: string;
|
||||
|
||||
/**
|
||||
* A boolean representing if component is active
|
||||
* @type {boolean}
|
||||
|
@@ -1,5 +1,9 @@
|
||||
<div class="clamp-{{background}}-{{lines}} min-{{minLines}} {{type}} {{fixedHeight ? 'fixedHeight' : ''}}">
|
||||
<div class="content dont-break-out">
|
||||
<div #content class="content dont-break-out">
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
<button class="btn btn-link p-0 expandButton" dsDragClick (actualClick)="toggle()">
|
||||
<i class="fas fa-angle-down"></i> {{ 'item.truncatable-part.show-more' | translate }}</button>
|
||||
<button class="btn btn-link p-0 collapseButton" dsDragClick (actualClick)="toggle()" *ngIf="expand && expandable">
|
||||
<i class="fas fa-angle-up"></i> {{ 'item.truncatable-part.show-less' | translate }}</button>
|
||||
</div>
|
||||
|
@@ -0,0 +1,11 @@
|
||||
.content:not(.truncated) ~ button.expandButton {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.btn:focus {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.removeFaded.content::after {
|
||||
display: none;
|
||||
}
|
||||
|
@@ -4,10 +4,17 @@ import { TruncatablePartComponent } from './truncatable-part.component';
|
||||
import { TruncatableService } from '../truncatable.service';
|
||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||
import { getMockTranslateService } from '../../mocks/translate.service.mock';
|
||||
import { TranslateLoaderMock } from '../../mocks/translate-loader.mock';
|
||||
import { mockTruncatableService } from '../../mocks/mock-trucatable.service';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { NativeWindowRef, NativeWindowService } from '../../../core/services/window.service';
|
||||
|
||||
describe('TruncatablePartComponent', () => {
|
||||
let comp: TruncatablePartComponent;
|
||||
let fixture: ComponentFixture<TruncatablePartComponent>;
|
||||
let translateService: TranslateService;
|
||||
const id1 = '123';
|
||||
const id2 = '456';
|
||||
|
||||
@@ -22,10 +29,19 @@ describe('TruncatablePartComponent', () => {
|
||||
}
|
||||
};
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [NoopAnimationsModule],
|
||||
translateService = getMockTranslateService();
|
||||
void TestBed.configureTestingModule({
|
||||
imports: [NoopAnimationsModule,
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useClass: TranslateLoaderMock
|
||||
}
|
||||
}),
|
||||
],
|
||||
declarations: [TruncatablePartComponent],
|
||||
providers: [
|
||||
{ provide: NativeWindowService, useValue: new NativeWindowRef() },
|
||||
{ provide: TruncatableService, useValue: truncatableServiceStub },
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
@@ -52,6 +68,11 @@ describe('TruncatablePartComponent', () => {
|
||||
it('lines should equal minlines', () => {
|
||||
expect((comp as any).lines).toEqual(comp.minLines.toString());
|
||||
});
|
||||
|
||||
it('collapseButton should be hidden', () => {
|
||||
const a = fixture.debugElement.query(By.css('.collapseButton'));
|
||||
expect(a).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('When the item is expanded', () => {
|
||||
@@ -72,5 +93,63 @@ describe('TruncatablePartComponent', () => {
|
||||
fixture.detectChanges();
|
||||
expect((comp as any).lines).toEqual('none');
|
||||
});
|
||||
|
||||
it('collapseButton should be shown', () => {
|
||||
(comp as any).setLines();
|
||||
(comp as any).expandable = true;
|
||||
fixture.detectChanges();
|
||||
const a = fixture.debugElement.query(By.css('.collapseButton'));
|
||||
expect(a).not.toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('TruncatablePartComponent', () => {
|
||||
let comp: TruncatablePartComponent;
|
||||
let fixture: ComponentFixture<TruncatablePartComponent>;
|
||||
let translateService: TranslateService;
|
||||
const identifier = '1234567890';
|
||||
let truncatableService;
|
||||
beforeEach(waitForAsync(() => {
|
||||
translateService = getMockTranslateService();
|
||||
void TestBed.configureTestingModule({
|
||||
imports: [
|
||||
NoopAnimationsModule,
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useClass: TranslateLoaderMock
|
||||
}
|
||||
}),
|
||||
],
|
||||
declarations: [TruncatablePartComponent],
|
||||
providers: [
|
||||
{ provide: NativeWindowService, useValue: new NativeWindowRef() },
|
||||
{ provide: TruncatableService, useValue: mockTruncatableService },
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).overrideComponent(TruncatablePartComponent, {
|
||||
set: { changeDetection: ChangeDetectionStrategy.Default }
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(TruncatablePartComponent);
|
||||
comp = fixture.componentInstance; // TruncatablePartComponent test instance
|
||||
comp.id = identifier;
|
||||
fixture.detectChanges();
|
||||
truncatableService = (comp as any).service;
|
||||
});
|
||||
|
||||
describe('When toggle is called', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(truncatableService, 'toggle');
|
||||
comp.toggle();
|
||||
});
|
||||
|
||||
it('should call toggle on the TruncatableService', () => {
|
||||
expect(truncatableService.toggle).toHaveBeenCalledWith(identifier);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
|
||||
import { AfterViewChecked, Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||
import { TruncatableService } from '../truncatable.service';
|
||||
import { hasValue } from '../../empty.util';
|
||||
|
||||
@@ -12,7 +12,7 @@ import { hasValue } from '../../empty.util';
|
||||
* Component that truncates/clamps a piece of text
|
||||
* It needs a TruncatableComponent parent to identify it's current state
|
||||
*/
|
||||
export class TruncatablePartComponent implements OnInit, OnDestroy {
|
||||
export class TruncatablePartComponent implements AfterViewChecked, OnInit, OnDestroy {
|
||||
/**
|
||||
* Number of lines shown when the part is collapsed
|
||||
*/
|
||||
@@ -40,6 +40,17 @@ export class TruncatablePartComponent implements OnInit, OnDestroy {
|
||||
|
||||
@Input() background = 'default';
|
||||
|
||||
/**
|
||||
* A boolean representing if to show or not the show/collapse toggle.
|
||||
* This value must have the same value as the parent TruncatableComponent
|
||||
*/
|
||||
@Input() showToggle = true;
|
||||
|
||||
/**
|
||||
* The view on the truncatable part
|
||||
*/
|
||||
@ViewChild('content', {static: true}) content: ElementRef;
|
||||
|
||||
/**
|
||||
* Current amount of lines shown of this part
|
||||
*/
|
||||
@@ -49,9 +60,16 @@ export class TruncatablePartComponent implements OnInit, OnDestroy {
|
||||
* Subscription to unsubscribe from
|
||||
*/
|
||||
private sub;
|
||||
/**
|
||||
* store variable used for local to expand collapse
|
||||
*/
|
||||
expand = false;
|
||||
/**
|
||||
* variable to check if expandable
|
||||
*/
|
||||
expandable = false;
|
||||
|
||||
public constructor(private service: TruncatableService) {
|
||||
}
|
||||
public constructor(private service: TruncatableService) {}
|
||||
|
||||
/**
|
||||
* Initialize lines variable
|
||||
@@ -67,12 +85,57 @@ export class TruncatablePartComponent implements OnInit, OnDestroy {
|
||||
this.sub = this.service.isCollapsed(this.id).subscribe((collapsed: boolean) => {
|
||||
if (collapsed) {
|
||||
this.lines = this.minLines.toString();
|
||||
this.expand = false;
|
||||
} else {
|
||||
this.lines = this.maxLines < 0 ? 'none' : this.maxLines.toString();
|
||||
this.expand = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngAfterViewChecked() {
|
||||
this.truncateElement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Expands the truncatable when it's collapsed, collapses it when it's expanded
|
||||
*/
|
||||
public toggle() {
|
||||
this.service.toggle(this.id);
|
||||
this.expandable = !this.expandable;
|
||||
}
|
||||
|
||||
/**
|
||||
* check for the truncate element
|
||||
*/
|
||||
public truncateElement() {
|
||||
if (this.showToggle) {
|
||||
const entry = this.content.nativeElement;
|
||||
if (entry.scrollHeight > entry.offsetHeight) {
|
||||
if (entry.children.length > 0) {
|
||||
if (entry.children[entry.children.length - 1].offsetHeight > entry.offsetHeight) {
|
||||
entry.classList.add('truncated');
|
||||
entry.classList.remove('removeFaded');
|
||||
} else {
|
||||
entry.classList.remove('truncated');
|
||||
entry.classList.add('removeFaded');
|
||||
}
|
||||
} else {
|
||||
if (entry.innerText.length > 0) {
|
||||
entry.classList.add('truncated');
|
||||
entry.classList.remove('removeFaded');
|
||||
} else {
|
||||
entry.classList.remove('truncated');
|
||||
entry.classList.add('removeFaded');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
entry.classList.remove('truncated');
|
||||
entry.classList.add('removeFaded');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from the subscription
|
||||
*/
|
||||
|
@@ -1,3 +1,3 @@
|
||||
<div dsDragClick (actualClick)="toggle()" (mouseenter)="hoverExpand()" (mouseleave)="hoverCollapse()">
|
||||
<div (mouseenter)="hoverExpand()" (mouseleave)="hoverCollapse()">
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
|
@@ -70,15 +70,4 @@ describe('TruncatableComponent', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('When toggle is called', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(truncatableService, 'toggle');
|
||||
comp.toggle();
|
||||
});
|
||||
|
||||
it('should call toggle on the TruncatableService', () => {
|
||||
expect(truncatableService.toggle).toHaveBeenCalledWith(identifier);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@@ -1,6 +1,4 @@
|
||||
import {
|
||||
Component, Input
|
||||
} from '@angular/core';
|
||||
import { AfterViewChecked, Component, ElementRef, Input, OnInit } from '@angular/core';
|
||||
import { TruncatableService } from './truncatable.service';
|
||||
|
||||
@Component({
|
||||
@@ -13,7 +11,7 @@ import { TruncatableService } from './truncatable.service';
|
||||
/**
|
||||
* Component that represents a section with one or more truncatable parts that all listen to this state
|
||||
*/
|
||||
export class TruncatableComponent {
|
||||
export class TruncatableComponent implements OnInit, AfterViewChecked {
|
||||
/**
|
||||
* Is true when all truncatable parts in this truncatable should be expanded on loading
|
||||
*/
|
||||
@@ -29,7 +27,13 @@ export class TruncatableComponent {
|
||||
*/
|
||||
@Input() onHover = false;
|
||||
|
||||
public constructor(private service: TruncatableService) {
|
||||
/**
|
||||
* A boolean representing if to show or not the show/collapse toggle
|
||||
* This value must have the same value as the children TruncatablePartComponent
|
||||
*/
|
||||
@Input() showToggle = true;
|
||||
|
||||
public constructor(private service: TruncatableService, private el: ElementRef,) {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,11 +65,18 @@ export class TruncatableComponent {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Expands the truncatable when it's collapsed, collapses it when it's expanded
|
||||
*/
|
||||
public toggle() {
|
||||
this.service.toggle(this.id);
|
||||
ngAfterViewChecked() {
|
||||
if (this.showToggle) {
|
||||
const truncatedElements = this.el.nativeElement.querySelectorAll('.truncated');
|
||||
if (truncatedElements?.length > 0) {
|
||||
const truncateElements = this.el.nativeElement.querySelectorAll('.dont-break-out');
|
||||
for (let i = 0; i < (truncateElements.length - 1); i++) {
|
||||
truncateElements[i].classList.remove('truncated');
|
||||
truncateElements[i].classList.add('notruncatable');
|
||||
}
|
||||
truncateElements[truncateElements.length - 1].classList.add('truncated');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -895,6 +895,8 @@
|
||||
"bitstream.edit.title": "Datei bearbeiten",
|
||||
|
||||
|
||||
// "browse.back.all-results": "All browse results",
|
||||
"browse.back.all-results": "Filter zurücksetzen",
|
||||
|
||||
// "browse.comcol.by.author": "By Author",
|
||||
"browse.comcol.by.author": "Nach Autor:in",
|
||||
@@ -1782,6 +1784,18 @@
|
||||
// "dso-selector.placeholder": "Search for a {{ type }}",
|
||||
"dso-selector.placeholder": "Suche nach {{ type }}",
|
||||
|
||||
// "dso-selector.select.collection.head": "Select a collection",
|
||||
"dso-selector.select.collection.head": "Sammlung auswählen",
|
||||
|
||||
// "dso-selector.set-scope.community.head": "Select a search scope",
|
||||
"dso-selector.set-scope.community.head": "Suchbereich auswählen",
|
||||
|
||||
// "dso-selector.set-scope.community.button": "Search all of DSpace",
|
||||
"dso-selector.set-scope.community.button": "Alles durchsuchen",
|
||||
|
||||
// "dso-selector.set-scope.community.input-header": "Search for a community or collection",
|
||||
"dso-selector.set-scope.community.input-header": "Suche Bereich oder Sammlung",
|
||||
|
||||
|
||||
|
||||
// "confirmation-modal.export-metadata.header": "Export metadata for {{ dsoName }}",
|
||||
@@ -4293,6 +4307,9 @@
|
||||
// "search.filters.filter.author.placeholder": "Author name",
|
||||
"search.filters.filter.author.placeholder": "Autor:innenname",
|
||||
|
||||
// "search.filters.filter.author.label": "Search author name",
|
||||
"search.filters.filter.author.label": "Autorensuche",
|
||||
|
||||
// "search.filters.filter.birthDate.head": "Birth Date",
|
||||
"search.filters.filter.birthDate.head": "Geburtsdatum",
|
||||
|
||||
@@ -4472,6 +4489,8 @@
|
||||
// "search.form.search_mydspace": "Search MyDSpace",
|
||||
"search.form.search_mydspace": "Suche im persönlichen Arbeitsbereich",
|
||||
|
||||
// "search.form.scope.all": "All of DSpace",
|
||||
"search.form.scope.all": "Alles durchsuchen",
|
||||
|
||||
|
||||
// "search.results.head": "Search Results",
|
||||
@@ -4486,7 +4505,11 @@
|
||||
// "search.results.empty": "Your search returned no results.",
|
||||
"search.results.empty": "Ihre Suche führte zu keinem Ergebnis.",
|
||||
|
||||
// "search.results.view-result": "View",
|
||||
"search.results.view-result": "Anzeigen",
|
||||
|
||||
// "default.search.results.head": "Search Results",
|
||||
"default.search.results.head": "Suchergebnisse",
|
||||
|
||||
// "search.sidebar.close": "Back to results",
|
||||
"search.sidebar.close": "Zurück zu den Ergebnissen",
|
||||
@@ -4534,10 +4557,29 @@
|
||||
// "sorting.dc.title.DESC": "Title Descending",
|
||||
"sorting.dc.title.DESC": "Titel absteigend",
|
||||
|
||||
// "sorting.score.DESC": "Relevance",
|
||||
"sorting.score.DESC": "Relevanz",
|
||||
// "sorting.score.ASC": "Least Relevant",
|
||||
"sorting.score.ASC": "Relevanz aufsteigend",
|
||||
|
||||
// "sorting.score.DESC": "Most Relevant",
|
||||
"sorting.score.DESC": "Relevanz absteigend",
|
||||
|
||||
// "sorting.dc.date.issued.ASC": "Date Issued Ascending",
|
||||
"sorting.dc.date.issued.ASC": "Erscheinungsdatum aufsteigend",
|
||||
|
||||
// "sorting.dc.date.issued.DESC": "Date Issued Descending",
|
||||
"sorting.dc.date.issued.DESC": "Erscheinungsdatum absteigend",
|
||||
|
||||
// "sorting.dc.date.accessioned.ASC": "Accessioned Date Ascending",
|
||||
"sorting.dc.date.accessioned.ASC": "Freischaltungsdatum aufsteigend",
|
||||
|
||||
// "sorting.dc.date.accessioned.DESC": "Accessioned Date Descending",
|
||||
"sorting.dc.date.accessioned.DESC": "Freischaltungsdatum absteigend",
|
||||
|
||||
// "sorting.lastModified.ASC": "Last modified Ascending",
|
||||
"sorting.lastModified.ASC": "Änderungsdatum aufsteigend",
|
||||
|
||||
// "sorting.lastModified.DESC": "Last modified Descending",
|
||||
"sorting.lastModified.DESC": "Änderungsdatum absteigend",
|
||||
|
||||
// "statistics.title": "Statistics",
|
||||
"statistics.title": "Statistiken",
|
||||
@@ -5238,6 +5280,30 @@
|
||||
"submission.workflow.tasks.pool.show-detail": "Details anzeigen",
|
||||
|
||||
|
||||
// "thumbnail.default.alt": "Thumbnail Image",
|
||||
"thumbnail.default.alt": "Vorschaubild",
|
||||
|
||||
// "thumbnail.default.placeholder": "No Thumbnail Available",
|
||||
"thumbnail.default.placeholder": "Vorschaubild nicht verfügbar",
|
||||
|
||||
// "thumbnail.project.alt": "Project Logo",
|
||||
"thumbnail.project.alt": "Logo des Projekt",
|
||||
|
||||
// "thumbnail.project.placeholder": "Project Placeholder Image",
|
||||
"thumbnail.project.placeholder": "Platzhalterbild des Projekts",
|
||||
|
||||
// "thumbnail.orgunit.alt": "OrgUnit Logo",
|
||||
"thumbnail.orgunit.alt": "Logo der Organisationseinheit",
|
||||
|
||||
// "thumbnail.orgunit.placeholder": "OrgUnit Placeholder Image",
|
||||
"thumbnail.orgunit.placeholder": "Platzhalterbild der Organisationseinheit",
|
||||
|
||||
// "thumbnail.person.alt": "Profile Picture",
|
||||
"thumbnail.person.alt": "Profilbild",
|
||||
|
||||
// "thumbnail.person.placeholder": "No Profile Picture Available",
|
||||
"thumbnail.person.placeholder": "Profilbild nicht verfügbar",
|
||||
|
||||
|
||||
// "title": "DSpace",
|
||||
"title": "DSpace",
|
||||
|
@@ -872,6 +872,12 @@
|
||||
|
||||
"collection.edit.tabs.authorizations.title": "Collection Edit - Authorizations",
|
||||
|
||||
"collection.edit.item.authorizations.load-bundle-button": "Load more bundles",
|
||||
|
||||
"collection.edit.item.authorizations.load-more-button": "Load more",
|
||||
|
||||
"collection.edit.item.authorizations.show-bitstreams-button": "Show bitstream policies for bundle",
|
||||
|
||||
"collection.edit.tabs.metadata.head": "Edit Metadata",
|
||||
|
||||
"collection.edit.tabs.metadata.title": "Collection Edit - Metadata",
|
||||
@@ -2150,6 +2156,10 @@
|
||||
|
||||
"item.search.title": "Item Search",
|
||||
|
||||
"item.truncatable-part.show-more": "Show more",
|
||||
|
||||
"item.truncatable-part.show-less": "Collapse",
|
||||
|
||||
|
||||
|
||||
"item.page.abstract": "Abstract",
|
||||
|
@@ -107,7 +107,7 @@
|
||||
"admin.registries.bitstream-formats.edit.head": "Tiedostoformaatti: {{ format }}",
|
||||
|
||||
// "admin.registries.bitstream-formats.edit.internal.hint": "Formats marked as internal are hidden from the user, and used for administrative purposes.",
|
||||
"admin.registries.bitstream-formats.edit.internal.hint": "Sisäisiksi merkittyjä formaatteja käytetään hallinnollisiin tarkoituksiin, ja ne on piilotettu käyttäjiltä.",
|
||||
"admin.registries.bitstream-formats.edit.internal.hint": "Sisäisiksi merkittyjä formaatteja käytetään ylläpitotarkoituksiin, ja ne on piilotettu käyttäjiltä.",
|
||||
|
||||
// "admin.registries.bitstream-formats.edit.internal.label": "Internal",
|
||||
"admin.registries.bitstream-formats.edit.internal.label": "Sisäinen",
|
||||
@@ -662,7 +662,7 @@
|
||||
|
||||
|
||||
// "admin.search.breadcrumbs": "Administrative Search",
|
||||
"admin.search.breadcrumbs": "Hallinnollinen haku",
|
||||
"admin.search.breadcrumbs": "Ylläpitäjän haku",
|
||||
|
||||
// "admin.search.collection.edit": "Edit",
|
||||
"admin.search.collection.edit": "Muokkaa",
|
||||
@@ -692,19 +692,19 @@
|
||||
"admin.search.item.withdraw": "Poista käytöstä",
|
||||
|
||||
// "admin.search.title": "Administrative Search",
|
||||
"admin.search.title": "Hallinnollinen haku",
|
||||
"admin.search.title": "Ylläpitäjän haku",
|
||||
|
||||
// "administrativeView.search.results.head": "Administrative Search",
|
||||
"administrativeView.search.results.head": "Hallinnollinen haku",
|
||||
"administrativeView.search.results.head": "Ylläpitäjän haku",
|
||||
|
||||
|
||||
|
||||
|
||||
// "admin.workflow.breadcrumbs": "Administer Workflow",
|
||||
"admin.workflow.breadcrumbs": "Hallinnointityönkulku",
|
||||
"admin.workflow.breadcrumbs": "Hallinnoi työnkulkua",
|
||||
|
||||
// "admin.workflow.title": "Administer Workflow",
|
||||
"admin.workflow.title": "Hallinnointityönkulku",
|
||||
"admin.workflow.title": "Hallinnoi työnkulkua",
|
||||
|
||||
// "admin.workflow.item.workflow": "Workflow",
|
||||
"admin.workflow.item.workflow": "Työnkulku",
|
||||
@@ -2954,7 +2954,7 @@
|
||||
|
||||
|
||||
// "menu.section.admin_search": "Admin Search",
|
||||
"menu.section.admin_search": "Admin-haku",
|
||||
"menu.section.admin_search": "Ylläpitäjän haku",
|
||||
|
||||
|
||||
|
||||
@@ -3033,7 +3033,7 @@
|
||||
"menu.section.icon.access_control": "Pääsyoikeudet",
|
||||
|
||||
// "menu.section.icon.admin_search": "Admin search menu section",
|
||||
"menu.section.icon.admin_search": "Admin-haku",
|
||||
"menu.section.icon.admin_search": "Ylläpitäjän haku",
|
||||
|
||||
// "menu.section.icon.control_panel": "Control Panel menu section",
|
||||
"menu.section.icon.control_panel": "Hallintapaneeli",
|
||||
@@ -3168,7 +3168,7 @@
|
||||
|
||||
|
||||
// "menu.section.workflow": "Administer Workflow",
|
||||
"menu.section.workflow": "Hallinnointityönkulku",
|
||||
"menu.section.workflow": "Hallinnoi työnkulkua",
|
||||
|
||||
|
||||
// "mydspace.description": "",
|
||||
@@ -5079,7 +5079,7 @@
|
||||
|
||||
|
||||
// "workflowAdmin.search.results.head": "Administer Workflow",
|
||||
"workflowAdmin.search.results.head": "Hallinnointityönkulku",
|
||||
"workflowAdmin.search.results.head": "Hallinnoi työnkulkua",
|
||||
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user