mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-16 14:33:03 +00:00
Merge remote-tracking branch 'upstream/main' into CST-7216
This commit is contained in:
@@ -13,12 +13,13 @@
|
||||
[formGroup]="formGroup"
|
||||
[formLayout]="formLayout"
|
||||
[displayCancel]="false"
|
||||
[submitLabel]="submitLabel"
|
||||
(submitForm)="onSubmit()">
|
||||
<div before class="btn-group">
|
||||
<button (click)="onCancel()"
|
||||
class="btn btn-outline-secondary"><i class="fas fa-arrow-left"></i> {{messagePrefix + '.return' | translate}}</button>
|
||||
</div>
|
||||
<div between class="btn-group">
|
||||
<div *ngIf="displayResetPassword" between class="btn-group">
|
||||
<button class="btn btn-primary" [disabled]="!(canReset$ | async)" (click)="resetPassword()">
|
||||
<i class="fa fa-key"></i> {{'admin.access-control.epeople.actions.reset' | translate}}
|
||||
</button>
|
||||
|
@@ -165,6 +165,15 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
|
||||
*/
|
||||
isImpersonated = false;
|
||||
|
||||
/**
|
||||
* A boolean that indicate if to display EPersonForm's Rest password button
|
||||
*/
|
||||
displayResetPassword = false;
|
||||
|
||||
/**
|
||||
* A string that indicate the label of Submit button
|
||||
*/
|
||||
submitLabel = 'form.create';
|
||||
/**
|
||||
* Subscription to email field value change
|
||||
*/
|
||||
@@ -188,6 +197,8 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
|
||||
this.epersonInitial = eperson;
|
||||
if (hasValue(eperson)) {
|
||||
this.isImpersonated = this.authService.isImpersonatingUser(eperson.id);
|
||||
this.displayResetPassword = true;
|
||||
this.submitLabel = 'form.submit';
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
@@ -117,9 +117,15 @@ export class BatchImportPageComponent {
|
||||
this.router.navigateByUrl(getProcessDetailRoute(rd.payload.processId));
|
||||
}
|
||||
} else {
|
||||
const title = this.translate.get('process.new.notification.error.title');
|
||||
const content = this.translate.get('process.new.notification.error.content');
|
||||
this.notificationsService.error(title, content);
|
||||
if (rd.statusCode === 413) {
|
||||
const title = this.translate.get('process.new.notification.error.title');
|
||||
const content = this.translate.get('process.new.notification.error.max-upload.content');
|
||||
this.notificationsService.error(title, content);
|
||||
} else {
|
||||
const title = this.translate.get('process.new.notification.error.title');
|
||||
const content = this.translate.get('process.new.notification.error.content');
|
||||
this.notificationsService.error(title, content);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@@ -218,7 +218,8 @@ import { ThemedPageErrorComponent } from './page-error/themed-page-error.compone
|
||||
{
|
||||
path: 'statistics',
|
||||
loadChildren: () => import('./statistics-page/statistics-page-routing.module')
|
||||
.then((m) => m.StatisticsPageRoutingModule)
|
||||
.then((m) => m.StatisticsPageRoutingModule),
|
||||
canActivate: [EndUserAgreementCurrentUserGuard],
|
||||
},
|
||||
{
|
||||
path: HEALTH_PAGE_PATH,
|
||||
@@ -228,7 +229,7 @@ import { ThemedPageErrorComponent } from './page-error/themed-page-error.compone
|
||||
{
|
||||
path: ACCESS_CONTROL_MODULE_PATH,
|
||||
loadChildren: () => import('./access-control/access-control.module').then((m) => m.AccessControlModule),
|
||||
canActivate: [GroupAdministratorGuard],
|
||||
canActivate: [GroupAdministratorGuard, EndUserAgreementCurrentUserGuard],
|
||||
},
|
||||
{
|
||||
path: 'subscriptions',
|
||||
|
@@ -22,6 +22,7 @@ import { PaginationService } from '../../core/pagination/pagination.service';
|
||||
import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
|
||||
import { APP_CONFIG } from '../../../config/app-config.interface';
|
||||
import { environment } from '../../../environments/environment';
|
||||
import { SortDirection } from '../../core/cache/models/sort-options.model';
|
||||
|
||||
describe('BrowseByDatePageComponent', () => {
|
||||
let comp: BrowseByDatePageComponent;
|
||||
@@ -49,12 +50,22 @@ describe('BrowseByDatePageComponent', () => {
|
||||
]
|
||||
}
|
||||
});
|
||||
const lastItem = Object.assign(new Item(), {
|
||||
id: 'last-item-id',
|
||||
metadata: {
|
||||
'dc.date.issued': [
|
||||
{
|
||||
value: '1960-01-01'
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
const mockBrowseService = {
|
||||
getBrowseEntriesFor: (options: BrowseEntrySearchOptions) => toRemoteData([]),
|
||||
getBrowseItemsFor: (value: string, options: BrowseEntrySearchOptions) => toRemoteData([firstItem]),
|
||||
getFirstItemFor: () => createSuccessfulRemoteDataObject$(firstItem)
|
||||
};
|
||||
const mockBrowseService = {
|
||||
getBrowseEntriesFor: (options: BrowseEntrySearchOptions) => toRemoteData([]),
|
||||
getBrowseItemsFor: (value: string, options: BrowseEntrySearchOptions) => toRemoteData([firstItem]),
|
||||
getFirstItemFor: (definition: string, scope?: string, sortDirection?: SortDirection) => null
|
||||
};
|
||||
|
||||
const mockDsoService = {
|
||||
findById: () => createSuccessfulRemoteDataObject$(mockCommunity)
|
||||
@@ -91,9 +102,14 @@ describe('BrowseByDatePageComponent', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(BrowseByDatePageComponent);
|
||||
const browseService = fixture.debugElement.injector.get(BrowseService);
|
||||
spyOn(browseService, 'getFirstItemFor')
|
||||
// ok to expect the default browse as first param since we just need the mock items obtained via sort direction.
|
||||
.withArgs('author', undefined, SortDirection.ASC).and.returnValue(createSuccessfulRemoteDataObject$(firstItem))
|
||||
.withArgs('author', undefined, SortDirection.DESC).and.returnValue(createSuccessfulRemoteDataObject$(lastItem));
|
||||
comp = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
route = (comp as any).route;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should initialize the list of items', () => {
|
||||
@@ -107,6 +123,7 @@ describe('BrowseByDatePageComponent', () => {
|
||||
});
|
||||
|
||||
it('should create a list of startsWith options with the current year first', () => {
|
||||
expect(comp.startsWithOptions[0]).toEqual(new Date().getUTCFullYear());
|
||||
//expect(comp.startsWithOptions[0]).toEqual(new Date().getUTCFullYear());
|
||||
expect(comp.startsWithOptions[0]).toEqual(1960);
|
||||
});
|
||||
});
|
||||
|
@@ -1,11 +1,10 @@
|
||||
import { ChangeDetectorRef, Component, Inject } from '@angular/core';
|
||||
import {
|
||||
BrowseByMetadataPageComponent,
|
||||
browseParamsToOptions, getBrowseSearchOptions
|
||||
browseParamsToOptions,
|
||||
getBrowseSearchOptions
|
||||
} from '../browse-by-metadata-page/browse-by-metadata-page.component';
|
||||
import { combineLatest as observableCombineLatest } from 'rxjs';
|
||||
import { RemoteData } from '../../core/data/remote-data';
|
||||
import { Item } from '../../core/shared/item.model';
|
||||
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
||||
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||
import { BrowseService } from '../../core/browse/browse.service';
|
||||
@@ -16,7 +15,9 @@ import { map } from 'rxjs/operators';
|
||||
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
|
||||
import { isValidDate } from '../../shared/date.util';
|
||||
import { AppConfig, APP_CONFIG } from '../../../config/app-config.interface';
|
||||
import { APP_CONFIG, AppConfig } from '../../../config/app-config.interface';
|
||||
import { RemoteData } from '../../core/data/remote-data';
|
||||
import { Item } from '../../core/shared/item.model';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-browse-by-date-page',
|
||||
@@ -72,30 +73,24 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent {
|
||||
|
||||
/**
|
||||
* Update the StartsWith options
|
||||
* In this implementation, it creates a list of years starting from now, going all the way back to the earliest
|
||||
* date found on an item within this scope. The further back in time, the bigger the change in years become to avoid
|
||||
* extremely long lists with a one-year difference.
|
||||
* In this implementation, it creates a list of years starting from the most recent item or the current year, going
|
||||
* all the way back to the earliest date found on an item within this scope. The further back in time, the bigger
|
||||
* the change in years become to avoid extremely long lists with a one-year difference.
|
||||
* To determine the change in years, the config found under GlobalConfig.BrowseBy is used for this.
|
||||
* @param definition The metadata definition to fetch the first item for
|
||||
* @param metadataKeys The metadata fields to fetch the earliest date from (expects a date field)
|
||||
* @param scope The scope under which to fetch the earliest item for
|
||||
*/
|
||||
updateStartsWithOptions(definition: string, metadataKeys: string[], scope?: string) {
|
||||
const firstItemRD = this.browseService.getFirstItemFor(definition, scope, SortDirection.ASC);
|
||||
const lastItemRD = this.browseService.getFirstItemFor(definition, scope, SortDirection.DESC);
|
||||
this.subs.push(
|
||||
this.browseService.getFirstItemFor(definition, scope).subscribe((firstItemRD: RemoteData<Item>) => {
|
||||
let lowerLimit = this.appConfig.browseBy.defaultLowerLimit;
|
||||
if (hasValue(firstItemRD.payload)) {
|
||||
const date = firstItemRD.payload.firstMetadataValue(metadataKeys);
|
||||
if (isNotEmpty(date) && isValidDate(date)) {
|
||||
const dateObj = new Date(date);
|
||||
// TODO: it appears that getFullYear (based on local time) is sometimes unreliable. Switching to UTC.
|
||||
lowerLimit = isNaN(dateObj.getUTCFullYear()) ? lowerLimit : dateObj.getUTCFullYear();
|
||||
}
|
||||
}
|
||||
observableCombineLatest([firstItemRD, lastItemRD]).subscribe(([firstItem, lastItem]) => {
|
||||
let lowerLimit = this.getLimit(firstItem, metadataKeys, this.appConfig.browseBy.defaultLowerLimit);
|
||||
let upperLimit = this.getLimit(lastItem, metadataKeys, new Date().getUTCFullYear());
|
||||
const options = [];
|
||||
const currentYear = new Date().getUTCFullYear();
|
||||
const oneYearBreak = Math.floor((currentYear - this.appConfig.browseBy.oneYearLimit) / 5) * 5;
|
||||
const fiveYearBreak = Math.floor((currentYear - this.appConfig.browseBy.fiveYearLimit) / 10) * 10;
|
||||
const oneYearBreak = Math.floor((upperLimit - this.appConfig.browseBy.oneYearLimit) / 5) * 5;
|
||||
const fiveYearBreak = Math.floor((upperLimit - this.appConfig.browseBy.fiveYearLimit) / 10) * 10;
|
||||
if (lowerLimit <= fiveYearBreak) {
|
||||
lowerLimit -= 10;
|
||||
} else if (lowerLimit <= oneYearBreak) {
|
||||
@@ -103,7 +98,7 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent {
|
||||
} else {
|
||||
lowerLimit -= 1;
|
||||
}
|
||||
let i = currentYear;
|
||||
let i = upperLimit;
|
||||
while (i > lowerLimit) {
|
||||
options.push(i);
|
||||
if (i <= fiveYearBreak) {
|
||||
@@ -121,4 +116,24 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the year from the item metadata field or the limit.
|
||||
* @param itemRD the item remote data
|
||||
* @param metadataKeys The metadata fields to fetch the earliest date from (expects a date field)
|
||||
* @param limit the limit to use if the year can't be found in metadata
|
||||
* @private
|
||||
*/
|
||||
private getLimit(itemRD: RemoteData<Item>, metadataKeys: string[], limit: number): number {
|
||||
if (hasValue(itemRD.payload)) {
|
||||
const date = itemRD.payload.firstMetadataValue(metadataKeys);
|
||||
if (isNotEmpty(date) && isValidDate(date)) {
|
||||
const dateObj = new Date(date);
|
||||
// TODO: it appears that getFullYear (based on local time) is sometimes unreliable. Switching to UTC.
|
||||
return isNaN(dateObj.getUTCFullYear()) ? limit : dateObj.getUTCFullYear();
|
||||
} else {
|
||||
return new Date().getUTCFullYear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -151,8 +151,17 @@ export class BrowseByMetadataPageComponent implements OnInit, OnDestroy {
|
||||
).subscribe(([params, currentPage, currentSort]: [Params, PaginationComponentOptions, SortOptions]) => {
|
||||
this.browseId = params.id || this.defaultBrowseId;
|
||||
this.authority = params.authority;
|
||||
this.value = +params.value || params.value || '';
|
||||
this.startsWith = +params.startsWith || params.startsWith;
|
||||
|
||||
if (typeof params.value === 'string'){
|
||||
this.value = params.value.trim();
|
||||
} else {
|
||||
this.value = '';
|
||||
}
|
||||
|
||||
if (typeof params.startsWith === 'string'){
|
||||
this.startsWith = params.startsWith.trim();
|
||||
}
|
||||
|
||||
if (isNotEmpty(this.value)) {
|
||||
this.updatePageWithItems(
|
||||
browseParamsToOptions(params, currentPage, currentSort, this.browseId, this.fetchThumbnails), this.value, this.authority);
|
||||
@@ -305,7 +314,7 @@ export function browseParamsToOptions(params: any,
|
||||
metadata,
|
||||
paginationConfig,
|
||||
sortConfig,
|
||||
+params.startsWith || params.startsWith,
|
||||
params.startsWith,
|
||||
params.scope,
|
||||
fetchThumbnail
|
||||
);
|
||||
|
@@ -28,7 +28,8 @@
|
||||
[title]="'toggle ' + node.name"
|
||||
[attr.aria-label]="'toggle ' + node.name"
|
||||
(click)="toggleExpanded(node)"
|
||||
[ngClass]="(hasChild(null, node)| async) ? 'visible' : 'invisible'">
|
||||
[ngClass]="(hasChild(null, node)| async) ? 'visible' : 'invisible'"
|
||||
[attr.data-test]="(hasChild(null, node)| async) ? 'expand-button' : ''">
|
||||
<span class="{{node.isExpanded ? 'fa fa-chevron-down' : 'fa fa-chevron-right'}}"
|
||||
aria-hidden="true"></span>
|
||||
</button>
|
||||
|
@@ -17,6 +17,7 @@ export const AuthActionTypes = {
|
||||
AUTHENTICATED_SUCCESS: type('dspace/auth/AUTHENTICATED_SUCCESS'),
|
||||
CHECK_AUTHENTICATION_TOKEN: type('dspace/auth/CHECK_AUTHENTICATION_TOKEN'),
|
||||
CHECK_AUTHENTICATION_TOKEN_COOKIE: type('dspace/auth/CHECK_AUTHENTICATION_TOKEN_COOKIE'),
|
||||
SET_AUTH_COOKIE_STATUS: type('dspace/auth/SET_AUTH_COOKIE_STATUS'),
|
||||
RETRIEVE_AUTH_METHODS: type('dspace/auth/RETRIEVE_AUTH_METHODS'),
|
||||
RETRIEVE_AUTH_METHODS_SUCCESS: type('dspace/auth/RETRIEVE_AUTH_METHODS_SUCCESS'),
|
||||
RETRIEVE_AUTH_METHODS_ERROR: type('dspace/auth/RETRIEVE_AUTH_METHODS_ERROR'),
|
||||
@@ -150,6 +151,19 @@ export class CheckAuthenticationTokenCookieAction implements Action {
|
||||
public type: string = AuthActionTypes.CHECK_AUTHENTICATION_TOKEN_COOKIE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the authentication cookie status to flag an external authentication response.
|
||||
*/
|
||||
export class SetAuthCookieStatus implements Action {
|
||||
public type: string = AuthActionTypes.SET_AUTH_COOKIE_STATUS;
|
||||
|
||||
payload = false;
|
||||
|
||||
constructor(exists: boolean) {
|
||||
this.payload = exists;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign out.
|
||||
* @class LogOutAction
|
||||
@@ -425,6 +439,7 @@ export type AuthActions
|
||||
| AuthenticationSuccessAction
|
||||
| CheckAuthenticationTokenAction
|
||||
| CheckAuthenticationTokenCookieAction
|
||||
| SetAuthCookieStatus
|
||||
| RedirectWhenAuthenticationIsRequiredAction
|
||||
| RedirectWhenTokenExpiredAction
|
||||
| AddAuthenticationMessageAction
|
||||
|
@@ -214,12 +214,15 @@ describe('AuthEffects', () => {
|
||||
authenticated: true
|
||||
})
|
||||
);
|
||||
spyOn((authEffects as any).authService, 'setExternalAuthStatus');
|
||||
actions = hot('--a-', { a: { type: AuthActionTypes.CHECK_AUTHENTICATION_TOKEN_COOKIE } });
|
||||
|
||||
const expected = cold('--b-', { b: new RetrieveTokenAction() });
|
||||
|
||||
expect(authEffects.checkTokenCookie$).toBeObservable(expected);
|
||||
authEffects.checkTokenCookie$.subscribe(() => {
|
||||
expect(authServiceStub.setExternalAuthStatus).toHaveBeenCalled();
|
||||
expect(authServiceStub.isExternalAuthentication).toBeTrue();
|
||||
expect((authEffects as any).authorizationsService.invalidateAuthorizationsRequestCache).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
@@ -153,6 +153,7 @@ export class AuthEffects {
|
||||
return this.authService.checkAuthenticationCookie().pipe(
|
||||
map((response: AuthStatus) => {
|
||||
if (response.authenticated) {
|
||||
this.authService.setExternalAuthStatus(true);
|
||||
this.authorizationsService.invalidateAuthorizationsRequestCache();
|
||||
return new RetrieveTokenAction();
|
||||
} else {
|
||||
|
@@ -8,6 +8,7 @@ import {
|
||||
AuthenticationErrorAction,
|
||||
AuthenticationSuccessAction,
|
||||
CheckAuthenticationTokenAction,
|
||||
SetAuthCookieStatus,
|
||||
CheckAuthenticationTokenCookieAction,
|
||||
LogOutAction,
|
||||
LogOutErrorAction,
|
||||
@@ -219,6 +220,28 @@ describe('authReducer', () => {
|
||||
expect(newState).toEqual(state);
|
||||
});
|
||||
|
||||
it('should set the authentication cookie status in response to a SET_AUTH_COOKIE_STATUS action', () => {
|
||||
initialState = {
|
||||
authenticated: true,
|
||||
loaded: false,
|
||||
blocking: false,
|
||||
loading: true,
|
||||
externalAuth: false,
|
||||
idle: false
|
||||
};
|
||||
const action = new SetAuthCookieStatus(true);
|
||||
const newState = authReducer(initialState, action);
|
||||
state = {
|
||||
authenticated: true,
|
||||
loaded: false,
|
||||
blocking: false,
|
||||
loading: true,
|
||||
externalAuth: true,
|
||||
idle: false
|
||||
};
|
||||
expect(newState).toEqual(state);
|
||||
});
|
||||
|
||||
it('should properly set the state, in response to a LOG_OUT action', () => {
|
||||
initialState = {
|
||||
authenticated: true,
|
||||
|
@@ -10,7 +10,7 @@ import {
|
||||
RedirectWhenTokenExpiredAction,
|
||||
RefreshTokenSuccessAction,
|
||||
RetrieveAuthenticatedEpersonSuccessAction,
|
||||
RetrieveAuthMethodsSuccessAction,
|
||||
RetrieveAuthMethodsSuccessAction, SetAuthCookieStatus,
|
||||
SetRedirectUrlAction
|
||||
} from './auth.actions';
|
||||
// import models
|
||||
@@ -59,6 +59,8 @@ export interface AuthState {
|
||||
// all authentication Methods enabled at the backend
|
||||
authMethods?: AuthMethod[];
|
||||
|
||||
externalAuth?: boolean,
|
||||
|
||||
// true when the current user is idle
|
||||
idle: boolean;
|
||||
|
||||
@@ -73,6 +75,7 @@ const initialState: AuthState = {
|
||||
blocking: true,
|
||||
loading: false,
|
||||
authMethods: [],
|
||||
externalAuth: false,
|
||||
idle: false
|
||||
};
|
||||
|
||||
@@ -104,6 +107,11 @@ export function authReducer(state: any = initialState, action: AuthActions): Aut
|
||||
loading: true,
|
||||
});
|
||||
|
||||
case AuthActionTypes.SET_AUTH_COOKIE_STATUS:
|
||||
return Object.assign({}, state, {
|
||||
externalAuth: (action as SetAuthCookieStatus).payload
|
||||
});
|
||||
|
||||
case AuthActionTypes.AUTHENTICATED_ERROR:
|
||||
case AuthActionTypes.RETRIEVE_AUTHENTICATED_EPERSON_ERROR:
|
||||
return Object.assign({}, state, {
|
||||
|
@@ -25,7 +25,7 @@ import {
|
||||
import { CookieService } from '../services/cookie.service';
|
||||
import {
|
||||
getAuthenticatedUserId,
|
||||
getAuthenticationToken,
|
||||
getAuthenticationToken, getExternalAuthCookieStatus,
|
||||
getRedirectUrl,
|
||||
isAuthenticated,
|
||||
isAuthenticatedLoaded,
|
||||
@@ -36,7 +36,7 @@ import { AppState } from '../../app.reducer';
|
||||
import {
|
||||
CheckAuthenticationTokenAction,
|
||||
RefreshTokenAction,
|
||||
ResetAuthenticationMessagesAction,
|
||||
ResetAuthenticationMessagesAction, SetAuthCookieStatus,
|
||||
SetRedirectUrlAction,
|
||||
SetUserAsIdleAction,
|
||||
UnsetUserAsIdleAction
|
||||
@@ -156,6 +156,24 @@ export class AuthService {
|
||||
return this.store.pipe(select(isAuthenticatedLoaded));
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to set the external authentication status when authenticating via an
|
||||
* external authentication system (e.g. Shibboleth).
|
||||
* @param external
|
||||
*/
|
||||
public setExternalAuthStatus(external: boolean) {
|
||||
this.store.dispatch(new SetAuthCookieStatus(external));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if an external authentication system (e.g. Shibboleth) is being used
|
||||
* for authentication. Returns false otherwise.
|
||||
*/
|
||||
public isExternalAuthentication(): Observable<boolean> {
|
||||
return this.store.pipe(
|
||||
select(getExternalAuthCookieStatus));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the href link to authenticated user
|
||||
* @returns {string}
|
||||
|
@@ -116,6 +116,8 @@ const _getRedirectUrl = (state: AuthState) => state.redirectUrl;
|
||||
|
||||
const _getAuthenticationMethods = (state: AuthState) => state.authMethods;
|
||||
|
||||
const _getExternalAuthCookieStatus = (state: AuthState) => state.externalAuth;
|
||||
|
||||
/**
|
||||
* Returns true if the user is idle.
|
||||
* @function _isIdle
|
||||
@@ -178,6 +180,16 @@ export const isAuthenticated = createSelector(getAuthState, _isAuthenticated);
|
||||
*/
|
||||
export const isAuthenticatedLoaded = createSelector(getAuthState, _isAuthenticatedLoaded);
|
||||
|
||||
/**
|
||||
* Returns the authentication cookie status. Expect to be true when external authentication
|
||||
* is used.
|
||||
* @function getExternalAuthCookieStatus
|
||||
* @param {AuthState} state
|
||||
* @param {any} props
|
||||
* @return {boolean}
|
||||
*/
|
||||
export const getExternalAuthCookieStatus = createSelector(getAuthState, _getExternalAuthCookieStatus);
|
||||
|
||||
/**
|
||||
* Returns true if the authentication request is loading.
|
||||
* @function isAuthenticationLoading
|
||||
|
@@ -8,7 +8,7 @@ import { PostRequest } from '../data/request.models';
|
||||
import {
|
||||
XSRF_REQUEST_HEADER,
|
||||
XSRF_RESPONSE_HEADER
|
||||
} from '../xsrf/xsrf.interceptor';
|
||||
} from '../xsrf/xsrf.constants';
|
||||
|
||||
describe(`ServerAuthRequestService`, () => {
|
||||
let href: string;
|
||||
|
@@ -13,7 +13,7 @@ import {
|
||||
XSRF_REQUEST_HEADER,
|
||||
XSRF_RESPONSE_HEADER,
|
||||
DSPACE_XSRF_COOKIE
|
||||
} from '../xsrf/xsrf.interceptor';
|
||||
} from '../xsrf/xsrf.constants';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
|
@@ -22,6 +22,7 @@ import { BrowseEntrySearchOptions } from './browse-entry-search-options.model';
|
||||
import { HrefOnlyDataService } from '../data/href-only-data.service';
|
||||
import { followLink, FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
||||
import { BrowseDefinitionDataService } from './browse-definition-data.service';
|
||||
import { SortDirection } from '../cache/models/sort-options.model';
|
||||
|
||||
|
||||
export const BROWSE_LINKS_TO_FOLLOW: FollowLinkConfig<BrowseEntry | Item>[] = [
|
||||
@@ -160,8 +161,9 @@ export class BrowseService {
|
||||
* Get the first item for a metadata definition in an optional scope
|
||||
* @param definition
|
||||
* @param scope
|
||||
* @param sortDirection optional sort parameter
|
||||
*/
|
||||
getFirstItemFor(definition: string, scope?: string): Observable<RemoteData<Item>> {
|
||||
getFirstItemFor(definition: string, scope?: string, sortDirection?: SortDirection): Observable<RemoteData<Item>> {
|
||||
const href$ = this.getBrowseDefinitions().pipe(
|
||||
getBrowseDefinitionLinks(definition),
|
||||
hasValueOperator(),
|
||||
@@ -177,6 +179,9 @@ export class BrowseService {
|
||||
}
|
||||
args.push('page=0');
|
||||
args.push('size=1');
|
||||
if (sortDirection) {
|
||||
args.push('sort=default,' + sortDirection);
|
||||
}
|
||||
if (isNotEmpty(args)) {
|
||||
href = new URLCombiner(href, `?${args.join('&')}`).toString();
|
||||
}
|
||||
|
@@ -176,6 +176,7 @@ import { VocabularyEntryDetailsDataService } from './submission/vocabularies/voc
|
||||
import { IdentifierData } from '../shared/object-list/identifier-data/identifier-data.model';
|
||||
import { Subscription } from '../shared/subscriptions/models/subscription.model';
|
||||
import { SupervisionOrderDataService } from './supervision-order/supervision-order-data.service';
|
||||
import { ItemRequest } from './shared/item-request.model';
|
||||
|
||||
/**
|
||||
* When not in production, endpoint responses can be mocked for testing purposes
|
||||
@@ -369,6 +370,7 @@ export const models =
|
||||
AccessStatusObject,
|
||||
IdentifierData,
|
||||
Subscription,
|
||||
ItemRequest,
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
@@ -246,10 +246,10 @@ export abstract class BaseItemDataService extends IdentifiableDataService<Item>
|
||||
* Get the endpoint to move the item
|
||||
* @param itemId
|
||||
*/
|
||||
public getMoveItemEndpoint(itemId: string): Observable<string> {
|
||||
public getMoveItemEndpoint(itemId: string, inheritPolicies: boolean): Observable<string> {
|
||||
return this.halService.getEndpoint(this.linkPath).pipe(
|
||||
map((endpoint: string) => this.getIDHref(endpoint, itemId)),
|
||||
map((endpoint: string) => `${endpoint}/owningCollection`),
|
||||
map((endpoint: string) => `${endpoint}/owningCollection?inheritPolicies=${inheritPolicies}`)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -258,14 +258,14 @@ export abstract class BaseItemDataService extends IdentifiableDataService<Item>
|
||||
* @param itemId
|
||||
* @param collection
|
||||
*/
|
||||
public moveToCollection(itemId: string, collection: Collection): Observable<RemoteData<any>> {
|
||||
public moveToCollection(itemId: string, collection: Collection, inheritPolicies: boolean): Observable<RemoteData<any>> {
|
||||
const options: HttpOptions = Object.create({});
|
||||
let headers = new HttpHeaders();
|
||||
headers = headers.append('Content-Type', 'text/uri-list');
|
||||
options.headers = headers;
|
||||
|
||||
const requestId = this.requestService.generateRequestId();
|
||||
const hrefObs = this.getMoveItemEndpoint(itemId);
|
||||
const hrefObs = this.getMoveItemEndpoint(itemId, inheritPolicies);
|
||||
|
||||
hrefObs.pipe(
|
||||
find((href: string) => hasValue(href)),
|
||||
|
@@ -62,25 +62,33 @@ describe('LocaleService test suite', () => {
|
||||
});
|
||||
|
||||
describe('getCurrentLanguageCode', () => {
|
||||
it('should return language saved on cookie', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(translateService, 'getLangs').and.returnValue(langList);
|
||||
});
|
||||
|
||||
it('should return the language saved on cookie if it\'s a valid & active language', () => {
|
||||
spyOnGet.and.returnValue('de');
|
||||
expect(service.getCurrentLanguageCode()).toBe('de');
|
||||
});
|
||||
|
||||
describe('', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(translateService, 'getLangs').and.returnValue(langList);
|
||||
});
|
||||
it('should return the default language if the cookie language is disabled', () => {
|
||||
spyOnGet.and.returnValue('disabled');
|
||||
expect(service.getCurrentLanguageCode()).toBe('en');
|
||||
});
|
||||
|
||||
it('should return language from browser setting', () => {
|
||||
spyOn(translateService, 'getBrowserLang').and.returnValue('it');
|
||||
expect(service.getCurrentLanguageCode()).toBe('it');
|
||||
});
|
||||
it('should return the default language if the cookie language does not exist', () => {
|
||||
spyOnGet.and.returnValue('does-not-exist');
|
||||
expect(service.getCurrentLanguageCode()).toBe('en');
|
||||
});
|
||||
|
||||
it('should return default language from config', () => {
|
||||
spyOn(translateService, 'getBrowserLang').and.returnValue('fr');
|
||||
expect(service.getCurrentLanguageCode()).toBe('en');
|
||||
});
|
||||
it('should return language from browser setting', () => {
|
||||
spyOn(translateService, 'getBrowserLang').and.returnValue('it');
|
||||
expect(service.getCurrentLanguageCode()).toBe('it');
|
||||
});
|
||||
|
||||
it('should return default language from config', () => {
|
||||
spyOn(translateService, 'getBrowserLang').and.returnValue('fr');
|
||||
expect(service.getCurrentLanguageCode()).toBe('en');
|
||||
});
|
||||
});
|
||||
|
||||
|
@@ -11,6 +11,7 @@ import { map, mergeMap, take } from 'rxjs/operators';
|
||||
import { NativeWindowRef, NativeWindowService } from '../services/window.service';
|
||||
import { RouteService } from '../services/route.service';
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
import { LangConfig } from '../../../config/lang-config.interface';
|
||||
|
||||
export const LANG_COOKIE = 'dsLanguage';
|
||||
|
||||
@@ -52,8 +53,7 @@ export class LocaleService {
|
||||
getCurrentLanguageCode(): string {
|
||||
// Attempt to get the language from a cookie
|
||||
let lang = this.getLanguageCodeFromCookie();
|
||||
if (isEmpty(lang)) {
|
||||
// Cookie not found
|
||||
if (isEmpty(lang) || environment.languages.find((langConfig: LangConfig) => langConfig.code === lang && langConfig.active) === undefined) {
|
||||
// Attempt to get the browser language from the user
|
||||
if (this.translate.getLangs().includes(this.translate.getBrowserLang())) {
|
||||
lang = this.translate.getBrowserLang();
|
||||
|
33
src/app/core/xsrf/xsrf.constants.ts
Normal file
33
src/app/core/xsrf/xsrf.constants.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* XSRF / CSRF related constants
|
||||
*/
|
||||
|
||||
/**
|
||||
* Name of CSRF/XSRF header we (client) may SEND in requests to backend.
|
||||
* (This is a standard header name for XSRF/CSRF defined by Angular)
|
||||
*/
|
||||
export const XSRF_REQUEST_HEADER = 'X-XSRF-TOKEN';
|
||||
|
||||
/**
|
||||
* Name of CSRF/XSRF header we (client) may RECEIVE in responses from backend
|
||||
* This header is defined by DSpace backend, see https://github.com/DSpace/RestContract/blob/main/csrf-tokens.md
|
||||
*/
|
||||
export const XSRF_RESPONSE_HEADER = 'DSPACE-XSRF-TOKEN';
|
||||
|
||||
/**
|
||||
* Name of client-side Cookie where we store the CSRF/XSRF token between requests.
|
||||
* This cookie is only available to client, and should be updated whenever a new XSRF_RESPONSE_HEADER
|
||||
* is found in a response from the backend.
|
||||
*/
|
||||
export const XSRF_COOKIE = 'XSRF-TOKEN';
|
||||
|
||||
/**
|
||||
* Name of server-side cookie the backend expects the XSRF token to be in.
|
||||
* When the backend receives a modifying request, it will validate the CSRF/XSRF token by looking
|
||||
* for a match between the XSRF_REQUEST_HEADER and this Cookie. For more details see
|
||||
* https://github.com/DSpace/RestContract/blob/main/csrf-tokens.md
|
||||
*
|
||||
* NOTE: This Cookie is NOT readable to the client/UI. It is only readable to the backend and will
|
||||
* be sent along automatically by the user's browser.
|
||||
*/
|
||||
export const DSPACE_XSRF_COOKIE = 'DSPACE-XSRF-COOKIE';
|
@@ -12,15 +12,7 @@ import { Observable, throwError } from 'rxjs';
|
||||
import { tap, catchError } from 'rxjs/operators';
|
||||
import { RESTURLCombiner } from '../url-combiner/rest-url-combiner';
|
||||
import { CookieService } from '../services/cookie.service';
|
||||
|
||||
// Name of XSRF header we may send in requests to backend (this is a standard name defined by Angular)
|
||||
export const XSRF_REQUEST_HEADER = 'X-XSRF-TOKEN';
|
||||
// Name of XSRF header we may receive in responses from backend
|
||||
export const XSRF_RESPONSE_HEADER = 'DSPACE-XSRF-TOKEN';
|
||||
// Name of cookie where we store the XSRF token
|
||||
export const XSRF_COOKIE = 'XSRF-TOKEN';
|
||||
// Name of cookie the backend expects the XSRF token to be in
|
||||
export const DSPACE_XSRF_COOKIE = 'DSPACE-XSRF-COOKIE';
|
||||
import { XSRF_COOKIE, XSRF_REQUEST_HEADER, XSRF_RESPONSE_HEADER } from './xsrf.constants';
|
||||
|
||||
/**
|
||||
* Custom Http Interceptor intercepting Http Requests & Responses to
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<ds-themed-results-back-button *ngIf="showBackButton | async" [back]="back"></ds-themed-results-back-button>
|
||||
<div class="d-flex flex-row">
|
||||
<ds-item-page-title-field [item]="object" class="mr-auto">
|
||||
</ds-item-page-title-field>
|
||||
<ds-themed-item-page-title-field [item]="object" class="mr-auto">
|
||||
</ds-themed-item-page-title-field>
|
||||
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<ds-themed-results-back-button *ngIf="showBackButton | async" [back]="back"></ds-themed-results-back-button>
|
||||
<div class="d-flex flex-row">
|
||||
<ds-item-page-title-field [item]="object" class="mr-auto">
|
||||
</ds-item-page-title-field>
|
||||
<ds-themed-item-page-title-field [item]="object" class="mr-auto">
|
||||
</ds-themed-item-page-title-field>
|
||||
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<ds-themed-results-back-button *ngIf="showBackButton | async" [back]="back"></ds-themed-results-back-button>
|
||||
<div class="d-flex flex-row">
|
||||
<ds-item-page-title-field [item]="object" class="mr-auto">
|
||||
</ds-item-page-title-field>
|
||||
<ds-themed-item-page-title-field [item]="object" class="mr-auto">
|
||||
</ds-themed-item-page-title-field>
|
||||
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<ds-themed-results-back-button *ngIf="showBackButton | async" [back]="back"></ds-themed-results-back-button>
|
||||
<div class="d-flex flex-row">
|
||||
<ds-item-page-title-field [item]="object" class="mr-auto">
|
||||
</ds-item-page-title-field>
|
||||
<ds-themed-item-page-title-field [item]="object" class="mr-auto">
|
||||
</ds-themed-item-page-title-field>
|
||||
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<ds-themed-results-back-button *ngIf="showBackButton | async" [back]="back"></ds-themed-results-back-button>
|
||||
<div class="d-flex flex-row">
|
||||
<ds-item-page-title-field class="mr-auto" [item]="object">
|
||||
</ds-item-page-title-field>
|
||||
<ds-themed-item-page-title-field class="mr-auto" [item]="object">
|
||||
</ds-themed-item-page-title-field>
|
||||
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<ds-themed-results-back-button *ngIf="showBackButton | async" [back]="back"></ds-themed-results-back-button>
|
||||
<div class="d-flex flex-row">
|
||||
<ds-item-page-title-field [item]="object" class="mr-auto">
|
||||
</ds-item-page-title-field>
|
||||
<ds-themed-item-page-title-field [item]="object" class="mr-auto">
|
||||
</ds-themed-item-page-title-field>
|
||||
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
@@ -7,7 +7,7 @@ import { FooterComponent } from './footer.component';
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-themed-footer',
|
||||
styleUrls: ['footer.component.scss'],
|
||||
styleUrls: [],
|
||||
templateUrl: '../shared/theme-support/themed.component.html',
|
||||
})
|
||||
export class ThemedFooterComponent extends ThemedComponent<FooterComponent> {
|
||||
@@ -20,6 +20,6 @@ export class ThemedFooterComponent extends ThemedComponent<FooterComponent> {
|
||||
}
|
||||
|
||||
protected importUnthemedComponent(): Promise<any> {
|
||||
return import(`./footer.component`);
|
||||
return import('./footer.component');
|
||||
}
|
||||
}
|
||||
|
@@ -3,11 +3,11 @@ import { ThemedComponent } from '../shared/theme-support/themed.component';
|
||||
import { HeaderNavbarWrapperComponent } from './header-navbar-wrapper.component';
|
||||
|
||||
/**
|
||||
* Themed wrapper for BreadcrumbsComponent
|
||||
* Themed wrapper for {@link HeaderNavbarWrapperComponent}
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-themed-header-navbar-wrapper',
|
||||
styleUrls: ['./themed-header-navbar-wrapper.component.scss'],
|
||||
styleUrls: [],
|
||||
templateUrl: '../shared/theme-support/themed.component.html',
|
||||
})
|
||||
export class ThemedHeaderNavbarWrapperComponent extends ThemedComponent<HeaderNavbarWrapperComponent> {
|
||||
@@ -20,6 +20,6 @@ export class ThemedHeaderNavbarWrapperComponent extends ThemedComponent<HeaderNa
|
||||
}
|
||||
|
||||
protected importUnthemedComponent(): Promise<any> {
|
||||
return import(`./header-navbar-wrapper.component`);
|
||||
return import('./header-navbar-wrapper.component');
|
||||
}
|
||||
}
|
||||
|
@@ -8,8 +8,6 @@ import { Component } from '@angular/core';
|
||||
templateUrl: '../shared/theme-support/themed.component.html',
|
||||
})
|
||||
export class ThemedHomePageComponent extends ThemedComponent<HomePageComponent> {
|
||||
protected inAndOutputNames: (keyof HomePageComponent & keyof this)[];
|
||||
|
||||
|
||||
protected getComponentName(): string {
|
||||
return 'HomePageComponent';
|
||||
|
@@ -0,0 +1,27 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ThemedComponent } from '../../../shared/theme-support/themed.component';
|
||||
import { FeedbackFormComponent } from './feedback-form.component';
|
||||
|
||||
/**
|
||||
* Themed wrapper for {@link FeedbackFormComponent}
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-themed-feedback-form',
|
||||
styleUrls: [],
|
||||
templateUrl: '../../../shared/theme-support/themed.component.html',
|
||||
})
|
||||
export class ThemedFeedbackFormComponent extends ThemedComponent<FeedbackFormComponent> {
|
||||
|
||||
protected getComponentName(): string {
|
||||
return 'FeedbackFormComponent';
|
||||
}
|
||||
|
||||
protected importThemedComponent(themeName: string): Promise<any> {
|
||||
return import(`../../../../themes/${themeName}/app/info/feedback/feedback-form/feedback-form.component`);
|
||||
}
|
||||
|
||||
protected importUnthemedComponent(): Promise<any> {
|
||||
return import('./feedback-form.component');
|
||||
}
|
||||
|
||||
}
|
@@ -1,3 +1,3 @@
|
||||
<div class="container">
|
||||
<ds-feedback-form></ds-feedback-form>
|
||||
</div>
|
||||
<ds-themed-feedback-form></ds-themed-feedback-form>
|
||||
</div>
|
||||
|
@@ -10,6 +10,7 @@ import { ThemedEndUserAgreementComponent } from './end-user-agreement/themed-end
|
||||
import { ThemedPrivacyComponent } from './privacy/themed-privacy.component';
|
||||
import { FeedbackComponent } from './feedback/feedback.component';
|
||||
import { FeedbackFormComponent } from './feedback/feedback-form/feedback-form.component';
|
||||
import { ThemedFeedbackFormComponent } from './feedback/feedback-form/themed-feedback-form.component';
|
||||
import { ThemedFeedbackComponent } from './feedback/themed-feedback.component';
|
||||
import { FeedbackGuard } from '../core/feedback/feedback.guard';
|
||||
|
||||
@@ -23,6 +24,7 @@ const DECLARATIONS = [
|
||||
ThemedPrivacyComponent,
|
||||
FeedbackComponent,
|
||||
FeedbackFormComponent,
|
||||
ThemedFeedbackFormComponent,
|
||||
ThemedFeedbackComponent
|
||||
];
|
||||
|
||||
|
@@ -134,9 +134,10 @@ describe('ItemMoveComponent', () => {
|
||||
});
|
||||
comp.selectedCollectionName = 'selected-collection-id';
|
||||
comp.selectedCollection = collection1;
|
||||
comp.inheritPolicies = false;
|
||||
comp.moveToCollection();
|
||||
|
||||
expect(itemDataService.moveToCollection).toHaveBeenCalledWith('item-id', collection1);
|
||||
expect(itemDataService.moveToCollection).toHaveBeenCalledWith('item-id', collection1, false);
|
||||
});
|
||||
it('should call notificationsService success message on success', () => {
|
||||
comp.moveToCollection();
|
||||
|
@@ -104,7 +104,7 @@ export class ItemMoveComponent implements OnInit {
|
||||
*/
|
||||
moveToCollection() {
|
||||
this.processing = true;
|
||||
const move$ = this.itemDataService.moveToCollection(this.item.id, this.selectedCollection)
|
||||
const move$ = this.itemDataService.moveToCollection(this.item.id, this.selectedCollection, this.inheritPolicies)
|
||||
.pipe(getFirstCompletedRemoteData());
|
||||
|
||||
move$.subscribe((response: RemoteData<any>) => {
|
||||
|
@@ -6,9 +6,8 @@
|
||||
<ds-view-tracker [object]="item"></ds-view-tracker>
|
||||
<div *ngIf="!item.isWithdrawn || (isAdmin$|async)" class="full-item-info">
|
||||
<div class="d-flex flex-row">
|
||||
<ds-item-page-title-field class="mr-auto" [item]="item"></ds-item-page-title-field>
|
||||
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||
|
||||
<ds-themed-item-page-title-field class="mr-auto" [item]="item"></ds-themed-item-page-title-field>
|
||||
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||
</div>
|
||||
<div class="simple-view-link my-3" *ngIf="!fromSubmissionObject">
|
||||
<a class="btn btn-outline-primary" [routerLink]="[(itemPageRoute$ | async)]">
|
||||
|
@@ -34,8 +34,11 @@ import { ResearchEntitiesModule } from '../entity-groups/research-entities/resea
|
||||
import { ThemedItemPageComponent } from './simple/themed-item-page.component';
|
||||
import { ThemedFullItemPageComponent } from './full/themed-full-item-page.component';
|
||||
import { MediaViewerComponent } from './media-viewer/media-viewer.component';
|
||||
import { ThemedMediaViewerComponent } from './media-viewer/themed-media-viewer.component';
|
||||
import { MediaViewerVideoComponent } from './media-viewer/media-viewer-video/media-viewer-video.component';
|
||||
import { ThemedMediaViewerVideoComponent } from './media-viewer/media-viewer-video/themed-media-viewer-video.component';
|
||||
import { MediaViewerImageComponent } from './media-viewer/media-viewer-image/media-viewer-image.component';
|
||||
import { ThemedMediaViewerImageComponent } from './media-viewer/media-viewer-image/themed-media-viewer-image.component';
|
||||
import { NgxGalleryModule } from '@kolkov/ngx-gallery';
|
||||
import { MiradorViewerComponent } from './mirador-viewer/mirador-viewer.component';
|
||||
import { VersionPageComponent } from './version-page/version-page/version-page.component';
|
||||
@@ -58,7 +61,6 @@ import {
|
||||
ThemedFullFileSectionComponent
|
||||
} from './full/field-components/file-section/themed-full-file-section.component';
|
||||
|
||||
|
||||
const ENTRY_COMPONENTS = [
|
||||
// put only entry components that use custom decorator
|
||||
PublicationComponent,
|
||||
@@ -87,8 +89,11 @@ const DECLARATIONS = [
|
||||
UploadBitstreamComponent,
|
||||
AbstractIncrementalListComponent,
|
||||
MediaViewerComponent,
|
||||
ThemedMediaViewerComponent,
|
||||
MediaViewerVideoComponent,
|
||||
ThemedMediaViewerVideoComponent,
|
||||
MediaViewerImageComponent,
|
||||
ThemedMediaViewerImageComponent,
|
||||
MiradorViewerComponent,
|
||||
VersionPageComponent,
|
||||
OrcidPageComponent,
|
||||
|
@@ -1,6 +1,20 @@
|
||||
.ngx-gallery {
|
||||
display: inline-block;
|
||||
margin-bottom: 20px;
|
||||
width: 340px !important;
|
||||
height: 279px !important;
|
||||
:host ::ng-deep {
|
||||
.ngx-gallery {
|
||||
width: unset !important;
|
||||
height: unset !important;
|
||||
}
|
||||
|
||||
ngx-gallery-image {
|
||||
max-width: 340px !important;
|
||||
|
||||
.ngx-gallery-image {
|
||||
background-position: left;
|
||||
}
|
||||
}
|
||||
|
||||
ngx-gallery-image:after {
|
||||
padding-top: 75%;
|
||||
display: block;
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { Component, Input, OnChanges, OnInit } from '@angular/core';
|
||||
import { NgxGalleryImage, NgxGalleryOptions } from '@kolkov/ngx-gallery';
|
||||
import { MediaViewerItem } from '../../../core/shared/media-viewer-item.model';
|
||||
import { NgxGalleryAnimation } from '@kolkov/ngx-gallery';
|
||||
@@ -13,28 +13,28 @@ import { AuthService } from '../../../core/auth/auth.service';
|
||||
templateUrl: './media-viewer-image.component.html',
|
||||
styleUrls: ['./media-viewer-image.component.scss'],
|
||||
})
|
||||
export class MediaViewerImageComponent implements OnInit {
|
||||
export class MediaViewerImageComponent implements OnChanges, OnInit {
|
||||
@Input() images: MediaViewerItem[];
|
||||
@Input() preview?: boolean;
|
||||
@Input() image?: string;
|
||||
|
||||
loggedin: boolean;
|
||||
thumbnailPlaceholder = './assets/images/replacement_image.svg';
|
||||
|
||||
galleryOptions: NgxGalleryOptions[];
|
||||
galleryImages: NgxGalleryImage[];
|
||||
galleryOptions: NgxGalleryOptions[] = [];
|
||||
|
||||
galleryImages: NgxGalleryImage[] = [];
|
||||
|
||||
/**
|
||||
* Whether or not the current user is authenticated
|
||||
*/
|
||||
isAuthenticated$: Observable<boolean>;
|
||||
|
||||
constructor(private authService: AuthService) {}
|
||||
constructor(
|
||||
protected authService: AuthService,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Thi method sets up the gallery settings and data
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
this.isAuthenticated$ = this.authService.isAuthenticated();
|
||||
ngOnChanges(): void {
|
||||
this.galleryOptions = [
|
||||
{
|
||||
preview: this.preview !== undefined ? this.preview : true,
|
||||
@@ -50,7 +50,6 @@ export class MediaViewerImageComponent implements OnInit {
|
||||
previewFullscreen: true,
|
||||
},
|
||||
];
|
||||
|
||||
if (this.image) {
|
||||
this.galleryImages = [
|
||||
{
|
||||
@@ -64,25 +63,30 @@ export class MediaViewerImageComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.isAuthenticated$ = this.authService.isAuthenticated();
|
||||
this.ngOnChanges();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method convert an array of MediaViewerItem into NgxGalleryImage array
|
||||
* @param medias input NgxGalleryImage array
|
||||
*/
|
||||
convertToGalleryImage(medias: MediaViewerItem[]): NgxGalleryImage[] {
|
||||
const mappadImages = [];
|
||||
const mappedImages = [];
|
||||
for (const image of medias) {
|
||||
if (image.format === 'image') {
|
||||
mappadImages.push({
|
||||
mappedImages.push({
|
||||
small: image.thumbnail
|
||||
? image.thumbnail
|
||||
: './assets/images/replacement_image.svg',
|
||||
: this.thumbnailPlaceholder,
|
||||
medium: image.thumbnail
|
||||
? image.thumbnail
|
||||
: './assets/images/replacement_image.svg',
|
||||
: this.thumbnailPlaceholder,
|
||||
big: image.bitstream._links.content.href,
|
||||
});
|
||||
}
|
||||
}
|
||||
return mappadImages;
|
||||
return mappedImages;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,38 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { ThemedComponent } from '../../../shared/theme-support/themed.component';
|
||||
import { MediaViewerImageComponent } from './media-viewer-image.component';
|
||||
import { MediaViewerItem } from '../../../core/shared/media-viewer-item.model';
|
||||
|
||||
/**
|
||||
* Themed wrapper for {@link MediaViewerImageComponent}.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-themed-media-viewer-image',
|
||||
styleUrls: [],
|
||||
templateUrl: '../../../shared/theme-support/themed.component.html',
|
||||
})
|
||||
export class ThemedMediaViewerImageComponent extends ThemedComponent<MediaViewerImageComponent> {
|
||||
|
||||
@Input() images: MediaViewerItem[];
|
||||
@Input() preview?: boolean;
|
||||
@Input() image?: string;
|
||||
|
||||
protected inAndOutputNames: (keyof MediaViewerImageComponent & keyof this)[] = [
|
||||
'images',
|
||||
'preview',
|
||||
'image',
|
||||
];
|
||||
|
||||
protected getComponentName(): string {
|
||||
return 'MediaViewerImageComponent';
|
||||
}
|
||||
|
||||
protected importThemedComponent(themeName: string): Promise<any> {
|
||||
return import(`../../../../themes/${themeName}/app/item-page/media-viewer/media-viewer-image/media-viewer-image.component`);
|
||||
}
|
||||
|
||||
protected importUnthemedComponent(): Promise<any> {
|
||||
return import('./media-viewer-image.component');
|
||||
}
|
||||
|
||||
}
|
@@ -1,23 +1,22 @@
|
||||
<video
|
||||
crossorigin="anonymous"
|
||||
#media
|
||||
[src]="filteredMedias[currentIndex].bitstream._links.content.href"
|
||||
[src]="medias[currentIndex].bitstream._links.content.href"
|
||||
id="singleVideo"
|
||||
[poster]="
|
||||
filteredMedias[currentIndex].thumbnail ||
|
||||
replacements[filteredMedias[currentIndex].format]
|
||||
medias[currentIndex].thumbnail ||
|
||||
replacements[medias[currentIndex].format]
|
||||
"
|
||||
preload="none"
|
||||
controls
|
||||
>
|
||||
<ng-container *ngIf="getMediaCap(filteredMedias[currentIndex].bitstream.name) as capInfos">
|
||||
<ng-container *ngIf="getMediaCap(medias[currentIndex].bitstream.name, captions) as capInfos">
|
||||
<ng-container *ngFor="let capInfo of capInfos">
|
||||
<track [src]="capInfo.src" [label]="capInfo.langLabel" [srclang]="capInfo.srclang" />
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
</video>
|
||||
<div class="buttons" *ngIf="filteredMedias?.length > 1">
|
||||
<div class="buttons" *ngIf="medias?.length > 1">
|
||||
<button
|
||||
class="btn btn-primary previous"
|
||||
[disabled]="currentIndex === 0"
|
||||
@@ -28,7 +27,7 @@
|
||||
|
||||
<button
|
||||
class="btn btn-primary next"
|
||||
[disabled]="currentIndex === filteredMedias.length - 1"
|
||||
[disabled]="currentIndex === medias.length - 1"
|
||||
(click)="nextMedia()"
|
||||
>
|
||||
{{ "media-viewer.next" | translate }}
|
||||
@@ -44,7 +43,7 @@
|
||||
<div ngbDropdownMenu aria-labelledby="dropdownBasic1">
|
||||
<button
|
||||
ngbDropdownItem
|
||||
*ngFor="let item of filteredMedias; index as indexOfelement"
|
||||
*ngFor="let item of medias; index as indexOfelement"
|
||||
class="list-element"
|
||||
(click)="selectedMedia(indexOfelement)"
|
||||
>
|
||||
|
@@ -1,4 +1,10 @@
|
||||
video {
|
||||
width: 340px;
|
||||
height: 279px;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
max-width: 340px;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
gap: .25rem;
|
||||
}
|
||||
|
@@ -83,7 +83,6 @@ describe('MediaViewerVideoComponent', () => {
|
||||
fixture = TestBed.createComponent(MediaViewerVideoComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.medias = mockMediaViewerItem;
|
||||
component.filteredMedias = mockMediaViewerItem;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
@@ -94,7 +93,6 @@ describe('MediaViewerVideoComponent', () => {
|
||||
describe('should show controller buttons when the having mode then one video', () => {
|
||||
beforeEach(() => {
|
||||
component.medias = mockMediaViewerItems;
|
||||
component.filteredMedias = mockMediaViewerItems;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
|
@@ -1,22 +1,24 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { MediaViewerItem } from '../../../core/shared/media-viewer-item.model';
|
||||
import { languageHelper } from './language-helper';
|
||||
import { CaptionInfo} from './caption-info';
|
||||
import { CaptionInfo } from './caption-info';
|
||||
import { Bitstream } from 'src/app/core/shared/bitstream.model';
|
||||
|
||||
/**
|
||||
* This componenet renders a video viewer and playlist for the media viewer
|
||||
* This component renders a video viewer and playlist for the media viewer
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-media-viewer-video',
|
||||
templateUrl: './media-viewer-video.component.html',
|
||||
styleUrls: ['./media-viewer-video.component.scss'],
|
||||
})
|
||||
export class MediaViewerVideoComponent implements OnInit {
|
||||
export class MediaViewerVideoComponent {
|
||||
@Input() medias: MediaViewerItem[];
|
||||
|
||||
filteredMedias: MediaViewerItem[];
|
||||
@Input() captions: Bitstream[] = [];
|
||||
|
||||
isCollapsed = false;
|
||||
|
||||
isCollapsed: boolean;
|
||||
currentIndex = 0;
|
||||
|
||||
replacements = {
|
||||
@@ -24,13 +26,6 @@ export class MediaViewerVideoComponent implements OnInit {
|
||||
audio: './assets/images/replacement_audio.svg',
|
||||
};
|
||||
|
||||
replacementThumbnail: string;
|
||||
|
||||
ngOnInit() {
|
||||
this.isCollapsed = false;
|
||||
this.filteredMedias = this.medias.filter((media) => media.format === 'audio' || media.format === 'video');
|
||||
}
|
||||
|
||||
/**
|
||||
* This method check if there is caption file for the media
|
||||
* The caption file name is the media name plus "-" following two letter
|
||||
@@ -41,29 +36,24 @@ export class MediaViewerVideoComponent implements OnInit {
|
||||
* Two letter language code reference
|
||||
* https://www.w3schools.com/tags/ref_language_codes.asp
|
||||
*/
|
||||
getMediaCap(name: string): CaptionInfo[] {
|
||||
let filteredCapMedias: MediaViewerItem[];
|
||||
let capInfos: CaptionInfo[] = [];
|
||||
filteredCapMedias = this.medias
|
||||
.filter((media) => media.mimetype === 'text/vtt')
|
||||
.filter((media) => media.bitstream.name.substring(0, (media.bitstream.name.length - 7) ).toLowerCase() === name.toLowerCase());
|
||||
getMediaCap(name: string, captions: Bitstream[]): CaptionInfo[] {
|
||||
const capInfos: CaptionInfo[] = [];
|
||||
const filteredCapMedias: Bitstream[] = captions
|
||||
.filter((media: Bitstream) => media.name.substring(0, (media.name.length - 7)).toLowerCase() === name.toLowerCase());
|
||||
|
||||
if (filteredCapMedias) {
|
||||
filteredCapMedias
|
||||
.forEach((media, index) => {
|
||||
let srclang: string = media.bitstream.name.slice(-6, -4).toLowerCase();
|
||||
capInfos.push(new CaptionInfo(
|
||||
media.bitstream._links.content.href,
|
||||
srclang,
|
||||
languageHelper[srclang]
|
||||
));
|
||||
});
|
||||
for (const media of filteredCapMedias) {
|
||||
let srclang: string = media.name.slice(-6, -4).toLowerCase();
|
||||
capInfos.push(new CaptionInfo(
|
||||
media._links.content.href,
|
||||
srclang,
|
||||
languageHelper[srclang],
|
||||
));
|
||||
}
|
||||
return capInfos;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sets the reviced index into currentIndex
|
||||
* This method sets the received index into currentIndex
|
||||
* @param index Selected index
|
||||
*/
|
||||
selectedMedia(index: number) {
|
||||
@@ -71,14 +61,14 @@ export class MediaViewerVideoComponent implements OnInit {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method increade the number of the currentIndex
|
||||
* This method increases the number of the currentIndex
|
||||
*/
|
||||
nextMedia() {
|
||||
this.currentIndex++;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method decrese the number of the currentIndex
|
||||
* This method decreases the number of the currentIndex
|
||||
*/
|
||||
prevMedia() {
|
||||
this.currentIndex--;
|
||||
|
@@ -0,0 +1,38 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { ThemedComponent } from '../../../shared/theme-support/themed.component';
|
||||
import { MediaViewerItem } from '../../../core/shared/media-viewer-item.model';
|
||||
import { MediaViewerVideoComponent } from './media-viewer-video.component';
|
||||
import { Bitstream } from '../../../core/shared/bitstream.model';
|
||||
|
||||
/**
|
||||
* Themed wrapper for {@link MediaViewerVideoComponent}.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-themed-media-viewer-video',
|
||||
styleUrls: [],
|
||||
templateUrl: '../../../shared/theme-support/themed.component.html',
|
||||
})
|
||||
export class ThemedMediaViewerVideoComponent extends ThemedComponent<MediaViewerVideoComponent> {
|
||||
|
||||
@Input() medias: MediaViewerItem[];
|
||||
|
||||
@Input() captions: Bitstream[];
|
||||
|
||||
protected inAndOutputNames: (keyof MediaViewerVideoComponent & keyof this)[] = [
|
||||
'medias',
|
||||
'captions',
|
||||
];
|
||||
|
||||
protected getComponentName(): string {
|
||||
return 'MediaViewerVideoComponent';
|
||||
}
|
||||
|
||||
protected importThemedComponent(themeName: string): Promise<any> {
|
||||
return import(`../../../../themes/${themeName}/app/item-page/media-viewer/media-viewer-video/media-viewer-video.component`);
|
||||
}
|
||||
|
||||
protected importUnthemedComponent(): Promise<any> {
|
||||
return import('./media-viewer-video.component');
|
||||
}
|
||||
|
||||
}
|
@@ -5,32 +5,23 @@
|
||||
[showMessage]="false"
|
||||
></ds-themed-loading>
|
||||
<div class="media-viewer" *ngIf="!isLoading">
|
||||
<ng-container *ngIf="mediaList.length > 0">
|
||||
<ng-container *ngIf="videoOptions">
|
||||
<ng-container
|
||||
*ngIf="
|
||||
mediaList[0]?.format === 'video' || mediaList[0]?.format === 'audio'
|
||||
"
|
||||
>
|
||||
<ds-media-viewer-video [medias]="mediaList"></ds-media-viewer-video>
|
||||
<ng-container *ngIf="mediaList.length > 0; else showThumbnail">
|
||||
<ng-container *ngVar="mediaOptions.video && ['audio', 'video'].includes(mediaList[0]?.format) as showVideo">
|
||||
<ng-container *ngVar="mediaOptions.image && mediaList[0]?.format === 'image' as showImage">
|
||||
<ds-themed-media-viewer-video *ngIf="showVideo" [medias]="mediaList" [captions]="captions$ | async"></ds-themed-media-viewer-video>
|
||||
<ds-themed-media-viewer-image *ngIf="showImage" [images]="mediaList"></ds-themed-media-viewer-image>
|
||||
<ng-container *ngIf="showImage || showVideo; else showThumbnail"></ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="mediaList[0]?.format === 'image'">
|
||||
<ds-media-viewer-image [images]="mediaList"></ds-media-viewer-image>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<ng-container
|
||||
*ngIf="
|
||||
((mediaList[0]?.format !== 'image') &&
|
||||
(!videoOptions || mediaList[0]?.format !== 'video') &&
|
||||
(!videoOptions || mediaList[0]?.format !== 'audio')) ||
|
||||
mediaList.length === 0
|
||||
"
|
||||
>
|
||||
<ds-media-viewer-image
|
||||
[image]="mediaList[0]?.thumbnail || thumbnailPlaceholder"
|
||||
[preview]="false"
|
||||
></ds-media-viewer-image>
|
||||
</ng-container>
|
||||
</div>
|
||||
<ng-template #showThumbnail>
|
||||
<ds-themed-media-viewer-image *ngIf="mediaOptions.image && mediaOptions.video"
|
||||
[image]="(thumbnailsRD$ | async)?.payload?.page[0]?._links.content.href || thumbnailPlaceholder"
|
||||
[preview]="false"
|
||||
></ds-themed-media-viewer-image>
|
||||
<ds-thumbnail *ngIf="!(mediaOptions.image && mediaOptions.video)"
|
||||
[thumbnail]="(thumbnailsRD$ | async)?.payload?.page[0]">
|
||||
</ds-thumbnail>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
@@ -61,7 +61,7 @@ describe('MediaViewerComponent', () => {
|
||||
);
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
return TestBed.configureTestingModule({
|
||||
imports: [
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
@@ -94,7 +94,10 @@ describe('MediaViewerComponent', () => {
|
||||
describe('when the bitstreams are loading', () => {
|
||||
beforeEach(() => {
|
||||
comp.mediaList$.next([mockMediaViewerItem]);
|
||||
comp.videoOptions = true;
|
||||
comp.mediaOptions = {
|
||||
image: true,
|
||||
video: true,
|
||||
};
|
||||
comp.isLoading = true;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
@@ -118,7 +121,10 @@ describe('MediaViewerComponent', () => {
|
||||
describe('when the bitstreams loading is failed', () => {
|
||||
beforeEach(() => {
|
||||
comp.mediaList$.next([]);
|
||||
comp.videoOptions = true;
|
||||
comp.mediaOptions = {
|
||||
image: true,
|
||||
video: true,
|
||||
};
|
||||
comp.isLoading = false;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
@@ -135,7 +141,7 @@ describe('MediaViewerComponent', () => {
|
||||
|
||||
it('should display a default, thumbnail', () => {
|
||||
const defaultThumbnail = fixture.debugElement.query(
|
||||
By.css('ds-media-viewer-image')
|
||||
By.css('ds-themed-media-viewer-image')
|
||||
);
|
||||
expect(defaultThumbnail.nativeElement).toBeDefined();
|
||||
});
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { filter, take } from 'rxjs/operators';
|
||||
import { BitstreamDataService } from '../../core/data/bitstream-data.service';
|
||||
@@ -11,61 +11,83 @@ import { MediaViewerItem } from '../../core/shared/media-viewer-item.model';
|
||||
import { getFirstSucceededRemoteDataPayload } from '../../core/shared/operators';
|
||||
import { hasValue } from '../../shared/empty.util';
|
||||
import { followLink } from '../../shared/utils/follow-link-config.model';
|
||||
import { MediaViewerConfig } from '../../../config/media-viewer-config.interface';
|
||||
import { environment } from '../../../environments/environment';
|
||||
import { Subscription } from 'rxjs/internal/Subscription';
|
||||
|
||||
/**
|
||||
* This componenet renders the media viewers
|
||||
* This component renders the media viewers
|
||||
*/
|
||||
|
||||
@Component({
|
||||
selector: 'ds-media-viewer',
|
||||
templateUrl: './media-viewer.component.html',
|
||||
styleUrls: ['./media-viewer.component.scss'],
|
||||
})
|
||||
export class MediaViewerComponent implements OnInit {
|
||||
export class MediaViewerComponent implements OnDestroy, OnInit {
|
||||
@Input() item: Item;
|
||||
@Input() videoOptions: boolean;
|
||||
|
||||
mediaList$: BehaviorSubject<MediaViewerItem[]>;
|
||||
@Input() mediaOptions: MediaViewerConfig = environment.mediaViewer;
|
||||
|
||||
isLoading: boolean;
|
||||
mediaList$: BehaviorSubject<MediaViewerItem[]> = new BehaviorSubject([]);
|
||||
|
||||
captions$: BehaviorSubject<Bitstream[]> = new BehaviorSubject([]);
|
||||
|
||||
isLoading = true;
|
||||
|
||||
thumbnailPlaceholder = './assets/images/replacement_document.svg';
|
||||
|
||||
constructor(protected bitstreamDataService: BitstreamDataService) {}
|
||||
thumbnailsRD$: Observable<RemoteData<PaginatedList<Bitstream>>>;
|
||||
|
||||
subs: Subscription[] = [];
|
||||
|
||||
constructor(
|
||||
protected bitstreamDataService: BitstreamDataService,
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.subs.forEach((subscription: Subscription) => subscription.unsubscribe());
|
||||
}
|
||||
|
||||
/**
|
||||
* This metod loads all the Bitstreams and Thumbnails and contert it to media item
|
||||
* This method loads all the Bitstreams and Thumbnails and converts it to {@link MediaViewerItem}s
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
this.mediaList$ = new BehaviorSubject([]);
|
||||
this.isLoading = true;
|
||||
this.loadRemoteData('ORIGINAL').subscribe((bitstreamsRD) => {
|
||||
const types: string[] = [
|
||||
...(this.mediaOptions.image ? ['image'] : []),
|
||||
...(this.mediaOptions.video ? ['audio', 'video'] : []),
|
||||
];
|
||||
this.thumbnailsRD$ = this.loadRemoteData('THUMBNAIL');
|
||||
this.subs.push(this.loadRemoteData('ORIGINAL').subscribe((bitstreamsRD: RemoteData<PaginatedList<Bitstream>>) => {
|
||||
if (bitstreamsRD.payload.page.length === 0) {
|
||||
this.isLoading = false;
|
||||
this.mediaList$.next([]);
|
||||
} else {
|
||||
this.loadRemoteData('THUMBNAIL').subscribe((thumbnailsRD) => {
|
||||
this.subs.push(this.thumbnailsRD$.subscribe((thumbnailsRD: RemoteData<PaginatedList<Bitstream>>) => {
|
||||
for (
|
||||
let index = 0;
|
||||
index < bitstreamsRD.payload.page.length;
|
||||
index++
|
||||
) {
|
||||
bitstreamsRD.payload.page[index].format
|
||||
this.subs.push(bitstreamsRD.payload.page[index].format
|
||||
.pipe(getFirstSucceededRemoteDataPayload())
|
||||
.subscribe((format) => {
|
||||
const current = this.mediaList$.getValue();
|
||||
.subscribe((format: BitstreamFormat) => {
|
||||
const mediaItem = this.createMediaViewerItem(
|
||||
bitstreamsRD.payload.page[index],
|
||||
format,
|
||||
thumbnailsRD.payload && thumbnailsRD.payload.page[index]
|
||||
);
|
||||
this.mediaList$.next([...current, mediaItem]);
|
||||
});
|
||||
if (types.includes(mediaItem.format)) {
|
||||
this.mediaList$.next([...this.mediaList$.getValue(), mediaItem]);
|
||||
} else if (format.mimetype === 'text/vtt') {
|
||||
this.captions$.next([...this.captions$.getValue(), bitstreamsRD.payload.page[index]]);
|
||||
}
|
||||
}));
|
||||
}
|
||||
this.isLoading = false;
|
||||
});
|
||||
}));
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,16 +117,12 @@ export class MediaViewerComponent implements OnInit {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method create MediaViewerItem from incoming bitstreams
|
||||
* @param original original remote data bitstream
|
||||
* This method creates a {@link MediaViewerItem} from incoming {@link Bitstream}s
|
||||
* @param original original bitstream
|
||||
* @param format original bitstream format
|
||||
* @param thumbnail trunbnail remote data bitstream
|
||||
* @param thumbnail thumbnail bitstream
|
||||
*/
|
||||
createMediaViewerItem(
|
||||
original: Bitstream,
|
||||
format: BitstreamFormat,
|
||||
thumbnail: Bitstream
|
||||
): MediaViewerItem {
|
||||
createMediaViewerItem(original: Bitstream, format: BitstreamFormat, thumbnail: Bitstream): MediaViewerItem {
|
||||
const mediaItem = new MediaViewerItem();
|
||||
mediaItem.bitstream = original;
|
||||
mediaItem.format = format.mimetype.split('/')[0];
|
||||
|
@@ -0,0 +1,37 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { ThemedComponent } from '../../shared/theme-support/themed.component';
|
||||
import { MediaViewerComponent } from './media-viewer.component';
|
||||
import { Item } from '../../core/shared/item.model';
|
||||
import { MediaViewerConfig } from '../../../config/media-viewer-config.interface';
|
||||
|
||||
/**
|
||||
* Themed wrapper for {@link MediaViewerComponent}.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-themed-media-viewer',
|
||||
styleUrls: [],
|
||||
templateUrl: '../../shared/theme-support/themed.component.html',
|
||||
})
|
||||
export class ThemedMediaViewerComponent extends ThemedComponent<MediaViewerComponent> {
|
||||
|
||||
@Input() item: Item;
|
||||
@Input() mediaOptions: MediaViewerConfig;
|
||||
|
||||
protected inAndOutputNames: (keyof MediaViewerComponent & keyof this)[] = [
|
||||
'item',
|
||||
'mediaOptions',
|
||||
];
|
||||
|
||||
protected getComponentName(): string {
|
||||
return 'MediaViewerComponent';
|
||||
}
|
||||
|
||||
protected importThemedComponent(themeName: string): Promise<any> {
|
||||
return import(`../../../themes/${themeName}/app/item-page/media-viewer/media-viewer.component`);
|
||||
}
|
||||
|
||||
protected importUnthemedComponent(): Promise<any> {
|
||||
return import('./media-viewer.component');
|
||||
}
|
||||
|
||||
}
|
@@ -156,7 +156,8 @@ export class OrcidSyncSettingsComponent implements OnInit {
|
||||
}
|
||||
}),
|
||||
).subscribe((remoteData: RemoteData<ResearcherProfile>) => {
|
||||
if (remoteData.isSuccess) {
|
||||
// hasSucceeded is true if the response is success or successStale
|
||||
if (remoteData.hasSucceeded) {
|
||||
this.notificationsService.success(this.translateService.get(this.messagePrefix + '.synchronization-settings-update.success'));
|
||||
this.settingsUpdated.emit();
|
||||
} else {
|
||||
|
@@ -0,0 +1,33 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { ThemedComponent } from '../../../../../shared/theme-support/themed.component';
|
||||
import { ItemPageTitleFieldComponent } from './item-page-title-field.component';
|
||||
import { Item } from '../../../../../core/shared/item.model';
|
||||
|
||||
/**
|
||||
* Themed wrapper for {@link ItemPageTitleFieldComponent}
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-themed-item-page-title-field',
|
||||
styleUrls: [],
|
||||
templateUrl: '../../../../../shared/theme-support/themed.component.html',
|
||||
})
|
||||
export class ThemedItemPageTitleFieldComponent extends ThemedComponent<ItemPageTitleFieldComponent> {
|
||||
|
||||
protected inAndOutputNames: (keyof ItemPageTitleFieldComponent & keyof this)[] = [
|
||||
'item',
|
||||
];
|
||||
|
||||
@Input() item: Item;
|
||||
|
||||
protected getComponentName(): string {
|
||||
return 'ItemPageTitleFieldComponent';
|
||||
}
|
||||
|
||||
protected importThemedComponent(themeName: string): Promise<any> {
|
||||
return import(`../../../../../../themes/${themeName}/app/item-page/simple/field-components/specific-field/title/item-page-title-field.component`);
|
||||
}
|
||||
|
||||
protected importUnthemedComponent(): Promise<any> {
|
||||
return import('./item-page-title-field.component');
|
||||
}
|
||||
}
|
@@ -9,20 +9,20 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex flex-row">
|
||||
<ds-item-page-title-field [item]="object" class="mr-auto">
|
||||
</ds-item-page-title-field>
|
||||
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||
<ds-themed-item-page-title-field [item]="object" class="mr-auto">
|
||||
</ds-themed-item-page-title-field>
|
||||
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<ng-container *ngIf="!mediaViewer.image">
|
||||
<ng-container *ngIf="!(mediaViewer.image || mediaViewer.video)">
|
||||
<ds-metadata-field-wrapper [hideIfNoTextContent]="false">
|
||||
<ds-thumbnail [thumbnail]="object?.thumbnail | async"></ds-thumbnail>
|
||||
</ds-metadata-field-wrapper>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="mediaViewer.image">
|
||||
<ds-media-viewer [item]="object" [videoOptions]="mediaViewer.video"></ds-media-viewer>
|
||||
</ng-container>
|
||||
<div *ngIf="mediaViewer.image || mediaViewer.video" class="mb-2">
|
||||
<ds-themed-media-viewer [item]="object"></ds-themed-media-viewer>
|
||||
</div>
|
||||
<ds-themed-item-page-file-section [item]="object"></ds-themed-item-page-file-section>
|
||||
<ds-item-page-date-field [item]="object"></ds-item-page-date-field>
|
||||
<ds-themed-metadata-representation-list class="ds-item-page-mixed-author-field"
|
||||
|
@@ -10,20 +10,20 @@
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-row">
|
||||
<ds-item-page-title-field [item]="object" class="mr-auto">
|
||||
</ds-item-page-title-field>
|
||||
<ds-themed-item-page-title-field [item]="object" class="mr-auto">
|
||||
</ds-themed-item-page-title-field>
|
||||
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<ng-container *ngIf="!mediaViewer.image">
|
||||
<ng-container *ngIf="!(mediaViewer.image || mediaViewer.video)">
|
||||
<ds-metadata-field-wrapper [hideIfNoTextContent]="false">
|
||||
<ds-thumbnail [thumbnail]="object?.thumbnail | async"></ds-thumbnail>
|
||||
</ds-metadata-field-wrapper>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="mediaViewer.image">
|
||||
<ds-media-viewer [item]="object" [videoOptions]="mediaViewer.video"></ds-media-viewer>
|
||||
</ng-container>
|
||||
<div *ngIf="mediaViewer.image || mediaViewer.video" class="mb-2">
|
||||
<ds-themed-media-viewer [item]="object"></ds-themed-media-viewer>
|
||||
</div>
|
||||
<ds-themed-item-page-file-section [item]="object"></ds-themed-item-page-file-section>
|
||||
<ds-item-page-date-field [item]="object"></ds-item-page-date-field>
|
||||
<ds-themed-metadata-representation-list class="ds-item-page-mixed-author-field"
|
||||
|
@@ -19,7 +19,7 @@ export class ThemedMetadataRepresentationListComponent extends ThemedComponent<M
|
||||
|
||||
@Input() label: string;
|
||||
|
||||
@Input() incrementBy = 10;
|
||||
@Input() incrementBy: number;
|
||||
|
||||
protected getComponentName(): string {
|
||||
return 'MetadataRepresentationListComponent';
|
||||
|
@@ -11,7 +11,6 @@ import { MyDSpacePageComponent } from './my-dspace-page.component';
|
||||
templateUrl: './../shared/theme-support/themed.component.html'
|
||||
})
|
||||
export class ThemedMyDSpacePageComponent extends ThemedComponent<MyDSpacePageComponent> {
|
||||
protected inAndOutputNames: (keyof MyDSpacePageComponent & keyof this)[];
|
||||
|
||||
protected getComponentName(): string {
|
||||
return 'MyDSpacePageComponent';
|
||||
|
@@ -28,19 +28,18 @@ export class ThemedConfigurationSearchPageComponent extends ThemedComponent<Conf
|
||||
/**
|
||||
* True when the search component should show results on the current page
|
||||
*/
|
||||
@Input() inPlaceSearch = true;
|
||||
@Input() inPlaceSearch: boolean;
|
||||
|
||||
/**
|
||||
* Whether or not the search bar should be visible
|
||||
*/
|
||||
@Input()
|
||||
searchEnabled = true;
|
||||
@Input() searchEnabled: boolean;
|
||||
|
||||
/**
|
||||
* The width of the sidebar (bootstrap columns)
|
||||
*/
|
||||
@Input()
|
||||
sideBarWidth = 3;
|
||||
sideBarWidth: number;
|
||||
|
||||
/**
|
||||
* The currently applied configuration (determines title of search)
|
||||
@@ -66,7 +65,7 @@ export class ThemedConfigurationSearchPageComponent extends ThemedComponent<Conf
|
||||
}
|
||||
|
||||
protected importUnthemedComponent(): Promise<any> {
|
||||
return import(`./configuration-search-page.component`);
|
||||
return import('./configuration-search-page.component');
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -11,11 +11,11 @@ export class ThemedCollectionDropdownComponent extends ThemedComponent<Collectio
|
||||
|
||||
@Input() entityType: string;
|
||||
|
||||
@Output() searchComplete = new EventEmitter<any>();
|
||||
@Output() searchComplete: EventEmitter<any> = new EventEmitter();
|
||||
|
||||
@Output() theOnlySelectable = new EventEmitter<CollectionListEntry>();
|
||||
@Output() theOnlySelectable: EventEmitter<CollectionListEntry> = new EventEmitter();
|
||||
|
||||
@Output() selectionChange = new EventEmitter<CollectionListEntry>();
|
||||
@Output() selectionChange = new EventEmitter();
|
||||
|
||||
protected inAndOutputNames: (keyof CollectionDropdownComponent & keyof this)[] = ['entityType', 'searchComplete', 'theOnlySelectable', 'selectionChange'];
|
||||
|
||||
|
@@ -9,7 +9,7 @@ import {
|
||||
import {
|
||||
DsoEditMenuExpandableSectionComponent
|
||||
} from '../dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component';
|
||||
import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { NgbDropdownModule, NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
const COMPONENTS = [
|
||||
DsoEditMenuComponent,
|
||||
@@ -25,6 +25,7 @@ const MODULES = [
|
||||
RouterModule,
|
||||
CommonModule,
|
||||
NgbTooltipModule,
|
||||
NgbDropdownModule,
|
||||
];
|
||||
const PROVIDERS = [
|
||||
|
||||
|
@@ -16,7 +16,7 @@
|
||||
<ng-container #componentViewContainer></ng-container>
|
||||
</div>
|
||||
|
||||
<small *ngIf="hasHint && ((!model.repeatable && (isRelationship === false || value?.value === null)) || (model.repeatable === true && context?.index === context?.context?.groups?.length - 1)) && (!showErrorMessages || errorMessages.length === 0)"
|
||||
<small *ngIf="hasHint && (formBuilderService.hasArrayGroupValue(model) || (!model.repeatable && (isRelationship === false || value?.value === null)) || (model.repeatable === true && context?.index === context?.context?.groups?.length - 1)) && (!showErrorMessages || errorMessages.length === 0)"
|
||||
class="text-muted ds-hint" [innerHTML]="model.hint | translate" [ngClass]="getClass('element', 'hint')"></small>
|
||||
<!-- In case of repeatable fields show empty space for all elements except the first -->
|
||||
<div *ngIf="context?.index !== null
|
||||
|
@@ -147,12 +147,14 @@ describe('DsDynamicFormControlContainerComponent test suite', () => {
|
||||
new DynamicListCheckboxGroupModel({
|
||||
id: 'checkboxList',
|
||||
vocabularyOptions: vocabularyOptions,
|
||||
repeatable: true
|
||||
repeatable: true,
|
||||
required: false,
|
||||
}),
|
||||
new DynamicListRadioGroupModel({
|
||||
id: 'radioList',
|
||||
vocabularyOptions: vocabularyOptions,
|
||||
repeatable: false
|
||||
repeatable: false,
|
||||
required: false,
|
||||
}),
|
||||
new DynamicRelationGroupModel({
|
||||
submissionId: '1234',
|
||||
|
@@ -259,7 +259,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
|
||||
private submissionObjectService: SubmissionObjectDataService,
|
||||
private ref: ChangeDetectorRef,
|
||||
private formService: FormService,
|
||||
private formBuilderService: FormBuilderService,
|
||||
public formBuilderService: FormBuilderService,
|
||||
private submissionService: SubmissionService,
|
||||
@Inject(APP_CONFIG) protected appConfig: AppConfig,
|
||||
) {
|
||||
|
@@ -15,8 +15,10 @@ export interface DynamicListCheckboxGroupModelConfig extends DynamicFormGroupMod
|
||||
vocabularyOptions: VocabularyOptions;
|
||||
groupLength?: number;
|
||||
repeatable: boolean;
|
||||
value?: any;
|
||||
value?: VocabularyEntry[];
|
||||
typeBindRelations?: DynamicFormControlRelation[];
|
||||
required: boolean;
|
||||
hint?: string;
|
||||
}
|
||||
|
||||
export class DynamicListCheckboxGroupModel extends DynamicCheckboxGroupModel {
|
||||
@@ -26,6 +28,8 @@ export class DynamicListCheckboxGroupModel extends DynamicCheckboxGroupModel {
|
||||
@serializable() groupLength: number;
|
||||
@serializable() _value: VocabularyEntry[];
|
||||
@serializable() typeBindRelations: DynamicFormControlRelation[];
|
||||
@serializable() required: boolean;
|
||||
@serializable() hint: string;
|
||||
isListGroup = true;
|
||||
valueUpdates: Subject<any>;
|
||||
|
||||
@@ -36,6 +40,8 @@ export class DynamicListCheckboxGroupModel extends DynamicCheckboxGroupModel {
|
||||
this.groupLength = config.groupLength || 5;
|
||||
this._value = [];
|
||||
this.repeatable = config.repeatable;
|
||||
this.required = config.required;
|
||||
this.hint = config.hint;
|
||||
|
||||
this.valueUpdates = new Subject<any>();
|
||||
this.valueUpdates.subscribe((value: VocabularyEntry | VocabularyEntry[]) => this.value = value);
|
||||
@@ -56,9 +62,8 @@ export class DynamicListCheckboxGroupModel extends DynamicCheckboxGroupModel {
|
||||
if (Array.isArray(value)) {
|
||||
this._value = value;
|
||||
} else {
|
||||
// _value is non extendible so assign it a new array
|
||||
const newValue = (this.value as VocabularyEntry[]).concat([value]);
|
||||
this._value = newValue;
|
||||
// _value is non-extendable so assign it a new array
|
||||
this._value = (this.value as VocabularyEntry[]).concat([value]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -6,12 +6,15 @@ import {
|
||||
} from '@ng-dynamic-forms/core';
|
||||
import { VocabularyOptions } from '../../../../../../core/submission/vocabularies/models/vocabulary-options.model';
|
||||
import { hasValue } from '../../../../../empty.util';
|
||||
import { VocabularyEntry } from '../../../../../../core/submission/vocabularies/models/vocabulary-entry.model';
|
||||
|
||||
export interface DynamicListModelConfig extends DynamicRadioGroupModelConfig<any> {
|
||||
vocabularyOptions: VocabularyOptions;
|
||||
groupLength?: number;
|
||||
repeatable: boolean;
|
||||
value?: any;
|
||||
value?: VocabularyEntry[];
|
||||
required: boolean;
|
||||
hint?: string;
|
||||
}
|
||||
|
||||
export class DynamicListRadioGroupModel extends DynamicRadioGroupModel<any> {
|
||||
@@ -19,6 +22,8 @@ export class DynamicListRadioGroupModel extends DynamicRadioGroupModel<any> {
|
||||
@serializable() vocabularyOptions: VocabularyOptions;
|
||||
@serializable() repeatable: boolean;
|
||||
@serializable() groupLength: number;
|
||||
@serializable() required: boolean;
|
||||
@serializable() hint: string;
|
||||
isListGroup = true;
|
||||
|
||||
constructor(config: DynamicListModelConfig, layout?: DynamicFormControlLayout) {
|
||||
@@ -27,6 +32,8 @@ export class DynamicListRadioGroupModel extends DynamicRadioGroupModel<any> {
|
||||
this.vocabularyOptions = config.vocabularyOptions;
|
||||
this.groupLength = config.groupLength || 5;
|
||||
this.repeatable = config.repeatable;
|
||||
this.required = config.required;
|
||||
this.hint = config.hint;
|
||||
this.value = config.value;
|
||||
}
|
||||
|
||||
|
@@ -17,7 +17,6 @@
|
||||
[id]="item.id"
|
||||
[formControlName]="item.id"
|
||||
[name]="model.name"
|
||||
[required]="model.required"
|
||||
[value]="item.value"
|
||||
(blur)="onBlur($event)"
|
||||
(change)="onChange($event)"
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { FormGroup } from '@angular/forms';
|
||||
|
||||
import { FormGroup, ValidatorFn, ValidationErrors, AbstractControl } from '@angular/forms';
|
||||
import {
|
||||
DynamicCheckboxModel,
|
||||
DynamicFormControlComponent,
|
||||
@@ -110,6 +109,9 @@ export class DsDynamicListComponent extends DynamicFormControlComponent implemen
|
||||
protected setOptionsFromVocabulary() {
|
||||
if (this.model.vocabularyOptions.name && this.model.vocabularyOptions.name.length > 0) {
|
||||
const listGroup = this.group.controls[this.model.id] as FormGroup;
|
||||
if (this.model.repeatable && this.model.required) {
|
||||
listGroup.addValidators(this.hasAtLeastOneVocabularyEntry());
|
||||
}
|
||||
const pageInfo: PageInfo = new PageInfo({
|
||||
elementsPerPage: 9999, currentPage: 1
|
||||
} as PageInfo);
|
||||
@@ -121,7 +123,7 @@ export class DsDynamicListComponent extends DynamicFormControlComponent implemen
|
||||
let tempList: ListItem[] = [];
|
||||
this.optionsList = entries.page;
|
||||
// Make a list of available options (checkbox/radio) and split in groups of 'model.groupLength'
|
||||
entries.page.forEach((option, key) => {
|
||||
entries.page.forEach((option: VocabularyEntry, key: number) => {
|
||||
const value = option.authority || option.value;
|
||||
const checked: boolean = isNotEmpty(findKey(
|
||||
this.model.value,
|
||||
@@ -156,4 +158,13 @@ export class DsDynamicListComponent extends DynamicFormControlComponent implemen
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if at least one {@link VocabularyEntry} has been selected.
|
||||
*/
|
||||
hasAtLeastOneVocabularyEntry(): ValidatorFn {
|
||||
return (control: AbstractControl): ValidationErrors | null => {
|
||||
return control && control.value && Object.values(control.value).find((checked: boolean) => checked === true) ? null : this.model.errorMessages;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -4,27 +4,14 @@ import { RelationshipOptions } from '../../../models/relationship-options.model'
|
||||
import { ListableObject } from '../../../../../object-collection/shared/listable-object.model';
|
||||
import { Context } from '../../../../../../core/shared/context.model';
|
||||
import { Item } from '../../../../../../core/shared/item.model';
|
||||
import { SEARCH_CONFIG_SERVICE } from '../../../../../../my-dspace-page/my-dspace-page.component';
|
||||
import { SearchConfigurationService } from '../../../../../../core/shared/search/search-configuration.service';
|
||||
import { Collection } from '../../../../../../core/shared/collection.model';
|
||||
import { ExternalSource } from '../../../../../../core/shared/external-source.model';
|
||||
import { DsDynamicLookupRelationExternalSourceTabComponent } from './dynamic-lookup-relation-external-source-tab.component';
|
||||
import { fadeIn, fadeInOut } from '../../../../../animations/fade';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-themed-dynamic-lookup-relation-external-source-tab',
|
||||
styleUrls: [],
|
||||
templateUrl: '../../../../../theme-support/themed.component.html',
|
||||
providers: [
|
||||
{
|
||||
provide: SEARCH_CONFIG_SERVICE,
|
||||
useClass: SearchConfigurationService
|
||||
}
|
||||
],
|
||||
animations: [
|
||||
fadeIn,
|
||||
fadeInOut
|
||||
]
|
||||
})
|
||||
export class ThemedDynamicLookupRelationExternalSourceTabComponent extends ThemedComponent<DsDynamicLookupRelationExternalSourceTabComponent> {
|
||||
protected inAndOutputNames: (keyof DsDynamicLookupRelationExternalSourceTabComponent & keyof this)[] = ['label', 'listId',
|
||||
@@ -44,7 +31,7 @@ export class ThemedDynamicLookupRelationExternalSourceTabComponent extends Theme
|
||||
|
||||
@Input() repeatable: boolean;
|
||||
|
||||
@Output() importedObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
||||
@Output() importedObject: EventEmitter<ListableObject> = new EventEmitter();
|
||||
|
||||
@Input() externalSource: ExternalSource;
|
||||
|
||||
|
@@ -10,19 +10,11 @@ import { Item } from '../../../../../../core/shared/item.model';
|
||||
import { SearchResult } from '../../../../../search/models/search-result.model';
|
||||
import { SearchObjects } from '../../../../../search/models/search-objects.model';
|
||||
import { DSpaceObject } from '../../../../../../core/shared/dspace-object.model';
|
||||
import { SEARCH_CONFIG_SERVICE } from '../../../../../../my-dspace-page/my-dspace-page.component';
|
||||
import { SearchConfigurationService } from '../../../../../../core/shared/search/search-configuration.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-themed-dynamic-lookup-relation-search-tab',
|
||||
styleUrls: [],
|
||||
templateUrl: '../../../../../theme-support/themed.component.html',
|
||||
providers: [
|
||||
{
|
||||
provide: SEARCH_CONFIG_SERVICE,
|
||||
useClass: SearchConfigurationService
|
||||
}
|
||||
]
|
||||
})
|
||||
export class ThemedDynamicLookupRelationSearchTabComponent extends ThemedComponent<DsDynamicLookupRelationSearchTabComponent> {
|
||||
protected inAndOutputNames: (keyof DsDynamicLookupRelationSearchTabComponent & keyof this)[] = ['relationship', 'listId',
|
||||
@@ -51,11 +43,11 @@ export class ThemedDynamicLookupRelationSearchTabComponent extends ThemedCompone
|
||||
|
||||
@Input() isEditRelationship: boolean;
|
||||
|
||||
@Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
||||
@Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter();
|
||||
|
||||
@Output() selectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
||||
@Output() selectObject: EventEmitter<ListableObject> = new EventEmitter();
|
||||
|
||||
@Output() resultFound: EventEmitter<SearchObjects<DSpaceObject>> = new EventEmitter<SearchObjects<DSpaceObject>>();
|
||||
@Output() resultFound: EventEmitter<SearchObjects<DSpaceObject>> = new EventEmitter();
|
||||
|
||||
protected getComponentName(): string {
|
||||
return 'DsDynamicLookupRelationSearchTabComponent';
|
||||
|
@@ -235,10 +235,16 @@ describe('FormBuilderService test suite', () => {
|
||||
new DynamicListCheckboxGroupModel({
|
||||
id: 'testCheckboxList',
|
||||
vocabularyOptions: vocabularyOptions,
|
||||
repeatable: true
|
||||
repeatable: true,
|
||||
required: false,
|
||||
}),
|
||||
|
||||
new DynamicListRadioGroupModel({ id: 'testRadioList', vocabularyOptions: vocabularyOptions, repeatable: false }),
|
||||
new DynamicListRadioGroupModel({
|
||||
id: 'testRadioList',
|
||||
vocabularyOptions: vocabularyOptions,
|
||||
repeatable: false,
|
||||
required: false,
|
||||
}),
|
||||
|
||||
new DynamicRelationGroupModel({
|
||||
submissionId,
|
||||
|
@@ -14,8 +14,8 @@ import { ThemeService } from '../theme-support/theme.service';
|
||||
export class ThemedLoadingComponent extends ThemedComponent<LoadingComponent> {
|
||||
|
||||
@Input() message: string;
|
||||
@Input() showMessage = true;
|
||||
@Input() spinner = false;
|
||||
@Input() showMessage: boolean;
|
||||
@Input() spinner: boolean;
|
||||
|
||||
protected inAndOutputNames: (keyof LoadingComponent & keyof this)[] = ['message', 'showMessage', 'spinner'];
|
||||
|
||||
|
@@ -3,8 +3,8 @@
|
||||
<ds-mydspace-item-status [status]="status"></ds-mydspace-item-status>
|
||||
</ng-container>
|
||||
<div *ngIf="item">
|
||||
<ds-item-page-title-field [item]="item">
|
||||
</ds-item-page-title-field>
|
||||
<ds-themed-item-page-title-field [item]="item">
|
||||
</ds-themed-item-page-title-field>
|
||||
<div class="row mb-1">
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<ds-metadata-field-wrapper [hideIfNoTextContent]="false">
|
||||
|
@@ -1,11 +1,10 @@
|
||||
import { ChangeDetectorRef, Component, ComponentFactoryResolver, Input } from '@angular/core';
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { ThemedComponent } from '../../../theme-support/themed.component';
|
||||
import { ItemListPreviewComponent } from './item-list-preview.component';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
|
||||
import { SearchResult } from '../../../search/models/search-result.model';
|
||||
import { WorkflowItem } from 'src/app/core/submission/models/workflowitem.model';
|
||||
import { ThemeService } from 'src/app/shared/theme-support/theme.service';
|
||||
|
||||
/**
|
||||
* Themed wrapper for ItemListPreviewComponent
|
||||
@@ -24,22 +23,10 @@ export class ThemedItemListPreviewComponent extends ThemedComponent<ItemListPrev
|
||||
|
||||
@Input() status: MyDspaceItemStatusType;
|
||||
|
||||
@Input() showSubmitter = false;
|
||||
@Input() showSubmitter: boolean;
|
||||
|
||||
@Input() workflowItem: WorkflowItem;
|
||||
|
||||
constructor(
|
||||
protected resolver: ComponentFactoryResolver,
|
||||
protected cdr: ChangeDetectorRef,
|
||||
protected themeService: ThemeService,
|
||||
) {
|
||||
super(resolver, cdr, themeService);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
super.ngOnInit();
|
||||
}
|
||||
|
||||
protected getComponentName(): string {
|
||||
return 'ItemListPreviewComponent';
|
||||
}
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import {Component, EventEmitter, Input, Output} from '@angular/core';
|
||||
import { ObjectListComponent } from './object-list.component';
|
||||
import { ThemedComponent } from '../theme-support/themed.component';
|
||||
import {ViewMode} from '../../core/shared/view-mode.model';
|
||||
import {PaginationComponentOptions} from '../pagination/pagination-component-options.model';
|
||||
import {SortDirection, SortOptions} from '../../core/cache/models/sort-options.model';
|
||||
import {CollectionElementLinkType} from '../object-collection/collection-element-link.type';
|
||||
@@ -19,10 +18,6 @@ import {ListableObject} from '../object-collection/shared/listable-object.model'
|
||||
templateUrl: '../theme-support/themed.component.html',
|
||||
})
|
||||
export class ThemedObjectListComponent extends ThemedComponent<ObjectListComponent> {
|
||||
/**
|
||||
* The view mode of the this component
|
||||
*/
|
||||
viewMode = ViewMode.ListElement;
|
||||
|
||||
/**
|
||||
* The current pagination configuration
|
||||
@@ -37,18 +32,20 @@ export class ThemedObjectListComponent extends ThemedComponent<ObjectListCompone
|
||||
/**
|
||||
* Whether or not the list elements have a border
|
||||
*/
|
||||
@Input() hasBorder = false;
|
||||
@Input() hasBorder: boolean;
|
||||
|
||||
/**
|
||||
* The whether or not the gear is hidden
|
||||
*/
|
||||
@Input() hideGear = false;
|
||||
@Input() hideGear: boolean;
|
||||
|
||||
/**
|
||||
* Whether or not the pager is visible when there is only a single page of results
|
||||
*/
|
||||
@Input() hidePagerWhenSinglePage = true;
|
||||
@Input() selectable = false;
|
||||
@Input() hidePagerWhenSinglePage: boolean;
|
||||
|
||||
@Input() selectable: boolean;
|
||||
|
||||
@Input() selectionConfig: { repeatable: boolean, listId: string };
|
||||
|
||||
/**
|
||||
@@ -64,12 +61,12 @@ export class ThemedObjectListComponent extends ThemedComponent<ObjectListCompone
|
||||
/**
|
||||
* Option for hiding the pagination detail
|
||||
*/
|
||||
@Input() hidePaginationDetail = false;
|
||||
@Input() hidePaginationDetail: boolean;
|
||||
|
||||
/**
|
||||
* Whether or not to add an import button to the object
|
||||
*/
|
||||
@Input() importable = false;
|
||||
@Input() importable: boolean;
|
||||
|
||||
/**
|
||||
* Config used for the import button
|
||||
@@ -79,42 +76,24 @@ export class ThemedObjectListComponent extends ThemedComponent<ObjectListCompone
|
||||
/**
|
||||
* Whether or not the pagination should be rendered as simple previous and next buttons instead of the normal pagination
|
||||
*/
|
||||
@Input() showPaginator = true;
|
||||
@Input() showPaginator: boolean;
|
||||
|
||||
/**
|
||||
* Emit when one of the listed object has changed.
|
||||
*/
|
||||
@Output() contentChange = new EventEmitter<any>();
|
||||
@Output() contentChange: EventEmitter<any> = new EventEmitter();
|
||||
|
||||
/**
|
||||
* If showPaginator is set to true, emit when the previous button is clicked
|
||||
*/
|
||||
@Output() prev = new EventEmitter<boolean>();
|
||||
@Output() prev: EventEmitter<boolean> = new EventEmitter();
|
||||
|
||||
/**
|
||||
* If showPaginator is set to true, emit when the next button is clicked
|
||||
*/
|
||||
@Output() next = new EventEmitter<boolean>();
|
||||
@Output() next: EventEmitter<boolean> = new EventEmitter();
|
||||
|
||||
/**
|
||||
* The current listable objects
|
||||
*/
|
||||
private _objects: RemoteData<PaginatedList<ListableObject>>;
|
||||
|
||||
/**
|
||||
* Setter for the objects
|
||||
* @param objects The new objects
|
||||
*/
|
||||
@Input() set objects(objects: RemoteData<PaginatedList<ListableObject>>) {
|
||||
this._objects = objects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter to return the current objects
|
||||
*/
|
||||
get objects() {
|
||||
return this._objects;
|
||||
}
|
||||
@Input() objects: RemoteData<PaginatedList<ListableObject>>;
|
||||
|
||||
/**
|
||||
* An event fired when the page is changed.
|
||||
@@ -123,48 +102,45 @@ export class ThemedObjectListComponent extends ThemedComponent<ObjectListCompone
|
||||
@Output() change: EventEmitter<{
|
||||
pagination: PaginationComponentOptions,
|
||||
sort: SortOptions
|
||||
}> = new EventEmitter<{
|
||||
pagination: PaginationComponentOptions,
|
||||
sort: SortOptions
|
||||
}>();
|
||||
}> = new EventEmitter();
|
||||
|
||||
/**
|
||||
* An event fired when the page is changed.
|
||||
* Event's payload equals to the newly selected page.
|
||||
*/
|
||||
@Output() pageChange: EventEmitter<number> = new EventEmitter<number>();
|
||||
@Output() pageChange: EventEmitter<number> = new EventEmitter();
|
||||
|
||||
/**
|
||||
* An event fired when the page wsize is changed.
|
||||
* Event's payload equals to the newly selected page size.
|
||||
*/
|
||||
@Output() pageSizeChange: EventEmitter<number> = new EventEmitter<number>();
|
||||
@Output() pageSizeChange: EventEmitter<number> = new EventEmitter();
|
||||
|
||||
/**
|
||||
* An event fired when the sort direction is changed.
|
||||
* Event's payload equals to the newly selected sort direction.
|
||||
*/
|
||||
@Output() sortDirectionChange: EventEmitter<SortDirection> = new EventEmitter<SortDirection>();
|
||||
@Output() sortDirectionChange: EventEmitter<SortDirection> = new EventEmitter();
|
||||
|
||||
/**
|
||||
* An event fired when on of the pagination parameters changes
|
||||
*/
|
||||
@Output() paginationChange: EventEmitter<any> = new EventEmitter<any>();
|
||||
@Output() paginationChange: EventEmitter<any> = new EventEmitter();
|
||||
|
||||
@Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
||||
@Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter();
|
||||
|
||||
@Output() selectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
||||
@Output() selectObject: EventEmitter<ListableObject> = new EventEmitter();
|
||||
|
||||
/**
|
||||
* Send an import event to the parent component
|
||||
*/
|
||||
@Output() importObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
||||
@Output() importObject: EventEmitter<ListableObject> = new EventEmitter();
|
||||
|
||||
/**
|
||||
* An event fired when the sort field is changed.
|
||||
* Event's payload equals to the newly selected sort field.
|
||||
*/
|
||||
@Output() sortFieldChange: EventEmitter<string> = new EventEmitter<string>();
|
||||
@Output() sortFieldChange: EventEmitter<string> = new EventEmitter();
|
||||
|
||||
inAndOutputNames: (keyof ObjectListComponent & keyof this)[] = [
|
||||
'config',
|
||||
|
@@ -32,7 +32,7 @@ import { RemoteData } from '../../core/data/remote-data';
|
||||
})
|
||||
export class RSSComponent implements OnInit, OnDestroy {
|
||||
|
||||
route$: BehaviorSubject<string>;
|
||||
route$: BehaviorSubject<string> = new BehaviorSubject<string>('');
|
||||
|
||||
isEnabled$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(null);
|
||||
|
||||
@@ -99,7 +99,7 @@ export class RSSComponent implements OnInit, OnDestroy {
|
||||
rel: 'search',
|
||||
title: 'Dspace'
|
||||
});
|
||||
this.route$ = new BehaviorSubject<string>(route);
|
||||
this.route$.next(route);
|
||||
}));
|
||||
}
|
||||
|
||||
|
@@ -29,7 +29,7 @@ export class ThemedSearchResultsComponent extends ThemedComponent<SearchResultsC
|
||||
|
||||
@Input() searchConfig: PaginatedSearchOptions;
|
||||
|
||||
@Input() showCsvExport = false;
|
||||
@Input() showCsvExport: boolean;
|
||||
|
||||
@Input() sortConfig: SortOptions;
|
||||
|
||||
@@ -37,21 +37,21 @@ export class ThemedSearchResultsComponent extends ThemedComponent<SearchResultsC
|
||||
|
||||
@Input() configuration: string;
|
||||
|
||||
@Input() disableHeader = false;
|
||||
@Input() disableHeader: boolean;
|
||||
|
||||
@Input() selectable = false;
|
||||
@Input() selectable: boolean;
|
||||
|
||||
@Input() context: Context;
|
||||
|
||||
@Input() hidePaginationDetail = false;
|
||||
@Input() hidePaginationDetail: boolean;
|
||||
|
||||
@Input() selectionConfig: SelectionConfig = null;
|
||||
@Input() selectionConfig: SelectionConfig;
|
||||
|
||||
@Output() contentChange: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
||||
@Output() contentChange: EventEmitter<ListableObject> = new EventEmitter();
|
||||
|
||||
@Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
||||
@Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter();
|
||||
|
||||
@Output() selectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
||||
@Output() selectObject: EventEmitter<ListableObject> = new EventEmitter();
|
||||
|
||||
protected getComponentName(): string {
|
||||
return 'SearchResultsComponent';
|
||||
|
@@ -136,7 +136,7 @@ export class SearchComponent implements OnInit {
|
||||
/**
|
||||
* List of available view mode
|
||||
*/
|
||||
@Input() useUniquePageId: false;
|
||||
@Input() useUniquePageId: boolean;
|
||||
|
||||
/**
|
||||
* List of available view mode
|
||||
|
@@ -11,7 +11,7 @@ import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
||||
import { ListableObject } from '../object-collection/shared/listable-object.model';
|
||||
|
||||
/**
|
||||
* Themed wrapper for SearchComponent
|
||||
* Themed wrapper for {@link SearchComponent}
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-themed-search',
|
||||
@@ -21,53 +21,53 @@ import { ListableObject } from '../object-collection/shared/listable-object.mode
|
||||
export class ThemedSearchComponent extends ThemedComponent<SearchComponent> {
|
||||
protected inAndOutputNames: (keyof SearchComponent & keyof this)[] = ['configurationList', 'context', 'configuration', 'fixedFilterQuery', 'useCachedVersionIfAvailable', 'inPlaceSearch', 'linkType', 'paginationId', 'searchEnabled', 'sideBarWidth', 'searchFormPlaceholder', 'selectable', 'selectionConfig', 'showCsvExport', 'showSidebar', 'showViewModes', 'useUniquePageId', 'viewModeList', 'showScopeSelector', 'resultFound', 'deselectObject', 'selectObject', 'trackStatistics', 'query'];
|
||||
|
||||
@Input() configurationList: SearchConfigurationOption[] = [];
|
||||
@Input() configurationList: SearchConfigurationOption[];
|
||||
|
||||
@Input() context: Context = Context.Search;
|
||||
@Input() context: Context;
|
||||
|
||||
@Input() configuration = 'default';
|
||||
@Input() configuration: string;
|
||||
|
||||
@Input() fixedFilterQuery: string;
|
||||
|
||||
@Input() useCachedVersionIfAvailable = true;
|
||||
@Input() useCachedVersionIfAvailable: boolean;
|
||||
|
||||
@Input() inPlaceSearch = true;
|
||||
@Input() inPlaceSearch: boolean;
|
||||
|
||||
@Input() linkType: CollectionElementLinkType;
|
||||
|
||||
@Input() paginationId = 'spc';
|
||||
@Input() paginationId: string;
|
||||
|
||||
@Input() searchEnabled = true;
|
||||
@Input() searchEnabled: boolean;
|
||||
|
||||
@Input() sideBarWidth = 3;
|
||||
@Input() sideBarWidth: number;
|
||||
|
||||
@Input() searchFormPlaceholder = 'search.search-form.placeholder';
|
||||
@Input() searchFormPlaceholder: string;
|
||||
|
||||
@Input() selectable = false;
|
||||
@Input() selectable: boolean;
|
||||
|
||||
@Input() selectionConfig: SelectionConfig;
|
||||
|
||||
@Input() showCsvExport = false;
|
||||
@Input() showCsvExport: boolean;
|
||||
|
||||
@Input() showSidebar = true;
|
||||
@Input() showSidebar: boolean;
|
||||
|
||||
@Input() showViewModes = true;
|
||||
@Input() showViewModes: boolean;
|
||||
|
||||
@Input() useUniquePageId: false;
|
||||
@Input() useUniquePageId: boolean;
|
||||
|
||||
@Input() viewModeList: ViewMode[];
|
||||
|
||||
@Input() showScopeSelector = true;
|
||||
@Input() showScopeSelector: boolean;
|
||||
|
||||
@Input() trackStatistics = false;
|
||||
@Input() trackStatistics: boolean;
|
||||
|
||||
@Input() query: string;
|
||||
|
||||
@Output() resultFound: EventEmitter<SearchObjects<DSpaceObject>> = new EventEmitter<SearchObjects<DSpaceObject>>();
|
||||
@Output() resultFound: EventEmitter<SearchObjects<DSpaceObject>> = new EventEmitter();
|
||||
|
||||
@Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
||||
@Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter();
|
||||
|
||||
@Output() selectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
||||
@Output() selectObject: EventEmitter<ListableObject> = new EventEmitter();
|
||||
|
||||
protected getComponentName(): string {
|
||||
return 'SearchComponent';
|
||||
|
@@ -268,6 +268,9 @@ import {
|
||||
import { EpersonGroupListComponent } from './eperson-group-list/eperson-group-list.component';
|
||||
import { EpersonSearchBoxComponent } from './eperson-group-list/eperson-search-box/eperson-search-box.component';
|
||||
import { GroupSearchBoxComponent } from './eperson-group-list/group-search-box/group-search-box.component';
|
||||
import {
|
||||
ThemedItemPageTitleFieldComponent
|
||||
} from '../item-page/simple/field-components/specific-field/title/themed-item-page-field.component';
|
||||
|
||||
const MODULES = [
|
||||
CommonModule,
|
||||
@@ -373,7 +376,8 @@ const COMPONENTS = [
|
||||
ContextHelpWrapperComponent,
|
||||
EpersonGroupListComponent,
|
||||
EpersonSearchBoxComponent,
|
||||
GroupSearchBoxComponent
|
||||
GroupSearchBoxComponent,
|
||||
ThemedItemPageTitleFieldComponent,
|
||||
];
|
||||
|
||||
const ENTRY_COMPONENTS = [
|
||||
|
@@ -17,6 +17,7 @@ export class AuthServiceStub {
|
||||
token: AuthTokenInfo = new AuthTokenInfo('token_test');
|
||||
impersonating: string;
|
||||
private _tokenExpired = false;
|
||||
private _isExternalAuth = false;
|
||||
private redirectUrl;
|
||||
|
||||
constructor() {
|
||||
@@ -122,6 +123,13 @@ export class AuthServiceStub {
|
||||
checkAuthenticationCookie() {
|
||||
return;
|
||||
}
|
||||
setExternalAuthStatus(externalCookie: boolean) {
|
||||
this._isExternalAuth = externalCookie;
|
||||
}
|
||||
|
||||
isExternalAuthentication(): Observable<boolean> {
|
||||
return observableOf(this._isExternalAuth);
|
||||
}
|
||||
|
||||
retrieveAuthMethodsFromAuthStatus(status: AuthStatus) {
|
||||
return observableOf(authMethodsMock);
|
||||
|
@@ -9,7 +9,7 @@ import { UploaderOptions } from './uploader-options.model';
|
||||
import { hasValue, isNotEmpty, isUndefined } from '../../empty.util';
|
||||
import { UploaderProperties } from './uploader-properties.model';
|
||||
import { HttpXsrfTokenExtractor } from '@angular/common/http';
|
||||
import { XSRF_COOKIE, XSRF_REQUEST_HEADER, XSRF_RESPONSE_HEADER } from '../../../core/xsrf/xsrf.interceptor';
|
||||
import { XSRF_COOKIE, XSRF_REQUEST_HEADER, XSRF_RESPONSE_HEADER } from '../../../core/xsrf/xsrf.constants';
|
||||
import { CookieService } from '../../../core/services/cookie.service';
|
||||
import { DragService } from '../../../core/drag.service';
|
||||
|
||||
|
@@ -5,7 +5,7 @@
|
||||
{{ 'statistics.table.title.' + report.reportType | translate }}
|
||||
</h3>
|
||||
|
||||
<table class="table table-striped">
|
||||
<table class="table table-striped" [attr.data-test]="report.reportType">
|
||||
|
||||
<tbody>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
<tr *ngFor="let point of report.points"
|
||||
class="{{point.id}}-data">
|
||||
<th scope="row">
|
||||
<th scope="row" data-test="statistics-label">
|
||||
{{ getLabel(point) | async }}
|
||||
</th>
|
||||
<td *ngFor="let header of headers"
|
||||
|
@@ -3,8 +3,10 @@ import { Point, UsageReport } from '../../core/statistics/models/usage-report.mo
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { getRemoteDataPayload, getFirstSucceededRemoteData } from '../../core/shared/operators';
|
||||
import { getRemoteDataPayload, getFinishedRemoteData } from '../../core/shared/operators';
|
||||
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { isEmpty } from '../../shared/empty.util';
|
||||
|
||||
/**
|
||||
* Component representing a statistics table for a given usage report.
|
||||
@@ -35,6 +37,7 @@ export class StatisticsTableComponent implements OnInit {
|
||||
constructor(
|
||||
protected dsoService: DSpaceObjectDataService,
|
||||
protected nameService: DSONameService,
|
||||
private translateService: TranslateService,
|
||||
) {
|
||||
|
||||
}
|
||||
@@ -54,9 +57,9 @@ export class StatisticsTableComponent implements OnInit {
|
||||
switch (this.report.reportType) {
|
||||
case 'TotalVisits':
|
||||
return this.dsoService.findById(point.id).pipe(
|
||||
getFirstSucceededRemoteData(),
|
||||
getFinishedRemoteData(),
|
||||
getRemoteDataPayload(),
|
||||
map((item) => this.nameService.getName(item)),
|
||||
map((item) => !isEmpty(item) ? this.nameService.getName(item) : this.translateService.instant('statistics.table.no-name')),
|
||||
);
|
||||
case 'TopCities':
|
||||
case 'topCountries':
|
||||
|
@@ -5,5 +5,6 @@
|
||||
[submissionDefinition]="submissionDefinition"
|
||||
[submissionErrors]="submissionErrors"
|
||||
[item]="item"
|
||||
[collectionModifiable]="collectionModifiable"
|
||||
[submissionId]="submissionId"></ds-submission-form>
|
||||
</div>
|
||||
|
@@ -36,6 +36,13 @@ export class SubmissionEditComponent implements OnDestroy, OnInit {
|
||||
*/
|
||||
public collectionId: string;
|
||||
|
||||
/**
|
||||
* Checks if the collection can be modifiable by the user
|
||||
* @type {booelan}
|
||||
*/
|
||||
public collectionModifiable: boolean | null = null;
|
||||
|
||||
|
||||
/**
|
||||
* The list of submission's sections
|
||||
* @type {WorkspaceitemSectionsObject}
|
||||
@@ -109,6 +116,9 @@ export class SubmissionEditComponent implements OnDestroy, OnInit {
|
||||
* Retrieve workspaceitem/workflowitem from server and initialize all instance variables
|
||||
*/
|
||||
ngOnInit() {
|
||||
|
||||
this.collectionModifiable = this.route.snapshot.data?.collectionModifiable ?? null;
|
||||
|
||||
this.subs.push(
|
||||
this.route.paramMap.pipe(
|
||||
switchMap((params: ParamMap) => this.submissionService.retrieveSubmission(params.get('id'))),
|
||||
|
@@ -25,7 +25,7 @@
|
||||
class="btn btn-outline-primary"
|
||||
(blur)="onClose()"
|
||||
(click)="onClose()"
|
||||
[disabled]="(processingChange$ | async)"
|
||||
[disabled]="(processingChange$ | async) || collectionModifiable == false"
|
||||
ngbDropdownToggle>
|
||||
<span *ngIf="(processingChange$ | async)"><i class='fas fa-circle-notch fa-spin'></i></span>
|
||||
<span *ngIf="!(processingChange$ | async)">{{ selectedCollectionName$ | async }}</span>
|
||||
|
@@ -52,6 +52,12 @@ export class SubmissionFormCollectionComponent implements OnChanges, OnInit {
|
||||
*/
|
||||
@Input() currentDefinition: string;
|
||||
|
||||
/**
|
||||
* Checks if the collection can be modifiable by the user
|
||||
* @type {booelan}
|
||||
*/
|
||||
@Input() collectionModifiable: boolean | null = null;
|
||||
|
||||
/**
|
||||
* The submission id
|
||||
* @type {string}
|
||||
|
@@ -11,6 +11,7 @@
|
||||
<ds-submission-form-collection [currentCollectionId]="collectionId"
|
||||
[currentDefinition]="definitionId"
|
||||
[submissionId]="submissionId"
|
||||
[collectionModifiable]="collectionModifiable"
|
||||
(collectionChange)="onCollectionChange($event)">
|
||||
</ds-submission-form-collection>
|
||||
</div>
|
||||
|
@@ -34,8 +34,16 @@ export class SubmissionFormComponent implements OnChanges, OnDestroy {
|
||||
* @type {string}
|
||||
*/
|
||||
@Input() collectionId: string;
|
||||
|
||||
@Input() item: Item;
|
||||
|
||||
/**
|
||||
* Checks if the collection can be modifiable by the user
|
||||
* @type {booelan}
|
||||
*/
|
||||
@Input() collectionModifiable: boolean | null = null;
|
||||
|
||||
|
||||
/**
|
||||
* The list of submission's sections
|
||||
* @type {WorkspaceitemSectionsObject}
|
||||
|
@@ -34,7 +34,11 @@ import {
|
||||
resolve: {
|
||||
breadcrumb: I18nBreadcrumbResolver
|
||||
},
|
||||
data: { title: 'workflow-item.edit.title', breadcrumbKey: 'workflow-item.edit' }
|
||||
data: {
|
||||
title: 'workflow-item.edit.title',
|
||||
breadcrumbKey: 'workflow-item.edit',
|
||||
collectionModifiable: false
|
||||
}
|
||||
},
|
||||
{
|
||||
canActivate: [AuthenticatedGuard],
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -130,7 +130,7 @@
|
||||
"admin.registries.bitstream-formats.table.mimetype": "MIME Type",
|
||||
|
||||
"admin.registries.bitstream-formats.table.name": "Name",
|
||||
"admin.registries.bitstream-formats.table.id" : "ID",
|
||||
"admin.registries.bitstream-formats.table.id": "ID",
|
||||
|
||||
"admin.registries.bitstream-formats.table.return": "Back",
|
||||
|
||||
@@ -185,7 +185,7 @@
|
||||
"admin.registries.schema.fields.table.delete": "Delete selected",
|
||||
|
||||
"admin.registries.schema.fields.table.field": "Field",
|
||||
"admin.registries.schema.fields.table.id" : "ID",
|
||||
"admin.registries.schema.fields.table.id": "ID",
|
||||
|
||||
"admin.registries.schema.fields.table.scopenote": "Scope Note",
|
||||
|
||||
@@ -1404,9 +1404,9 @@
|
||||
|
||||
"cookies.consent.purpose.sharing": "Sharing",
|
||||
|
||||
"curation-task.task.citationpage.label": "Generate Citation Page",
|
||||
"curation-task.task.citationpage.label": "Generate Citation Page",
|
||||
|
||||
"curation-task.task.checklinks.label": "Check Links in Metadata",
|
||||
"curation-task.task.checklinks.label": "Check Links in Metadata",
|
||||
|
||||
"curation-task.task.noop.label": "NOOP",
|
||||
|
||||
@@ -1540,7 +1540,7 @@
|
||||
|
||||
"supervision-group-selector.notification.create.failure.title": "Error",
|
||||
|
||||
"supervision-group-selector.notification.create.already-existing" : "A supervision order already exists on this item for selected group",
|
||||
"supervision-group-selector.notification.create.already-existing": "A supervision order already exists on this item for selected group",
|
||||
|
||||
"confirmation-modal.export-metadata.header": "Export metadata for {{ dsoName }}",
|
||||
|
||||
@@ -1648,9 +1648,9 @@
|
||||
|
||||
"footer.link.privacy-policy": "Privacy policy",
|
||||
|
||||
"footer.link.end-user-agreement":"End User Agreement",
|
||||
"footer.link.end-user-agreement": "End User Agreement",
|
||||
|
||||
"footer.link.feedback":"Send Feedback",
|
||||
"footer.link.feedback": "Send Feedback",
|
||||
|
||||
|
||||
|
||||
@@ -1761,6 +1761,8 @@
|
||||
|
||||
"form.submit": "Save",
|
||||
|
||||
"form.create": "Create",
|
||||
|
||||
"form.repeatable.sort.tip": "Drop the item in the new position",
|
||||
|
||||
|
||||
@@ -1812,11 +1814,11 @@
|
||||
|
||||
"health.breadcrumbs": "Health",
|
||||
|
||||
"health-page.heading" : "Health",
|
||||
"health-page.heading": "Health",
|
||||
|
||||
"health-page.info-tab" : "Info",
|
||||
"health-page.info-tab": "Info",
|
||||
|
||||
"health-page.status-tab" : "Status",
|
||||
"health-page.status-tab": "Status",
|
||||
|
||||
"health-page.error.msg": "The health check service is temporarily unavailable",
|
||||
|
||||
@@ -1903,15 +1905,15 @@
|
||||
|
||||
"info.feedback.email-label": "Your Email",
|
||||
|
||||
"info.feedback.create.success" : "Feedback Sent Successfully!",
|
||||
"info.feedback.create.success": "Feedback Sent Successfully!",
|
||||
|
||||
"info.feedback.error.email.required" : "A valid email address is required",
|
||||
"info.feedback.error.email.required": "A valid email address is required",
|
||||
|
||||
"info.feedback.error.message.required" : "A comment is required",
|
||||
"info.feedback.error.message.required": "A comment is required",
|
||||
|
||||
"info.feedback.page-label" : "Page",
|
||||
"info.feedback.page-label": "Page",
|
||||
|
||||
"info.feedback.page_help" : "Tha page related to your feedback",
|
||||
"info.feedback.page_help": "Tha page related to your feedback",
|
||||
|
||||
|
||||
|
||||
@@ -1939,7 +1941,7 @@
|
||||
|
||||
"item.bitstreams.upload.bundle.new": "Create bundle",
|
||||
|
||||
"item.bitstreams.upload.bundles.empty": "This item doesn\'t contain any bundles to upload a bitstream to.",
|
||||
"item.bitstreams.upload.bundles.empty": "This item doesn't contain any bundles to upload a bitstream to.",
|
||||
|
||||
"item.bitstreams.upload.cancel": "Cancel",
|
||||
|
||||
@@ -2483,9 +2485,9 @@
|
||||
|
||||
"item.page.bitstreams.collapse": "Collapse",
|
||||
|
||||
"item.page.filesection.original.bundle" : "Original bundle",
|
||||
"item.page.filesection.original.bundle": "Original bundle",
|
||||
|
||||
"item.page.filesection.license.bundle" : "License bundle",
|
||||
"item.page.filesection.license.bundle": "License bundle",
|
||||
|
||||
"item.page.return": "Back",
|
||||
|
||||
@@ -2515,19 +2517,19 @@
|
||||
|
||||
"item.preview.dc.type": "Type:",
|
||||
|
||||
"item.preview.oaire.citation.issue" : "Issue",
|
||||
"item.preview.oaire.citation.issue": "Issue",
|
||||
|
||||
"item.preview.oaire.citation.volume" : "Volume",
|
||||
"item.preview.oaire.citation.volume": "Volume",
|
||||
|
||||
"item.preview.dc.relation.issn" : "ISSN",
|
||||
"item.preview.dc.relation.issn": "ISSN",
|
||||
|
||||
"item.preview.dc.identifier.isbn" : "ISBN",
|
||||
"item.preview.dc.identifier.isbn": "ISBN",
|
||||
|
||||
"item.preview.dc.identifier": "Identifier:",
|
||||
|
||||
"item.preview.dc.relation.ispartof" : "Journal or Serie",
|
||||
"item.preview.dc.relation.ispartof": "Journal or Serie",
|
||||
|
||||
"item.preview.dc.identifier.doi" : "DOI",
|
||||
"item.preview.dc.identifier.doi": "DOI",
|
||||
|
||||
"item.preview.person.familyName": "Surname:",
|
||||
|
||||
@@ -2626,11 +2628,11 @@
|
||||
|
||||
"item.version.create.modal.submitted.text": "The new version is being created. This may take some time if the item has a lot of relationships.",
|
||||
|
||||
"item.version.create.notification.success" : "New version has been created with version number {{version}}",
|
||||
"item.version.create.notification.success": "New version has been created with version number {{version}}",
|
||||
|
||||
"item.version.create.notification.failure" : "New version has not been created",
|
||||
"item.version.create.notification.failure": "New version has not been created",
|
||||
|
||||
"item.version.create.notification.inProgress" : "A new version cannot be created because there is an inprogress submission in the version history",
|
||||
"item.version.create.notification.inProgress": "A new version cannot be created because there is an inprogress submission in the version history",
|
||||
|
||||
|
||||
"item.version.delete.modal.header": "Delete version",
|
||||
@@ -2645,14 +2647,14 @@
|
||||
|
||||
"item.version.delete.modal.button.cancel.tooltip": "Do not delete this version",
|
||||
|
||||
"item.version.delete.notification.success" : "Version number {{version}} has been deleted",
|
||||
"item.version.delete.notification.success": "Version number {{version}} has been deleted",
|
||||
|
||||
"item.version.delete.notification.failure" : "Version number {{version}} has not been deleted",
|
||||
"item.version.delete.notification.failure": "Version number {{version}} has not been deleted",
|
||||
|
||||
|
||||
"item.version.edit.notification.success" : "The summary of version number {{version}} has been changed",
|
||||
"item.version.edit.notification.success": "The summary of version number {{version}} has been changed",
|
||||
|
||||
"item.version.edit.notification.failure" : "The summary of version number {{version}} has not been changed",
|
||||
"item.version.edit.notification.failure": "The summary of version number {{version}} has not been changed",
|
||||
|
||||
|
||||
|
||||
@@ -2688,7 +2690,7 @@
|
||||
|
||||
"itemtemplate.edit.metadata.notifications.discarded.content": "Your changes were discarded. To reinstate your changes click the 'Undo' button",
|
||||
|
||||
"itemtemplate.edit.metadata.notifications.discarded.title": "Changed discarded",
|
||||
"itemtemplate.edit.metadata.notifications.discarded.title": "Changes discarded",
|
||||
|
||||
"itemtemplate.edit.metadata.notifications.error.title": "An error occurred",
|
||||
|
||||
@@ -2698,7 +2700,7 @@
|
||||
|
||||
"itemtemplate.edit.metadata.notifications.outdated.content": "The item template you're currently working on has been changed by another user. Your current changes are discarded to prevent conflicts",
|
||||
|
||||
"itemtemplate.edit.metadata.notifications.outdated.title": "Changed outdated",
|
||||
"itemtemplate.edit.metadata.notifications.outdated.title": "Changes outdated",
|
||||
|
||||
"itemtemplate.edit.metadata.notifications.saved.content": "Your changes to this item template's metadata were saved.",
|
||||
|
||||
@@ -3158,11 +3160,11 @@
|
||||
|
||||
"nav.stop-impersonating": "Stop impersonating EPerson",
|
||||
|
||||
"nav.subscriptions" : "Subscriptions",
|
||||
"nav.subscriptions": "Subscriptions",
|
||||
|
||||
"nav.toggle" : "Toggle navigation",
|
||||
"nav.toggle": "Toggle navigation",
|
||||
|
||||
"nav.user.description" : "User profile bar",
|
||||
"nav.user.description": "User profile bar",
|
||||
|
||||
"none.listelement.badge": "Item",
|
||||
|
||||
@@ -3265,6 +3267,8 @@
|
||||
|
||||
"process.new.notification.error.content": "An error occurred while creating this process",
|
||||
|
||||
"process.new.notification.error.max-upload.content": "The file exceeds the maximum upload size",
|
||||
|
||||
"process.new.header": "Create a new process",
|
||||
|
||||
"process.new.title": "Create a new process",
|
||||
@@ -3273,13 +3277,13 @@
|
||||
|
||||
|
||||
|
||||
"process.detail.arguments" : "Arguments",
|
||||
"process.detail.arguments": "Arguments",
|
||||
|
||||
"process.detail.arguments.empty" : "This process doesn't contain any arguments",
|
||||
"process.detail.arguments.empty": "This process doesn't contain any arguments",
|
||||
|
||||
"process.detail.back" : "Back",
|
||||
"process.detail.back": "Back",
|
||||
|
||||
"process.detail.output" : "Process Output",
|
||||
"process.detail.output": "Process Output",
|
||||
|
||||
"process.detail.logs.button": "Retrieve process output",
|
||||
|
||||
@@ -3287,21 +3291,21 @@
|
||||
|
||||
"process.detail.logs.none": "This process has no output",
|
||||
|
||||
"process.detail.output-files" : "Output Files",
|
||||
"process.detail.output-files": "Output Files",
|
||||
|
||||
"process.detail.output-files.empty" : "This process doesn't contain any output files",
|
||||
"process.detail.output-files.empty": "This process doesn't contain any output files",
|
||||
|
||||
"process.detail.script" : "Script",
|
||||
"process.detail.script": "Script",
|
||||
|
||||
"process.detail.title" : "Process: {{ id }} - {{ name }}",
|
||||
"process.detail.title": "Process: {{ id }} - {{ name }}",
|
||||
|
||||
"process.detail.start-time" : "Start time",
|
||||
"process.detail.start-time": "Start time",
|
||||
|
||||
"process.detail.end-time" : "Finish time",
|
||||
"process.detail.end-time": "Finish time",
|
||||
|
||||
"process.detail.status" : "Status",
|
||||
"process.detail.status": "Status",
|
||||
|
||||
"process.detail.create" : "Create similar process",
|
||||
"process.detail.create": "Create similar process",
|
||||
|
||||
"process.detail.actions": "Actions",
|
||||
|
||||
@@ -3321,17 +3325,17 @@
|
||||
|
||||
|
||||
|
||||
"process.overview.table.finish" : "Finish time (UTC)",
|
||||
"process.overview.table.finish": "Finish time (UTC)",
|
||||
|
||||
"process.overview.table.id" : "Process ID",
|
||||
"process.overview.table.id": "Process ID",
|
||||
|
||||
"process.overview.table.name" : "Name",
|
||||
"process.overview.table.name": "Name",
|
||||
|
||||
"process.overview.table.start" : "Start time (UTC)",
|
||||
"process.overview.table.start": "Start time (UTC)",
|
||||
|
||||
"process.overview.table.status" : "Status",
|
||||
"process.overview.table.status": "Status",
|
||||
|
||||
"process.overview.table.user" : "User",
|
||||
"process.overview.table.user": "User",
|
||||
|
||||
"process.overview.title": "Processes Overview",
|
||||
|
||||
@@ -4041,6 +4045,8 @@
|
||||
|
||||
"statistics.table.header.views": "Views",
|
||||
|
||||
"statistics.table.no-name": "(object name could not be loaded)",
|
||||
|
||||
|
||||
|
||||
"submission.edit.breadcrumbs": "Edit Submission",
|
||||
@@ -4432,7 +4438,7 @@
|
||||
|
||||
"submission.sections.describe.relationship-lookup.selection-tab.title": "Search Results",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.name-variant.notification.content": "Would you like to save \"{{ value }}\" as a name variant for this person so you and others can reuse it for future submissions? If you don\'t you can still use it for this submission.",
|
||||
"submission.sections.describe.relationship-lookup.name-variant.notification.content": "Would you like to save \"{{ value }}\" as a name variant for this person so you and others can reuse it for future submissions? If you don't you can still use it for this submission.",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.name-variant.notification.confirm": "Save a new name variant",
|
||||
|
||||
@@ -4639,7 +4645,7 @@
|
||||
|
||||
"submission.sections.license.required": "You must accept the license",
|
||||
|
||||
"submission.sections.license.notgranted": "You must accept the license",
|
||||
"submission.sections.license.notgranted": "You must accept the license",
|
||||
|
||||
|
||||
"submission.sections.sherpa.publication.information": "Publication information",
|
||||
@@ -5008,7 +5014,7 @@
|
||||
|
||||
"idle-modal.extend-session": "Extend session",
|
||||
|
||||
"researcher.profile.action.processing" : "Processing...",
|
||||
"researcher.profile.action.processing": "Processing...",
|
||||
|
||||
"researcher.profile.associated": "Researcher profile associated",
|
||||
|
||||
@@ -5030,27 +5036,27 @@
|
||||
|
||||
"researcher.profile.view": "View",
|
||||
|
||||
"researcher.profile.private.visibility" : "PRIVATE",
|
||||
"researcher.profile.private.visibility": "PRIVATE",
|
||||
|
||||
"researcher.profile.public.visibility" : "PUBLIC",
|
||||
"researcher.profile.public.visibility": "PUBLIC",
|
||||
|
||||
"researcher.profile.status": "Status:",
|
||||
|
||||
"researcherprofile.claim.not-authorized": "You are not authorized to claim this item. For more details contact the administrator(s).",
|
||||
|
||||
"researcherprofile.error.claim.body" : "An error occurred while claiming the profile, please try again later",
|
||||
"researcherprofile.error.claim.body": "An error occurred while claiming the profile, please try again later",
|
||||
|
||||
"researcherprofile.error.claim.title" : "Error",
|
||||
"researcherprofile.error.claim.title": "Error",
|
||||
|
||||
"researcherprofile.success.claim.body" : "Profile claimed with success",
|
||||
"researcherprofile.success.claim.body": "Profile claimed with success",
|
||||
|
||||
"researcherprofile.success.claim.title" : "Success",
|
||||
"researcherprofile.success.claim.title": "Success",
|
||||
|
||||
"person.page.orcid.create": "Create an ORCID ID",
|
||||
|
||||
"person.page.orcid.granted-authorizations": "Granted authorizations",
|
||||
|
||||
"person.page.orcid.grant-authorizations" : "Grant authorizations",
|
||||
"person.page.orcid.grant-authorizations": "Grant authorizations",
|
||||
|
||||
"person.page.orcid.link": "Connect to ORCID ID",
|
||||
|
||||
@@ -5082,31 +5088,31 @@
|
||||
|
||||
"person.page.orcid.save.preference.changes": "Update settings",
|
||||
|
||||
"person.page.orcid.sync-profile.affiliation" : "Affiliation",
|
||||
"person.page.orcid.sync-profile.affiliation": "Affiliation",
|
||||
|
||||
"person.page.orcid.sync-profile.biographical" : "Biographical data",
|
||||
"person.page.orcid.sync-profile.biographical": "Biographical data",
|
||||
|
||||
"person.page.orcid.sync-profile.education" : "Education",
|
||||
"person.page.orcid.sync-profile.education": "Education",
|
||||
|
||||
"person.page.orcid.sync-profile.identifiers" : "Identifiers",
|
||||
"person.page.orcid.sync-profile.identifiers": "Identifiers",
|
||||
|
||||
"person.page.orcid.sync-fundings.all" : "All fundings",
|
||||
"person.page.orcid.sync-fundings.all": "All fundings",
|
||||
|
||||
"person.page.orcid.sync-fundings.mine" : "My fundings",
|
||||
"person.page.orcid.sync-fundings.mine": "My fundings",
|
||||
|
||||
"person.page.orcid.sync-fundings.my_selected" : "Selected fundings",
|
||||
"person.page.orcid.sync-fundings.my_selected": "Selected fundings",
|
||||
|
||||
"person.page.orcid.sync-fundings.disabled" : "Disabled",
|
||||
"person.page.orcid.sync-fundings.disabled": "Disabled",
|
||||
|
||||
"person.page.orcid.sync-publications.all" : "All publications",
|
||||
"person.page.orcid.sync-publications.all": "All publications",
|
||||
|
||||
"person.page.orcid.sync-publications.mine" : "My publications",
|
||||
"person.page.orcid.sync-publications.mine": "My publications",
|
||||
|
||||
"person.page.orcid.sync-publications.my_selected" : "Selected publications",
|
||||
"person.page.orcid.sync-publications.my_selected": "Selected publications",
|
||||
|
||||
"person.page.orcid.sync-publications.disabled" : "Disabled",
|
||||
"person.page.orcid.sync-publications.disabled": "Disabled",
|
||||
|
||||
"person.page.orcid.sync-queue.discard" : "Discard the change and do not synchronize with the ORCID registry",
|
||||
"person.page.orcid.sync-queue.discard": "Discard the change and do not synchronize with the ORCID registry",
|
||||
|
||||
"person.page.orcid.sync-queue.discard.error": "The discarding of the ORCID queue record failed",
|
||||
|
||||
@@ -5114,11 +5120,11 @@
|
||||
|
||||
"person.page.orcid.sync-queue.empty-message": "The ORCID queue registry is empty",
|
||||
|
||||
"person.page.orcid.sync-queue.table.header.type" : "Type",
|
||||
"person.page.orcid.sync-queue.table.header.type": "Type",
|
||||
|
||||
"person.page.orcid.sync-queue.table.header.description" : "Description",
|
||||
"person.page.orcid.sync-queue.table.header.description": "Description",
|
||||
|
||||
"person.page.orcid.sync-queue.table.header.action" : "Action",
|
||||
"person.page.orcid.sync-queue.table.header.action": "Action",
|
||||
|
||||
"person.page.orcid.sync-queue.description.affiliation": "Affiliations",
|
||||
|
||||
@@ -5162,7 +5168,7 @@
|
||||
|
||||
"person.page.orcid.sync-queue.tooltip.researcher_urls": "Researcher url",
|
||||
|
||||
"person.page.orcid.sync-queue.send" : "Synchronize with ORCID registry",
|
||||
"person.page.orcid.sync-queue.send": "Synchronize with ORCID registry",
|
||||
|
||||
"person.page.orcid.sync-queue.send.unauthorized-error.title": "The submission to ORCID failed for missing authorizations.",
|
||||
|
||||
@@ -5198,7 +5204,7 @@
|
||||
|
||||
"person.page.orcid.sync-queue.send.validation-error.organization.name-required": "The organization's name is required",
|
||||
|
||||
"person.page.orcid.sync-queue.send.validation-error.publication.date-invalid" : "The publication date must be one year after 1900",
|
||||
"person.page.orcid.sync-queue.send.validation-error.publication.date-invalid": "The publication date must be one year after 1900",
|
||||
|
||||
"person.page.orcid.sync-queue.send.validation-error.organization.address-required": "The organization to be sent requires an address",
|
||||
|
||||
|
@@ -195,7 +195,7 @@
|
||||
// "admin.registries.bitstream-formats.table.name": "Name",
|
||||
"admin.registries.bitstream-formats.table.name": "Nombre",
|
||||
// "admin.registries.bitstream-formats.table.id" : "ID",
|
||||
"admin.registries.bitstream-formats.table.id" : "ID",
|
||||
"admin.registries.bitstream-formats.table.id": "ID",
|
||||
|
||||
// "admin.registries.bitstream-formats.table.return": "Back",
|
||||
"admin.registries.bitstream-formats.table.return": "Atrás",
|
||||
@@ -276,7 +276,7 @@
|
||||
// "admin.registries.schema.fields.table.field": "Field",
|
||||
"admin.registries.schema.fields.table.field": "Campo",
|
||||
// "admin.registries.schema.fields.table.id" : "ID",
|
||||
"admin.registries.schema.fields.table.id" : "ID",
|
||||
"admin.registries.schema.fields.table.id": "ID",
|
||||
|
||||
// "admin.registries.schema.fields.table.scopenote": "Scope Note",
|
||||
"admin.registries.schema.fields.table.scopenote": "Nota de alcance",
|
||||
@@ -752,8 +752,8 @@
|
||||
// "admin.access-control.groups.form.tooltip.editGroup.addEpeople": "To add or remove an EPerson to/from this group, either click the 'Browse All' button or use the search bar below to search for users (use the dropdown to the left of the search bar to choose whether to search by metadata or by email). Then click the plus icon for each user you wish to add in the list below, or the trash can icon for each user you wish to remove. The list below may have several pages: use the page controls below the list to navigate to the next pages. Once you are ready, save your changes by clicking the 'Save' button in the top section.",
|
||||
"admin.access-control.groups.form.tooltip.editGroup.addEpeople": "Para agregar o remover una persona en este grupo, pulse el botón ‘Examinar todo’ o utilice la barra de búsqueda de abajo para buscar los usuario (Use el desplegable que se encuentra a la derecha de la barra de búsqueda para seleccionar entre buscar por metadato o por correo electrónico). Posteriormente pulse el botón con el icono más por cada usuario que desea agregar a la lista, o el icono de papelera por cada usuario que desea remover. La lista puede contener varias páginas: utilice los controles de paginación debajo de la lista, para navegar a las siguientes páginas. Cuando haya finalizado, guarde sus cambios pulsando el botón ‘Guardar’ ubicado en la parte superior de la sección.",
|
||||
|
||||
// "admin.access-control.groups.form.tooltip.editGroup.addSubgroups": "To add or remove a Subgroup to/from this group, either click the 'Browse All' button or use the search bar below to search for subgroups. Then click the plus icon for each subgroup you wish to add in the list below, or the trash can icon for each subgroup you wish to remove. The list below may have several pages: use the page controls below the list to navigate to the next pages. Once you are ready, save your changes by clicking the 'Save' button in the top section.",
|
||||
"admin.access-control.groups.form.tooltip.editGroup.addSubgroups": "Para agregar o remover un sub-grupo en este grupo, pulse el botón ‘Examinar todo’ o utilice la barra de búsqueda de abajo para buscar los subgrupos- Posteriormente pulse el botón con el icono más por cada sub-grupo que desea agregar a la lista, o el icono de papelera por cada sub-grupo que desea remover. La lista puede contener varias páginas: utilice los controles de paginación debajo de la lista, para navegar a las siguientes páginas. Cuando haya finalizado, guarde sus cambios pulsando el botón ‘Guardar’ ubicado en la parte superior de la sección.",
|
||||
// "admin.access-control.groups.form.tooltip.editGroup.addSubgroups": "To add or remove a Subgroup to/from this group, either click the 'Browse All' button or use the search bar below to search for users. Then click the plus icon for each user you wish to add in the list below, or the trash can icon for each user you wish to remove. The list below may have several pages: use the page controls below the list to navigate to the next pages. Once you are ready, save your changes by clicking the 'Save' button in the top section.",
|
||||
"admin.access-control.groups.form.tooltip.editGroup.addSubgroups": "Para agregar o remover un sub-grupo en este grupo, pulse el botón 'Examinar todo' o utilice la barra de búsqueda de abajo para buscar los subgrupos- Posteriormente pulse el botón con el icono más por cada sub-grupo que desea agregar a la lista, o el icono de papelera por cada sub-grupo que desea remover. La lista puede contener varias páginas: utilice los controles de paginación debajo de la lista, para navegar a las siguientes páginas. Cuando haya finalizado, guarde sus cambios pulsando el botón ‘Guardar’ ubicado en la parte superior de la sección.",
|
||||
|
||||
// "admin.search.breadcrumbs": "Administrative Search",
|
||||
"admin.search.breadcrumbs": "Búsqueda administrativa",
|
||||
@@ -1891,12 +1891,10 @@
|
||||
|
||||
|
||||
// "comcol-role.edit.scorereviewers.name": "Score Reviewers",
|
||||
// TODO New key - Add a translation
|
||||
"comcol-role.edit.scorereviewers.name": "Score Reviewers",
|
||||
"comcol-role.edit.scorereviewers.name": "Revisores de puntuación",
|
||||
|
||||
// "comcol-role.edit.scorereviewers.description": "Reviewers are able to give a score to incoming submissions, this will define whether the submission will be rejected or not.",
|
||||
// TODO New key - Add a translation
|
||||
"comcol-role.edit.scorereviewers.description": "Reviewers are able to give a score to incoming submissions, this will define whether the submission will be rejected or not.",
|
||||
"comcol-role.edit.scorereviewers.description": "Los revisores pueden dar una puntuación a un envío entrante, esto definirá si el envío será rechazado o no.",
|
||||
|
||||
// "community.form.abstract": "Short Description",
|
||||
"community.form.abstract": "Breve descripción",
|
||||
@@ -2062,7 +2060,7 @@
|
||||
"cookies.consent.purpose.sharing": "Compartición",
|
||||
|
||||
// "curation-task.task.citationpage.label": "Generate Citation Page",
|
||||
"curation-task.task.citationpage.label": "Generar página de cita",
|
||||
"curation-task.task.citationpage.label": "Generar página de cita",
|
||||
|
||||
// "curation-task.task.checklinks.label": "Check Links in Metadata",
|
||||
"curation-task.task.checklinks.label": "Comprobar enlaces en metadatos",
|
||||
@@ -2262,7 +2260,7 @@
|
||||
"supervision-group-selector.notification.create.failure.title": "Error",
|
||||
|
||||
// "supervision-group-selector.notification.create.already-existing" : "A supervision order already exists on this item for selected group",
|
||||
"supervision-group-selector.notification.create.already-existing" : "Ya existe una orden de supervisión para este ítem en el grupo selecionado",
|
||||
"supervision-group-selector.notification.create.already-existing": "Ya existe una orden de supervisión para este ítem en el grupo selecionado",
|
||||
|
||||
// "confirmation-modal.export-metadata.header": "Export metadata for {{ dsoName }}",
|
||||
"confirmation-modal.export-metadata.header": "Exportar metadatos para {{ dsoName }}",
|
||||
@@ -2425,7 +2423,7 @@
|
||||
"footer.link.end-user-agreement": "Acuerdo de usuario final",
|
||||
|
||||
// "footer.link.feedback":"Send Feedback",
|
||||
"footer.link.feedback":"Enviar Sugerencias",
|
||||
"footer.link.feedback": "Enviar Sugerencias",
|
||||
|
||||
|
||||
|
||||
@@ -2588,6 +2586,9 @@
|
||||
// "form.submit": "Save",
|
||||
"form.submit": "Guardar",
|
||||
|
||||
// "form.create": "Create",
|
||||
"form.create": "Crear",
|
||||
|
||||
// "form.repeatable.sort.tip": "Drop the item in the new position",
|
||||
"form.repeatable.sort.tip": "Suelte el ítem en la nueva posición",
|
||||
|
||||
@@ -2663,13 +2664,13 @@
|
||||
"health.breadcrumbs": "Chequeos",
|
||||
|
||||
// "health-page.heading" : "Health",
|
||||
"health-page.heading" : "Chequeos",
|
||||
"health-page.heading": "Chequeos",
|
||||
|
||||
// "health-page.info-tab" : "Info",
|
||||
"health-page.info-tab" : "Información",
|
||||
"health-page.info-tab": "Información",
|
||||
|
||||
// "health-page.status-tab" : "Status",
|
||||
"health-page.status-tab" : "Estado",
|
||||
"health-page.status-tab": "Estado",
|
||||
|
||||
// "health-page.error.msg": "The health check service is temporarily unavailable",
|
||||
"health-page.error.msg": "El servicio de comprobación no se encuentra temporalmente disponible",
|
||||
@@ -2798,19 +2799,19 @@
|
||||
"info.feedback.email-label": "Su correo electrónico",
|
||||
|
||||
// "info.feedback.create.success" : "Feedback Sent Successfully!",
|
||||
"info.feedback.create.success" : "Envío exitoso de sugerencia",
|
||||
"info.feedback.create.success": "Envío exitoso de sugerencia",
|
||||
|
||||
// "info.feedback.error.email.required" : "A valid email address is required",
|
||||
"info.feedback.error.email.required" : "se requiere una dirección válida de correo electrónico",
|
||||
"info.feedback.error.email.required": "se requiere una dirección válida de correo electrónico",
|
||||
|
||||
// "info.feedback.error.message.required" : "A comment is required",
|
||||
"info.feedback.error.message.required" : "Se requiere un comentario",
|
||||
"info.feedback.error.message.required": "Se requiere un comentario",
|
||||
|
||||
// "info.feedback.page-label" : "Page",
|
||||
"info.feedback.page-label" : "Página",
|
||||
"info.feedback.page-label": "Página",
|
||||
|
||||
// "info.feedback.page_help" : "Tha page related to your feedback",
|
||||
"info.feedback.page_help" : "La página relacionada con su sugerencia",
|
||||
"info.feedback.page_help": "La página relacionada con su sugerencia",
|
||||
|
||||
|
||||
|
||||
@@ -2847,7 +2848,7 @@
|
||||
// "item.bitstreams.upload.bundle.new": "Create bundle",
|
||||
"item.bitstreams.upload.bundle.new": "Crear bloque",
|
||||
|
||||
// "item.bitstreams.upload.bundles.empty": "This item doesn\'t contain any bundles to upload a bitstream to.",
|
||||
// "item.bitstreams.upload.bundles.empty": "This item doesn't contain any bundles to upload a bitstream to.",
|
||||
"item.bitstreams.upload.bundles.empty": "Este ítem no contiene ningún bloque para cargar un archivo.",
|
||||
|
||||
// "item.bitstreams.upload.cancel": "Cancel",
|
||||
@@ -3547,10 +3548,10 @@
|
||||
"workflow-item.search.result.delete-supervision.modal.confirm": "Borrar",
|
||||
|
||||
// "workflow-item.search.result.notification.deleted.success": "Successfully deleted supervision order \"{{name}}\"",
|
||||
"workflow-item.search.result.notification.deleted.success": "La orden de supervisión \”{{ name }}”\ ha sido borrada exitosamente",
|
||||
"workflow-item.search.result.notification.deleted.success": "La orden de supervisión \"{{ name }}\" ha sido borrada exitosamente",
|
||||
|
||||
// "workflow-item.search.result.notification.deleted.failure": "Failed to delete supervision order \"{{name}}\"",
|
||||
"workflow-item.search.result.notification.deleted.failure": "Error al borrar la orden de supervisión \”{{ name }}\”",
|
||||
"workflow-item.search.result.notification.deleted.failure": "Error al borrar la orden de supervisión \"{{ name }}\"",
|
||||
|
||||
// "workflow-item.search.result.list.element.supervised-by": "Supervised by:",
|
||||
"workflow-item.search.result.list.element.supervised-by": "Supervisado por:",
|
||||
@@ -3699,25 +3700,25 @@
|
||||
"item.preview.dc.type": "Tipo:",
|
||||
|
||||
// "item.preview.oaire.citation.issue" : "Issue",
|
||||
"item.preview.oaire.citation.issue" : "Número",
|
||||
"item.preview.oaire.citation.issue": "Número",
|
||||
|
||||
// "item.preview.oaire.citation.volume" : "Volume",
|
||||
"item.preview.oaire.citation.volume" : "Volumen",
|
||||
"item.preview.oaire.citation.volume": "Volumen",
|
||||
|
||||
// "item.preview.dc.relation.issn" : "ISSN",
|
||||
"item.preview.dc.relation.issn" : "ISSN",
|
||||
"item.preview.dc.relation.issn": "ISSN",
|
||||
|
||||
// "item.preview.dc.identifier.isbn" : "ISBN",
|
||||
"item.preview.dc.identifier.isbn" : "ISBN",
|
||||
"item.preview.dc.identifier.isbn": "ISBN",
|
||||
|
||||
// "item.preview.dc.identifier": "Identifier:",
|
||||
"item.preview.dc.identifier": "Identificador:",
|
||||
|
||||
// "item.preview.dc.relation.ispartof" : "Journal or Serie",
|
||||
"item.preview.dc.relation.ispartof" : "Revista o Serie",
|
||||
"item.preview.dc.relation.ispartof": "Revista o Serie",
|
||||
|
||||
// "item.preview.dc.identifier.doi" : "DOI",
|
||||
"item.preview.dc.identifier.doi" : "DOI",
|
||||
"item.preview.dc.identifier.doi": "DOI",
|
||||
|
||||
// "item.preview.person.familyName": "Surname:",
|
||||
"item.preview.person.familyName": "Apellido:",
|
||||
@@ -3863,13 +3864,13 @@
|
||||
"item.version.create.modal.submitted.text": "Se está creando la nueva versión. Si el ítem tiene muchas relaciones, este proceso podría tardar.",
|
||||
|
||||
// "item.version.create.notification.success" : "New version has been created with version number {{version}}",
|
||||
"item.version.create.notification.success" : "Se ha creado una nueva versión con número {{version}}",
|
||||
"item.version.create.notification.success": "Se ha creado una nueva versión con número {{version}}",
|
||||
|
||||
// "item.version.create.notification.failure" : "New version has not been created",
|
||||
"item.version.create.notification.failure" : "No se ha creado una nueva versión",
|
||||
"item.version.create.notification.failure": "No se ha creado una nueva versión",
|
||||
|
||||
// "item.version.create.notification.inProgress" : "A new version cannot be created because there is an inprogress submission in the version history",
|
||||
"item.version.create.notification.inProgress" : "No es posible crear una nueva versión puesto que existe en el historial de versiones un envío pendiente",
|
||||
"item.version.create.notification.inProgress": "No es posible crear una nueva versión puesto que existe en el historial de versiones un envío pendiente",
|
||||
|
||||
|
||||
// "item.version.delete.modal.header": "Delete version",
|
||||
@@ -3891,17 +3892,17 @@
|
||||
"item.version.delete.modal.button.cancel.tooltip": "No borrar esta versión",
|
||||
|
||||
// "item.version.delete.notification.success" : "Version number {{version}} has been deleted",
|
||||
"item.version.delete.notification.success" : "Se ha borrado la versión número {{version}}",
|
||||
"item.version.delete.notification.success": "Se ha borrado la versión número {{version}}",
|
||||
|
||||
// "item.version.delete.notification.failure" : "Version number {{version}} has not been deleted",
|
||||
"item.version.delete.notification.failure" : "No se ha borrado la versión número {{version}}",
|
||||
"item.version.delete.notification.failure": "No se ha borrado la versión número {{version}}",
|
||||
|
||||
|
||||
// "item.version.edit.notification.success" : "The summary of version number {{version}} has been changed",
|
||||
"item.version.edit.notification.success" : "Ha cambiado el resumen de la versión número {{version}}",
|
||||
"item.version.edit.notification.success": "Ha cambiado el resumen de la versión número {{version}}",
|
||||
|
||||
// "item.version.edit.notification.failure" : "The summary of version number {{version}} has not been changed",
|
||||
"item.version.edit.notification.failure" : "No ha cambiado el resumen de la versión número {{version}}",
|
||||
"item.version.edit.notification.failure": "No ha cambiado el resumen de la versión número {{version}}",
|
||||
|
||||
|
||||
|
||||
@@ -3953,8 +3954,8 @@
|
||||
// "itemtemplate.edit.metadata.notifications.discarded.content": "Your changes were discarded. To reinstate your changes click the 'Undo' button",
|
||||
"itemtemplate.edit.metadata.notifications.discarded.content": "Sus cambios fueron descartados. Para recuperar sus cambios pulse el botón 'Deshacer'",
|
||||
|
||||
// "itemtemplate.edit.metadata.notifications.discarded.title": "Changed discarded",
|
||||
"itemtemplate.edit.metadata.notifications.discarded.title": "Cambio descartado",
|
||||
// "itemtemplate.edit.metadata.notifications.discarded.title": "Changes discarded",
|
||||
"itemtemplate.edit.metadata.notifications.discarded.title": "Cambios descartados",
|
||||
|
||||
// "itemtemplate.edit.metadata.notifications.error.title": "An error occurred",
|
||||
"itemtemplate.edit.metadata.notifications.error.title": "Ocurrió un error",
|
||||
@@ -3968,8 +3969,8 @@
|
||||
// "itemtemplate.edit.metadata.notifications.outdated.content": "The item template you're currently working on has been changed by another user. Your current changes are discarded to prevent conflicts",
|
||||
"itemtemplate.edit.metadata.notifications.outdated.content": "La plantilla de ítem en la que usted esta trabajando actualmente ha sido cambiada por otro usuario. Sus cambios han sido descartados para evitar conflictos",
|
||||
|
||||
// "itemtemplate.edit.metadata.notifications.outdated.title": "Changed outdated",
|
||||
"itemtemplate.edit.metadata.notifications.outdated.title": "Cambiado obsoleto",
|
||||
// "itemtemplate.edit.metadata.notifications.outdated.title": "Changes outdated",
|
||||
"itemtemplate.edit.metadata.notifications.outdated.title": "Cambios obsoletos",
|
||||
|
||||
// "itemtemplate.edit.metadata.notifications.saved.content": "Your changes to this item template's metadata were saved.",
|
||||
"itemtemplate.edit.metadata.notifications.saved.content": "Sus cambios a los metadatos de esta plantilla de ítem han sido guardados.",
|
||||
@@ -4637,7 +4638,7 @@
|
||||
"nav.stop-impersonating": "Dejar de hacerse pasar por usuario",
|
||||
|
||||
// "nav.subscriptions" : "Subscriptions",
|
||||
"nav.subscriptions" : "Suscripciones",
|
||||
"nav.subscriptions": "Suscripciones",
|
||||
|
||||
// "nav.toggle" : "Toggle navigation",
|
||||
"nav.toggle": "Alternar navegación",
|
||||
@@ -5211,7 +5212,6 @@
|
||||
|
||||
// "register-page.registration.google-recaptcha.must-accept-cookies": "In order to register you must accept the <b>Registration and Password recovery</b> (Google reCaptcha) cookies.",
|
||||
"register-page.registration.google-recaptcha.must-accept-cookies": "Para registrarse debe aceptar las cookies de <b>Registro y recuperación de contraseña</b> (Google reCaptcha).",
|
||||
|
||||
// "register-page.registration.error.maildomain": "This email address is not on the list of domains who can register. Allowed domains are {{ domains }}",
|
||||
"register-page.registration.error.maildomain": "Este correo electrónico no esta en la lista de dominios que pueden registrarse. Los dominios permitidos son {{ domains }}",
|
||||
|
||||
@@ -5226,7 +5226,6 @@
|
||||
|
||||
// "register-page.registration.google-recaptcha.notification.message.expired": "Verification expired. Please verify again.",
|
||||
"register-page.registration.google-recaptcha.notification.message.expired": "Verificación caducada. Verifique de nuevo.",
|
||||
|
||||
// "register-page.registration.info.maildomain": "Accounts can be registered for mail addresses of the domains",
|
||||
"register-page.registration.info.maildomain": "Las cuentas pueden registrarse para las direcciones de correo de los dominios",
|
||||
|
||||
@@ -5943,6 +5942,8 @@
|
||||
// "statistics.table.header.views": "Views",
|
||||
"statistics.table.header.views": "Visitas",
|
||||
|
||||
// "statistics.table.no-name": "(object name could not be loaded)",
|
||||
"statistics.table.no-name": "(el nombre del objeto no pudo ser cargado)",
|
||||
|
||||
|
||||
// "submission.edit.breadcrumbs": "Edit Submission",
|
||||
@@ -6531,7 +6532,7 @@
|
||||
// "submission.sections.describe.relationship-lookup.selection-tab.title": "Search Results",
|
||||
"submission.sections.describe.relationship-lookup.selection-tab.title": "Resultados de la búsqueda",
|
||||
|
||||
// "submission.sections.describe.relationship-lookup.name-variant.notification.content": "Would you like to save \"{{ value }}\" as a name variant for this person so you and others can reuse it for future submissions? If you don\'t you can still use it for this submission.",
|
||||
// "submission.sections.describe.relationship-lookup.name-variant.notification.content": "Would you like to save \"{{ value }}\" as a name variant for this person so you and others can reuse it for future submissions? If you don't you can still use it for this submission.",
|
||||
"submission.sections.describe.relationship-lookup.name-variant.notification.content": "¿Le gustaría guardar \"{{ value }}\" como una variante de nombre para esta persona para que usted y otros puedan reutilizarlo para envíos futuros? Si no lo hace, aún puede usarlo para este envío.",
|
||||
|
||||
// "submission.sections.describe.relationship-lookup.name-variant.notification.confirm": "Save a new name variant",
|
||||
@@ -6842,7 +6843,7 @@
|
||||
"submission.sections.license.required": "Debe aceptar la licencia",
|
||||
|
||||
// "submission.sections.license.notgranted": "You must accept the license",
|
||||
"submission.sections.license.notgranted": "Debe aceptar la licencia",
|
||||
"submission.sections.license.notgranted": "Debe aceptar la licencia",
|
||||
|
||||
|
||||
// "submission.sections.sherpa.publication.information": "Publication information",
|
||||
@@ -6936,8 +6937,8 @@
|
||||
// "submission.workflow.generic.delete": "Delete",
|
||||
"submission.workflow.generic.delete": "Eliminar",
|
||||
|
||||
// "submission.workflow.generic.delete-help": "If you would to discard this item, select \"Delete\". You will then be asked to confirm it.",
|
||||
"submission.workflow.generic.delete-help": "Si desea descartar este ítem, seleccione \"Eliminar\". Luego se le pedirá que lo confirme.",
|
||||
// "submission.workflow.generic.delete-help": "Select this option to discard this item. You will then be asked to confirm it.",
|
||||
"submission.workflow.generic.delete-help": "Seleccione esta opción para descartar este ítem. A contunuación se le pedirá confirmación.",
|
||||
|
||||
// "submission.workflow.generic.edit": "Edit",
|
||||
"submission.workflow.generic.edit": "Editar",
|
||||
@@ -7084,12 +7085,12 @@
|
||||
"subscriptions.modal.close": "Cerrar",
|
||||
|
||||
// "subscriptions.modal.delete-info": "To remove this subscription, please visit the \"Subscriptions\" page under your user profile",
|
||||
"subscriptions.modal.delete-info": "Para eliminar esta suscripción, por favor visite la página \”Suscripciones”\ en su perfil de usuario",
|
||||
"subscriptions.modal.delete-info": "Para eliminar esta suscripción, por favor visite la página \"Suscripciones\" en su perfil de usuario",
|
||||
|
||||
// "subscriptions.modal.new-subscription-form.type.content": "Content",
|
||||
"subscriptions.modal.new-subscription-form.type.content": "Contenido",
|
||||
|
||||
// "subscriptions.modal.new-subscription-form.frequency.D": "Diario",
|
||||
// "subscriptions.modal.new-subscription-form.frequency.D": "Daily",
|
||||
"subscriptions.modal.new-subscription-form.frequency.D": "Diario",
|
||||
|
||||
// "subscriptions.modal.new-subscription-form.frequency.W": "Weekly",
|
||||
@@ -7345,8 +7346,8 @@
|
||||
"workflow-item.selectrevieweraction.button.confirm": "Confirmar",
|
||||
|
||||
|
||||
// "workflow-item.scorereviewaction.notification.success.title": "Revisar evaluación",
|
||||
"workflow-item.scorereviewaction.notification.success.title": "Revisar evaluación",
|
||||
// "workflow-item.scorereviewaction.notification.success.title": "Rating review",
|
||||
"workflow-item.scorereviewaction.notification.success.title": "Revisión de evaluación",
|
||||
|
||||
// "workflow-item.scorereviewaction.notification.success.content": "The rating for this item workflow item has been successfully submitted",
|
||||
"workflow-item.scorereviewaction.notification.success.content": "La evaluación para este ítem ha sido enviada exitosamente",
|
||||
@@ -7382,7 +7383,7 @@
|
||||
"idle-modal.extend-session": "Prolongar la sesión",
|
||||
|
||||
// "researcher.profile.action.processing" : "Processing...",
|
||||
"researcher.profile.action.processing" : "Procesando...",
|
||||
"researcher.profile.action.processing": "Procesando...",
|
||||
|
||||
// "researcher.profile.associated": "Researcher profile associated",
|
||||
"researcher.profile.associated": "Perfil asociado de investigador",
|
||||
@@ -7415,10 +7416,10 @@
|
||||
"researcher.profile.view": "Ver",
|
||||
|
||||
// "researcher.profile.private.visibility" : "PRIVATE",
|
||||
"researcher.profile.private.visibility" : "PRIVADO",
|
||||
"researcher.profile.private.visibility": "PRIVADO",
|
||||
|
||||
// "researcher.profile.public.visibility" : "PUBLIC",
|
||||
"researcher.profile.public.visibility" : "PÚBLICO",
|
||||
"researcher.profile.public.visibility": "PÚBLICO",
|
||||
|
||||
// "researcher.profile.status": "Status:",
|
||||
"researcher.profile.status": "Estado:",
|
||||
@@ -7427,16 +7428,16 @@
|
||||
"researcherprofile.claim.not-authorized": "No está autorizado pare reclamar este ítem. Contacte con el administrador para mas detalles.",
|
||||
|
||||
// "researcherprofile.error.claim.body" : "An error occurred while claiming the profile, please try again later",
|
||||
"researcherprofile.error.claim.body" : "Hubo un error reclamando el perfil, por favor, inténtelo mas tarde",
|
||||
"researcherprofile.error.claim.body": "Hubo un error reclamando el perfil, por favor, inténtelo mas tarde",
|
||||
|
||||
// "researcherprofile.error.claim.title" : "Error",
|
||||
"researcherprofile.error.claim.title" : "Error",
|
||||
"researcherprofile.error.claim.title": "Error",
|
||||
|
||||
// "researcherprofile.success.claim.body" : "Profile claimed with success",
|
||||
"researcherprofile.success.claim.body" : "Perfil reclamado con éxito",
|
||||
"researcherprofile.success.claim.body": "Perfil reclamado con éxito",
|
||||
|
||||
// "researcherprofile.success.claim.title" : "Success",
|
||||
"researcherprofile.success.claim.title" : "Éxito",
|
||||
"researcherprofile.success.claim.title": "Éxito",
|
||||
|
||||
// "person.page.orcid.create": "Create an ORCID ID",
|
||||
"person.page.orcid.create": "Crear un ORCID ID",
|
||||
@@ -7445,7 +7446,7 @@
|
||||
"person.page.orcid.granted-authorizations": "Autorizaciones concedidas",
|
||||
|
||||
// "person.page.orcid.grant-authorizations" : "Grant authorizations",
|
||||
"person.page.orcid.grant-authorizations" : "Conceder autorizaciones",
|
||||
"person.page.orcid.grant-authorizations": "Conceder autorizaciones",
|
||||
|
||||
// "person.page.orcid.link": "Connect to ORCID ID",
|
||||
"person.page.orcid.link": "Conectar con ORCID ID",
|
||||
@@ -7493,43 +7494,43 @@
|
||||
"person.page.orcid.save.preference.changes": "Actualizar configuración",
|
||||
|
||||
// "person.page.orcid.sync-profile.affiliation" : "Affiliation",
|
||||
"person.page.orcid.sync-profile.affiliation" : "Afiliación",
|
||||
"person.page.orcid.sync-profile.affiliation": "Afiliación",
|
||||
|
||||
// "person.page.orcid.sync-profile.biographical" : "Biographical data",
|
||||
"person.page.orcid.sync-profile.biographical" : "Biografía",
|
||||
"person.page.orcid.sync-profile.biographical": "Biografía",
|
||||
|
||||
// "person.page.orcid.sync-profile.education" : "Education",
|
||||
"person.page.orcid.sync-profile.education" : "Grados",
|
||||
"person.page.orcid.sync-profile.education": "Grados",
|
||||
|
||||
// "person.page.orcid.sync-profile.identifiers" : "Identifiers",
|
||||
"person.page.orcid.sync-profile.identifiers" : "Identificadores",
|
||||
"person.page.orcid.sync-profile.identifiers": "Identificadores",
|
||||
|
||||
// "person.page.orcid.sync-fundings.all" : "All fundings",
|
||||
"person.page.orcid.sync-fundings.all" : "Todas las financiaciones",
|
||||
"person.page.orcid.sync-fundings.all": "Todas las financiaciones",
|
||||
|
||||
// "person.page.orcid.sync-fundings.mine" : "My fundings",
|
||||
"person.page.orcid.sync-fundings.mine" : "Mi financiación",
|
||||
"person.page.orcid.sync-fundings.mine": "Mi financiación",
|
||||
|
||||
// "person.page.orcid.sync-fundings.my_selected" : "Selected fundings",
|
||||
"person.page.orcid.sync-fundings.my_selected" : "Financiaciones seleccionadas",
|
||||
"person.page.orcid.sync-fundings.my_selected": "Financiaciones seleccionadas",
|
||||
|
||||
// "person.page.orcid.sync-fundings.disabled" : "Disabled",
|
||||
"person.page.orcid.sync-fundings.disabled" : "Deshabilitado",
|
||||
"person.page.orcid.sync-fundings.disabled": "Deshabilitado",
|
||||
|
||||
// "person.page.orcid.sync-publications.all" : "All publications",
|
||||
"person.page.orcid.sync-publications.all" : "Todas las publicaciones",
|
||||
"person.page.orcid.sync-publications.all": "Todas las publicaciones",
|
||||
|
||||
// "person.page.orcid.sync-publications.mine" : "My publications",
|
||||
"person.page.orcid.sync-publications.mine" : "Mis publicaciones",
|
||||
"person.page.orcid.sync-publications.mine": "Mis publicaciones",
|
||||
|
||||
// "person.page.orcid.sync-publications.my_selected" : "Selected publications",
|
||||
"person.page.orcid.sync-publications.my_selected" : "Publicaciones seleccionadas",
|
||||
"person.page.orcid.sync-publications.my_selected": "Publicaciones seleccionadas",
|
||||
|
||||
// "person.page.orcid.sync-publications.disabled" : "Disabled",
|
||||
"person.page.orcid.sync-publications.disabled" : "Deshabilitado",
|
||||
"person.page.orcid.sync-publications.disabled": "Deshabilitado",
|
||||
|
||||
// "person.page.orcid.sync-queue.discard" : "Discard the change and do not synchronize with the ORCID registry",
|
||||
"person.page.orcid.sync-queue.discard" : "Deshechar el cambio y no sincronizar con ORCID",
|
||||
"person.page.orcid.sync-queue.discard": "Deshechar el cambio y no sincronizar con ORCID",
|
||||
|
||||
// "person.page.orcid.sync-queue.discard.error": "The discarding of the ORCID queue record failed",
|
||||
"person.page.orcid.sync-queue.discard.error": "Falló el borrado del registro ORCID en la cola",
|
||||
@@ -7541,13 +7542,13 @@
|
||||
"person.page.orcid.sync-queue.empty-message": "La cola del registro de ORCID está vacía",
|
||||
|
||||
// "person.page.orcid.sync-queue.table.header.type" : "Type",
|
||||
"person.page.orcid.sync-queue.table.header.type" : "Tipo",
|
||||
"person.page.orcid.sync-queue.table.header.type": "Tipo",
|
||||
|
||||
// "person.page.orcid.sync-queue.table.header.description" : "Description",
|
||||
"person.page.orcid.sync-queue.table.header.description" : "Descripción",
|
||||
"person.page.orcid.sync-queue.table.header.description": "Descripción",
|
||||
|
||||
// "person.page.orcid.sync-queue.table.header.action" : "Action",
|
||||
"person.page.orcid.sync-queue.table.header.action" : "Acción",
|
||||
"person.page.orcid.sync-queue.table.header.action": "Acción",
|
||||
|
||||
// "person.page.orcid.sync-queue.description.affiliation": "Affiliations",
|
||||
"person.page.orcid.sync-queue.description.affiliation": "Affiliaciones",
|
||||
@@ -7613,7 +7614,7 @@
|
||||
"person.page.orcid.sync-queue.tooltip.researcher_urls": "URL del investigador",
|
||||
|
||||
// "person.page.orcid.sync-queue.send" : "Synchronize with ORCID registry",
|
||||
"person.page.orcid.sync-queue.send" : "Sincronizar con el registro ORCID",
|
||||
"person.page.orcid.sync-queue.send": "Sincronizar con el registro ORCID",
|
||||
|
||||
// "person.page.orcid.sync-queue.send.unauthorized-error.title": "The submission to ORCID failed for missing authorizations.",
|
||||
"person.page.orcid.sync-queue.send.unauthorized-error.title": "Falló el envío a ORCID debdo a autorización insuficiente.",
|
||||
@@ -7667,7 +7668,7 @@
|
||||
"person.page.orcid.sync-queue.send.validation-error.organization.name-required": "Se requiere el nombre de la organización",
|
||||
|
||||
// "person.page.orcid.sync-queue.send.validation-error.publication.date-invalid" : "The publication date must be one year after 1900",
|
||||
"person.page.orcid.sync-queue.send.validation-error.publication.date-invalid" : "la fecha de la publicación debe ser posterior a 1900",
|
||||
"person.page.orcid.sync-queue.send.validation-error.publication.date-invalid": "la fecha de la publicación debe ser posterior a 1900",
|
||||
|
||||
// "person.page.orcid.sync-queue.send.validation-error.organization.address-required": "The organization to be sent requires an address",
|
||||
"person.page.orcid.sync-queue.send.validation-error.organization.address-required": "Se requiere la dirección de la organización",
|
||||
|
@@ -156,8 +156,9 @@
|
||||
|
||||
// "admin.registries.bitstream-formats.table.name": "Name",
|
||||
"admin.registries.bitstream-formats.table.name": "Nimi",
|
||||
// TODO New key - Add a translation
|
||||
"admin.registries.bitstream-formats.table.id" : "ID",
|
||||
|
||||
// "admin.registries.bitstream-formats.table.id" : "ID",
|
||||
"admin.registries.bitstream-formats.table.id": "ID",
|
||||
|
||||
// "admin.registries.bitstream-formats.table.return": "Return",
|
||||
"admin.registries.bitstream-formats.table.return": "Palaa",
|
||||
@@ -207,7 +208,7 @@
|
||||
"admin.registries.metadata.schemas.table.delete": "Poista valittu",
|
||||
|
||||
// "admin.registries.metadata.schemas.table.id": "ID",
|
||||
"admin.registries.metadata.schemas.table.id": "ID-tunnus",
|
||||
"admin.registries.metadata.schemas.table.id": "ID",
|
||||
|
||||
// "admin.registries.metadata.schemas.table.name": "Name",
|
||||
"admin.registries.metadata.schemas.table.name": "Nimi",
|
||||
@@ -237,8 +238,9 @@
|
||||
|
||||
// "admin.registries.schema.fields.table.field": "Field",
|
||||
"admin.registries.schema.fields.table.field": "Kenttä",
|
||||
// TODO New key - Add a translation
|
||||
"admin.registries.schema.fields.table.id" : "ID",
|
||||
|
||||
// "admin.registries.schema.fields.table.id": "ID",
|
||||
"admin.registries.schema.fields.table.id": "ID",
|
||||
|
||||
// "admin.registries.schema.fields.table.scopenote": "Scope Note",
|
||||
"admin.registries.schema.fields.table.scopenote": "Soveltamisala",
|
||||
@@ -408,7 +410,7 @@
|
||||
"admin.access-control.epeople.form.groupsEPersonIsMemberOf": "Jäsenenä näissä ryhmissä:",
|
||||
|
||||
// "admin.access-control.epeople.form.table.id": "ID",
|
||||
"admin.access-control.epeople.form.table.id": "ID-tunnus",
|
||||
"admin.access-control.epeople.form.table.id": "ID",
|
||||
|
||||
// "admin.access-control.epeople.form.table.name": "Name",
|
||||
"admin.access-control.epeople.form.table.name": "Nimi",
|
||||
@@ -1560,7 +1562,7 @@
|
||||
|
||||
|
||||
// "curation-task.task.checklinks.label": "Check Links in Metadata",
|
||||
"curation-task.task.checklinks.label": "Tarkista metadatan linkit",
|
||||
"curation-task.task.checklinks.label": "Tarkista metadatan linkit",
|
||||
|
||||
// "curation-task.task.noop.label": "NOOP",
|
||||
"curation-task.task.noop.label": "Ei toimintaa",
|
||||
@@ -1754,7 +1756,7 @@
|
||||
"footer.link.privacy-policy": "Yksilönsuoja",
|
||||
|
||||
// "footer.link.end-user-agreement":"End User Agreement",
|
||||
"footer.link.end-user-agreement":"Käyttöehdot",
|
||||
"footer.link.end-user-agreement": "Käyttöehdot",
|
||||
|
||||
|
||||
|
||||
@@ -2004,7 +2006,7 @@
|
||||
// "item.bitstreams.upload.bundle.new": "Create bundle",
|
||||
"item.bitstreams.upload.bundle.new": "Luo nippu",
|
||||
|
||||
// "item.bitstreams.upload.bundles.empty": "This item doesn\'t contain any bundles to upload a bitstream to.",
|
||||
// "item.bitstreams.upload.bundles.empty": "This item doesn't contain any bundles to upload a bitstream to.",
|
||||
"item.bitstreams.upload.bundles.empty": "Tällä tietueella ei ole nippua, johon tiedoston voisi ladata.",
|
||||
|
||||
// "item.bitstreams.upload.cancel": "Cancel",
|
||||
@@ -2495,7 +2497,7 @@
|
||||
"item.edit.tabs.status.buttons.reinstate.label": "Palauta tietue arkistoon",
|
||||
|
||||
// "item.edit.tabs.status.buttons.withdraw.button": "Withdraw this item",
|
||||
"item.edit.tabs.status.buttons.withdraw.button": "poista tämä kohde",
|
||||
"item.edit.tabs.status.buttons.withdraw.button": "Poista tämä kohde",
|
||||
|
||||
// "item.edit.tabs.status.buttons.withdraw.label": "Withdraw item from the repository",
|
||||
"item.edit.tabs.status.buttons.withdraw.label": "Poista tietue käytöstä",
|
||||
@@ -2510,7 +2512,7 @@
|
||||
"item.edit.tabs.status.labels.handle": "Handle-tunnus",
|
||||
|
||||
// "item.edit.tabs.status.labels.id": "Item Internal ID",
|
||||
"item.edit.tabs.status.labels.id": "Tietueen sisäinen ID-tunnus",
|
||||
"item.edit.tabs.status.labels.id": "Tietueen sisäinen ID",
|
||||
|
||||
// "item.edit.tabs.status.labels.itemPage": "Item Page",
|
||||
"item.edit.tabs.status.labels.itemPage": "Tietueen tiedot",
|
||||
@@ -2564,9 +2566,6 @@
|
||||
// "item.page.description": "Description",
|
||||
"item.page.description": "Kuvaus",
|
||||
|
||||
// "item.page.edit": "Edit this item",
|
||||
"item.page.edit": "Muokkaa tietuetta",
|
||||
|
||||
// "item.page.journal-issn": "Journal ISSN",
|
||||
"item.page.journal-issn": "Kausijulkaisun ISSN",
|
||||
|
||||
@@ -2669,10 +2668,10 @@
|
||||
"item.page.bitstreams.collapse": "Sulje",
|
||||
|
||||
// "item.page.filesection.original.bundle" : "Original bundle",
|
||||
"item.page.filesection.original.bundle" : "Alkuperäinen nippu",
|
||||
"item.page.filesection.original.bundle": "Alkuperäinen nippu",
|
||||
|
||||
// "item.page.filesection.license.bundle" : "License bundle",
|
||||
"item.page.filesection.license.bundle" : "Lisenssinippu",
|
||||
"item.page.filesection.license.bundle": "Lisenssinippu",
|
||||
|
||||
// "item.preview.dc.identifier.uri": "Identifier:",
|
||||
"item.preview.dc.identifier.uri": "Tunnus:",
|
||||
@@ -3284,7 +3283,7 @@
|
||||
"mydspace.upload.upload-failed": "Virhe uutta työtilaa luotaessa. Tarkista ladattava sisältö ennen kuin yrität uudelleen.",
|
||||
|
||||
// "mydspace.upload.upload-failed-manyentries": "Unprocessable file. Detected too many entries but allowed only one for file.",
|
||||
"mydspace.upload.upload-failed-manyentries": "Tiedostoa ei voida käsitellä. Vain yksi kohde on sallittu, mutta niitä on useita.",
|
||||
"mydspace.upload.upload-failed-manyentries": "Tiedostoa ei voida käsitellä. Vain yksi kohde on sallittu, mutta niitä on useita.",
|
||||
|
||||
// "mydspace.upload.upload-failed-moreonefile": "Unprocessable request. Only one file is allowed.",
|
||||
"mydspace.upload.upload-failed-moreonefile": "Pyyntöä ei voida käsitellä. Vain yksi tiedosto sallittu.",
|
||||
@@ -3351,7 +3350,7 @@
|
||||
"orgunit.page.edit": "Muokkaa tietuetta",
|
||||
|
||||
// "orgunit.page.id": "ID",
|
||||
"orgunit.page.id": "ID-tunnus",
|
||||
"orgunit.page.id": "ID",
|
||||
|
||||
// "orgunit.page.titleprefix": "Organizational Unit: ",
|
||||
"orgunit.page.titleprefix": "Organisaatioyksikkö: ",
|
||||
@@ -3403,7 +3402,7 @@
|
||||
"person.page.orcid": "ORCID-tunniste",
|
||||
|
||||
// "person.page.staffid": "Staff ID",
|
||||
"person.page.staffid": "Henkilökunnan ID-tunnus",
|
||||
"person.page.staffid": "Henkilökunnan ID",
|
||||
|
||||
// "person.page.titleprefix": "Person: ",
|
||||
"person.page.titleprefix": "Käyttäjä: ",
|
||||
@@ -3476,16 +3475,16 @@
|
||||
|
||||
|
||||
// "process.detail.arguments" : "Arguments",
|
||||
"process.detail.arguments" : "Muuttujat",
|
||||
"process.detail.arguments": "Muuttujat",
|
||||
|
||||
// "process.detail.arguments.empty" : "This process doesn't contain any arguments",
|
||||
"process.detail.arguments.empty" : "Prosessiin ei liity muuttujia",
|
||||
"process.detail.arguments.empty": "Prosessiin ei liity muuttujia",
|
||||
|
||||
// "process.detail.back" : "Back",
|
||||
"process.detail.back" : "Paluu",
|
||||
"process.detail.back": "Paluu",
|
||||
|
||||
// "process.detail.output" : "Process Output",
|
||||
"process.detail.output" : "Prosessin tulos",
|
||||
"process.detail.output": "Prosessin tulos",
|
||||
|
||||
// "process.detail.logs.button": "Retrieve process output",
|
||||
"process.detail.logs.button": "Nouda prosessin tulos",
|
||||
@@ -3497,48 +3496,48 @@
|
||||
"process.detail.logs.none": "Prosessilla ei tulosta",
|
||||
|
||||
// "process.detail.output-files" : "Output Files",
|
||||
"process.detail.output-files" : "Tulostiedostot",
|
||||
"process.detail.output-files": "Tulostiedostot",
|
||||
|
||||
// "process.detail.output-files.empty" : "This process doesn't contain any output files",
|
||||
"process.detail.output-files.empty" : "Prosessilla ei ole tulostiedostoja",
|
||||
"process.detail.output-files.empty": "Prosessilla ei ole tulostiedostoja",
|
||||
|
||||
// "process.detail.script" : "Script",
|
||||
"process.detail.script" : "Skripti",
|
||||
"process.detail.script": "Skripti",
|
||||
|
||||
// "process.detail.title" : "Process: {{ id }} - {{ name }}",
|
||||
"process.detail.title" : "Prosessi: {{ id }} - {{ name }}",
|
||||
"process.detail.title": "Prosessi: {{ id }} - {{ name }}",
|
||||
|
||||
// "process.detail.start-time" : "Start time",
|
||||
"process.detail.start-time" : "Aloitusaika",
|
||||
"process.detail.start-time": "Aloitusaika",
|
||||
|
||||
// "process.detail.end-time" : "Finish time",
|
||||
"process.detail.end-time" : "Lopetusaika",
|
||||
"process.detail.end-time": "Lopetusaika",
|
||||
|
||||
// "process.detail.status" : "Status",
|
||||
"process.detail.status" : "Tila",
|
||||
"process.detail.status": "Tila",
|
||||
|
||||
// "process.detail.create" : "Create similar process",
|
||||
"process.detail.create" : "Luo vastaava prosessi",
|
||||
"process.detail.create": "Luo vastaava prosessi",
|
||||
|
||||
|
||||
|
||||
// "process.overview.table.finish" : "Finish time",
|
||||
"process.overview.table.finish" : "Lopetusaika",
|
||||
"process.overview.table.finish": "Lopetusaika",
|
||||
|
||||
// "process.overview.table.id" : "Process ID",
|
||||
"process.overview.table.id" : "Prosessin ID",
|
||||
"process.overview.table.id": "Prosessin ID",
|
||||
|
||||
// "process.overview.table.name" : "Name",
|
||||
"process.overview.table.name" : "Nimi",
|
||||
"process.overview.table.name": "Nimi",
|
||||
|
||||
// "process.overview.table.start" : "Start time",
|
||||
"process.overview.table.start" : "Aloitusaika",
|
||||
"process.overview.table.start": "Aloitusaika",
|
||||
|
||||
// "process.overview.table.status" : "Status",
|
||||
"process.overview.table.status" : "Tila",
|
||||
"process.overview.table.status": "Tila",
|
||||
|
||||
// "process.overview.table.user" : "User",
|
||||
"process.overview.table.user" : "Käyttäjä",
|
||||
"process.overview.table.user": "Käyttäjä",
|
||||
|
||||
// "process.overview.title": "Processes Overview",
|
||||
"process.overview.title": "Prosessien yleiskatsaukset",
|
||||
@@ -3655,7 +3654,7 @@
|
||||
"project.page.funder": "Rahoittajat",
|
||||
|
||||
// "project.page.id": "ID",
|
||||
"project.page.id": "ID-tunnus",
|
||||
"project.page.id": "ID",
|
||||
|
||||
// "project.page.keyword": "Keywords",
|
||||
"project.page.keyword": "Asiasanat",
|
||||
@@ -3939,7 +3938,7 @@
|
||||
"resource-policies.form.eperson-group-list.table.headers.action": "Toimenpide",
|
||||
|
||||
// "resource-policies.form.eperson-group-list.table.headers.id": "ID",
|
||||
"resource-policies.form.eperson-group-list.table.headers.id": "ID-tunnus",
|
||||
"resource-policies.form.eperson-group-list.table.headers.id": "ID",
|
||||
|
||||
// "resource-policies.form.eperson-group-list.table.headers.name": "Name",
|
||||
"resource-policies.form.eperson-group-list.table.headers.name": "Nimi",
|
||||
@@ -3987,7 +3986,7 @@
|
||||
"resource-policies.table.headers.group": "Ryhmä",
|
||||
|
||||
// "resource-policies.table.headers.id": "ID",
|
||||
"resource-policies.table.headers.id": "ID-tunnus",
|
||||
"resource-policies.table.headers.id": "ID",
|
||||
|
||||
// "resource-policies.table.headers.name": "Name",
|
||||
"resource-policies.table.headers.name": "Nimi",
|
||||
@@ -4766,7 +4765,7 @@
|
||||
// "submission.sections.describe.relationship-lookup.selection-tab.title.arxiv": "Search Results",
|
||||
"submission.sections.describe.relationship-lookup.selection-tab.title.arxiv": "Hakutulokset",
|
||||
|
||||
// "submission.sections.describe.relationship-lookup.name-variant.notification.content": "Would you like to save \"{{ value }}\" as a name variant for this person so you and others can reuse it for future submissions? If you don\'t you can still use it for this submission.",
|
||||
// "submission.sections.describe.relationship-lookup.name-variant.notification.content": "Would you like to save \"{{ value }}\" as a name variant for this person so you and others can reuse it for future submissions? If you don't you can still use it for this submission.",
|
||||
"submission.sections.describe.relationship-lookup.name-variant.notification.content": "Haluatko tallentaa nimen \"{{ value }}\" käyttäjän vaihtoehtoiseksi nimeksi, jota muutkin voivat käyttää uudelleen myös tulevissa tallennuksissa? Ellet tallenna nimeä, voit silti käyttää sitä tässä tallennuksessa.",
|
||||
|
||||
// "submission.sections.describe.relationship-lookup.name-variant.notification.confirm": "Save a new name variant",
|
||||
@@ -4914,8 +4913,8 @@
|
||||
// "submission.sections.upload.header.policy.default.withlist": "Please note that uploaded files in the {{collectionName}} collection will be accessible, in addition to what is explicitly decided for the single file, with the following group(s):",
|
||||
"submission.sections.upload.header.policy.default.withlist": "Yksittäisten tiedostojen pääsyrajoitusten lisäksi {{collectionName}}-kokoelmaan ladatut tiedostot ovat seuraavien ryhmien saatavilla:",
|
||||
|
||||
// "submission.sections.upload.info": "Here you will find all the files currently in the item. You can update the file metadata and access conditions or <strong>upload additional files by dragging & dropping them anywhere on the page.</strong>",
|
||||
"submission.sections.upload.info": "Tietueen kaikki tiedostot on lueteltu tässä. Voit päivittää tiedoston metadataa ja pääsyehtoja tai <strong>ladata lisää tiedostoja raahaamalla ne mihin hyvänsä sivun kohtaan</strong>",
|
||||
// "submission.sections.upload.info": "Here you will find all the files currently in the item. You can update the file metadata and access conditions or <strong>upload additional files just dragging & dropping them everywhere in the page</strong>",
|
||||
"submission.sections.upload.info": "Tietueen kaikki tiedostot on lueteltu tässä. Voit päivittää tiedoston metadataa ja pääsyehtoja tai <strong>ladata lisää tiedostoja raahaamalla ne mihin hyvänsä sivun kohtaan.</strong>",
|
||||
|
||||
// "submission.sections.upload.no-entry": "No",
|
||||
"submission.sections.upload.no-entry": "Ei",
|
||||
@@ -5066,8 +5065,7 @@
|
||||
"uploader.or": " tai",
|
||||
|
||||
// "uploader.processing": "Processing uploaded file(s)... (it's now safe to close this page)",
|
||||
// TODO Source message changed - Revise the translation
|
||||
"uploader.processing": "Käsitellään",
|
||||
"uploader.processing": "Käsitellään ladattuja tiedostoja... (voit sulkea tämän sivun)",
|
||||
|
||||
// "uploader.queue-length": "Queue length",
|
||||
"uploader.queue-length": "Jonon pituus",
|
||||
@@ -5135,7 +5133,7 @@
|
||||
"workflow-item.send-back.button.cancel": "Peruuta",
|
||||
|
||||
// "workflow-item.send-back.button.confirm": "Send back"
|
||||
"workflow-item.send-back.button.confirm": "Lähetä takaisin"
|
||||
"workflow-item.send-back.button.confirm": "Lähetä takaisin",
|
||||
|
||||
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user