Merge pull request #972 from atmire/Private-withdrawn-badges-for-items-everywhere

Private/Withdrawn badges for items everywhere
This commit is contained in:
Tim Donohue
2021-01-04 11:03:14 -06:00
committed by GitHub
22 changed files with 278 additions and 128 deletions

View File

@@ -1,12 +1,7 @@
<ng-template dsListableObject> <ng-template dsListableObject>
</ng-template> </ng-template>
<div #badges class="position-absolute ml-1"> <div #badges>
<div *ngIf="dso && !dso.isDiscoverable" class="private-badge"> <ng-content></ng-content>
<span class="badge badge-danger">{{ "admin.search.item.private" | translate }}</span>
</div>
<div *ngIf="dso && dso.isWithdrawn" class="withdrawn-badge">
<span class="badge badge-warning">{{ "admin.search.item.withdrawn" | translate }}</span>
</div>
</div> </div>
<ul #buttons class="list-group list-group-flush"> <ul #buttons class="list-group list-group-flush">
<li class="list-group-item"> <li class="list-group-item">

View File

@@ -1,5 +1,4 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core'; import { NO_ERRORS_SCHEMA } from '@angular/core';
import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
@@ -13,7 +12,6 @@ import { SharedModule } from '../../../../../shared/shared.module';
import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service'; import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service';
import { CollectionElementLinkType } from '../../../../../shared/object-collection/collection-element-link.type'; import { CollectionElementLinkType } from '../../../../../shared/object-collection/collection-element-link.type';
import { ViewMode } from '../../../../../core/shared/view-mode.model'; import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { By } from '@angular/platform-browser';
import { RouterTestingModule } from '@angular/router/testing'; import { RouterTestingModule } from '@angular/router/testing';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model'; import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { ItemAdminSearchResultGridElementComponent } from './item-admin-search-result-grid-element.component'; import { ItemAdminSearchResultGridElementComponent } from './item-admin-search-result-grid-element.component';
@@ -71,51 +69,4 @@ describe('ItemAdminSearchResultGridElementComponent', () => {
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
describe('when the item is not withdrawn', () => {
beforeEach(() => {
component.dso.isWithdrawn = false;
fixture.detectChanges();
});
it('should not show the withdrawn badge', () => {
const badge = fixture.debugElement.query(By.css('div.withdrawn-badge'));
expect(badge).toBeNull();
});
});
describe('when the item is withdrawn', () => {
beforeEach(() => {
component.dso.isWithdrawn = true;
fixture.detectChanges();
});
it('should show the withdrawn badge', () => {
const badge = fixture.debugElement.query(By.css('div.withdrawn-badge'));
expect(badge).not.toBeNull();
});
});
describe('when the item is not private', () => {
beforeEach(() => {
component.dso.isDiscoverable = true;
fixture.detectChanges();
});
it('should not show the private badge', () => {
const badge = fixture.debugElement.query(By.css('div.private-badge'));
expect(badge).toBeNull();
});
});
describe('when the item is private', () => {
beforeEach(() => {
component.dso.isDiscoverable = false;
fixture.detectChanges();
});
it('should show the private badge', () => {
const badge = fixture.debugElement.query(By.css('div.private-badge'));
expect(badge).not.toBeNull();
});
})
}); });

View File

@@ -1,12 +1,7 @@
<div *ngIf="dso && !dso.isDiscoverable" class="private-badge">
<span class="badge badge-danger">{{ "admin.search.item.private" | translate }}</span>
</div>
<div *ngIf="dso && dso.isWithdrawn" class="withdrawn-badge">
<span class="badge badge-warning">{{ "admin.search.item.withdrawn" | translate }}</span>
</div>
<ds-listable-object-component-loader [object]="object" <ds-listable-object-component-loader [object]="object"
[viewMode]="viewModes.ListElement" [viewMode]="viewModes.ListElement"
[index]="index" [index]="index"
[linkType]="linkType" [linkType]="linkType"
[listID]="listID"></ds-listable-object-component-loader> [listID]="listID"
[hideBadges]="true"></ds-listable-object-component-loader>
<ds-item-admin-search-result-actions-element [item]="dso" [small]="false"></ds-item-admin-search-result-actions-element> <ds-item-admin-search-result-actions-element [item]="dso" [small]="false"></ds-item-admin-search-result-actions-element>

View File

@@ -1,11 +1,9 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core'; import { NO_ERRORS_SCHEMA } from '@angular/core';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service'; import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service';
import { CollectionElementLinkType } from '../../../../../shared/object-collection/collection-element-link.type'; import { CollectionElementLinkType } from '../../../../../shared/object-collection/collection-element-link.type';
import { ViewMode } from '../../../../../core/shared/view-mode.model'; import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { By } from '@angular/platform-browser';
import { RouterTestingModule } from '@angular/router/testing'; import { RouterTestingModule } from '@angular/router/testing';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model'; import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { ItemAdminSearchResultListElementComponent } from './item-admin-search-result-list-element.component'; import { ItemAdminSearchResultListElementComponent } from './item-admin-search-result-list-element.component';
@@ -51,51 +49,4 @@ describe('ItemAdminSearchResultListElementComponent', () => {
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
describe('when the item is not withdrawn', () => {
beforeEach(() => {
component.dso.isWithdrawn = false;
fixture.detectChanges();
});
it('should not show the withdrawn badge', () => {
const badge = fixture.debugElement.query(By.css('div.withdrawn-badge'));
expect(badge).toBeNull();
});
});
describe('when the item is withdrawn', () => {
beforeEach(() => {
component.dso.isWithdrawn = true;
fixture.detectChanges();
});
it('should show the withdrawn badge', () => {
const badge = fixture.debugElement.query(By.css('div.withdrawn-badge'));
expect(badge).not.toBeNull();
});
});
describe('when the item is not private', () => {
beforeEach(() => {
component.dso.isDiscoverable = true;
fixture.detectChanges();
});
it('should not show the private badge', () => {
const badge = fixture.debugElement.query(By.css('div.private-badge'));
expect(badge).toBeNull();
});
});
describe('when the item is private', () => {
beforeEach(() => {
component.dso.isDiscoverable = false;
fixture.detectChanges();
});
it('should show the private badge', () => {
const badge = fixture.debugElement.query(By.css('div.private-badge'));
expect(badge).not.toBeNull();
});
})
}); });

View File

@@ -1,6 +1,7 @@
<div class="container" *ngVar="(itemRD$ | async) as itemRD"> <div class="container" *ngVar="(itemRD$ | async) as itemRD">
<div class="item-page" *ngIf="itemRD?.hasSucceeded" @fadeInOut> <div class="item-page" *ngIf="itemRD?.hasSucceeded" @fadeInOut>
<div *ngIf="itemRD?.payload as item"> <div *ngIf="itemRD?.payload as item">
<ds-item-alerts [item]="item"></ds-item-alerts>
<ds-item-versions-notice [item]="item"></ds-item-versions-notice> <ds-item-versions-notice [item]="item"></ds-item-versions-notice>
<ds-view-tracker [object]="item"></ds-view-tracker> <ds-view-tracker [object]="item"></ds-view-tracker>
<div class="d-flex flex-row"> <div class="d-flex flex-row">

View File

@@ -1,6 +1,7 @@
<div class="container" *ngVar="(itemRD$ | async) as itemRD"> <div class="container" *ngVar="(itemRD$ | async) as itemRD">
<div class="item-page" *ngIf="itemRD?.hasSucceeded" @fadeInOut> <div class="item-page" *ngIf="itemRD?.hasSucceeded" @fadeInOut>
<div *ngIf="itemRD?.payload as item"> <div *ngIf="itemRD?.payload as item">
<ds-item-alerts [item]="item"></ds-item-alerts>
<ds-item-versions-notice [item]="item"></ds-item-versions-notice> <ds-item-versions-notice [item]="item"></ds-item-versions-notice>
<ds-view-tracker [object]="item"></ds-view-tracker> <ds-view-tracker [object]="item"></ds-view-tracker>
<ds-listable-object-component-loader [object]="item" [viewMode]="viewMode"></ds-listable-object-component-loader> <ds-listable-object-component-loader [object]="item" [viewMode]="viewMode"></ds-listable-object-component-loader>

View File

@@ -1,6 +1,8 @@
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'"> <div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
<ds-truncatable [id]="dso.id"> <ds-truncatable [id]="dso.id">
<ng-content></ng-content> <div class="position-absolute ml-1">
<ng-content></ng-content>
</div>
<a *ngIf="linkType != linkTypes.None" <a *ngIf="linkType != linkTypes.None"
[target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"

View File

@@ -1,6 +1,8 @@
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'"> <div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
<ds-truncatable [id]="dso.id"> <ds-truncatable [id]="dso.id">
<ng-content></ng-content> <div class="position-absolute ml-1">
<ng-content></ng-content>
</div>
<a *ngIf="linkType != linkTypes.None" <a *ngIf="linkType != linkTypes.None"
[target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"

View File

@@ -1,6 +1,8 @@
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'"> <div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
<ds-truncatable [id]="dso.id"> <ds-truncatable [id]="dso.id">
<ng-content></ng-content> <div class="position-absolute ml-1">
<ng-content></ng-content>
</div>
<a *ngIf="linkType != linkTypes.None" <a *ngIf="linkType != linkTypes.None"
[target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"

View File

@@ -1,6 +1,8 @@
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'"> <div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
<ds-truncatable [id]="dso.id"> <ds-truncatable [id]="dso.id">
<ng-content></ng-content> <div class="position-absolute ml-1">
<ng-content></ng-content>
</div>
<a *ngIf="linkType != linkTypes.None" <a *ngIf="linkType != linkTypes.None"
[target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"

View File

@@ -1,6 +1,8 @@
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'"> <div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
<ds-truncatable [id]="dso.id"> <ds-truncatable [id]="dso.id">
<ng-content></ng-content> <div class="position-absolute ml-1">
<ng-content></ng-content>
</div>
<a *ngIf="linkType != linkTypes.None" <a *ngIf="linkType != linkTypes.None"
[target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"

View File

@@ -1,6 +1,8 @@
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'"> <div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
<ds-truncatable [id]="dso.id"> <ds-truncatable [id]="dso.id">
<ng-content></ng-content> <div class="position-absolute ml-1">
<ng-content></ng-content>
</div>
<a *ngIf="linkType != linkTypes.None" <a *ngIf="linkType != linkTypes.None"
[target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"

View File

@@ -0,0 +1,8 @@
<div>
<div *ngIf="item && !item.isDiscoverable" class="private-warning">
<ds-alert [type]="AlertTypeEnum.Warning" [content]="'item.alerts.private' | translate"></ds-alert>
</div>
<div *ngIf="item && item.isWithdrawn" class="withdrawn-warning">
<ds-alert [type]="AlertTypeEnum.Warning" [content]="'item.alerts.withdrawn' | translate"></ds-alert>
</div>
</div>

View File

@@ -0,0 +1,87 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ItemAlertsComponent } from './item-alerts.component';
import { TranslateModule } from '@ngx-translate/core';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { Item } from '../../../core/shared/item.model';
import { By } from '@angular/platform-browser';
describe('ItemAlertsComponent', () => {
let component: ItemAlertsComponent;
let fixture: ComponentFixture<ItemAlertsComponent>;
let item: Item;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ItemAlertsComponent],
imports: [TranslateModule.forRoot()],
schemas: [NO_ERRORS_SCHEMA]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ItemAlertsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
describe('when the item is discoverable', () => {
beforeEach(() => {
item = Object.assign(new Item(), {
isDiscoverable: true
});
component.item = item;
fixture.detectChanges();
});
it('should not display the private alert', () => {
const privateWarning = fixture.debugElement.query(By.css('.private-warning'));
expect(privateWarning).toBeNull();
});
});
describe('when the item is not discoverable', () => {
beforeEach(() => {
item = Object.assign(new Item(), {
isDiscoverable: false
});
component.item = item;
fixture.detectChanges();
});
it('should display the private alert', () => {
const privateWarning = fixture.debugElement.query(By.css('.private-warning'));
expect(privateWarning).not.toBeNull();
});
});
describe('when the item is withdrawn', () => {
beforeEach(() => {
item = Object.assign(new Item(), {
isWithdrawn: true
});
component.item = item;
fixture.detectChanges();
});
it('should display the withdrawn alert', () => {
const privateWarning = fixture.debugElement.query(By.css('.withdrawn-warning'));
expect(privateWarning).not.toBeNull();
});
});
describe('when the item is not withdrawn', () => {
beforeEach(() => {
item = Object.assign(new Item(), {
isWithdrawn: false
});
component.item = item;
fixture.detectChanges();
});
it('should not display the withdrawn alert', () => {
const privateWarning = fixture.debugElement.query(By.css('.withdrawn-warning'));
expect(privateWarning).toBeNull();
});
});
});

View File

@@ -0,0 +1,24 @@
import { Component, Input } from '@angular/core';
import { Item } from '../../../core/shared/item.model';
import { AlertType } from '../../alert/aletr-type';
@Component({
selector: 'ds-item-alerts',
templateUrl: './item-alerts.component.html',
styleUrls: ['./item-alerts.component.scss']
})
/**
* Component displaying alerts for an item
*/
export class ItemAlertsComponent {
/**
* The Item to display alerts for
*/
@Input() item: Item;
/**
* The AlertType enumeration
* @type {AlertType}
*/
public AlertTypeEnum = AlertType;
}

View File

@@ -1 +1,9 @@
<div [ngClass]="{'d-none' : hideBadges}" #badges>
<div *ngIf="privateBadge" class="private-badge">
<span class="badge badge-danger">{{ "item.badge.private" | translate }}</span>
</div>
<div *ngIf="withdrawnBadge" class="withdrawn-badge">
<span class="badge badge-warning">{{ "item.badge.withdrawn" | translate }}</span>
</div>
</div>
<ng-template dsListableObject></ng-template> <ng-template dsListableObject></ng-template>

View File

@@ -9,6 +9,9 @@ import * as listableObjectDecorators from './listable-object.decorator';
import { ItemListElementComponent } from '../../../object-list/item-list-element/item-types/item/item-list-element.component'; import { ItemListElementComponent } from '../../../object-list/item-list-element/item-types/item/item-list-element.component';
import { ListableObjectDirective } from './listable-object.directive'; import { ListableObjectDirective } from './listable-object.directive';
import { spyOnExported } from '../../../testing/utils.test'; import { spyOnExported } from '../../../testing/utils.test';
import { TranslateModule } from '@ngx-translate/core';
import { By } from '@angular/platform-browser';
import { Item } from '../../../../core/shared/item.model';
const testType = 'TestType'; const testType = 'TestType';
const testContext = Context.Search; const testContext = Context.Search;
@@ -26,7 +29,7 @@ describe('ListableObjectComponentLoaderComponent', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [], imports: [TranslateModule.forRoot()],
declarations: [ListableObjectComponentLoaderComponent, ItemListElementComponent, ListableObjectDirective], declarations: [ListableObjectComponentLoaderComponent, ItemListElementComponent, ListableObjectDirective],
schemas: [NO_ERRORS_SCHEMA], schemas: [NO_ERRORS_SCHEMA],
providers: [ComponentFactoryResolver] providers: [ComponentFactoryResolver]
@@ -55,4 +58,63 @@ describe('ListableObjectComponentLoaderComponent', () => {
expect(listableObjectDecorators.getListableObjectComponent).toHaveBeenCalledWith([testType], testViewMode, testContext); expect(listableObjectDecorators.getListableObjectComponent).toHaveBeenCalledWith([testType], testViewMode, testContext);
}) })
}); });
describe('when the object is an item and viewMode is a list', () => {
beforeEach(() => {
comp.object = Object.assign(new Item());
comp.viewMode = ViewMode.ListElement;
});
describe('when the item is not withdrawn', () => {
beforeEach(() => {
(comp.object as any).isWithdrawn = false;
comp.initBadges();
fixture.detectChanges();
});
it('should not show the withdrawn badge', () => {
const badge = fixture.debugElement.query(By.css('div.withdrawn-badge'));
expect(badge).toBeNull();
});
});
describe('when the item is withdrawn', () => {
beforeEach(() => {
(comp.object as any).isWithdrawn = true;
comp.initBadges();
fixture.detectChanges();
});
it('should show the withdrawn badge', () => {
const badge = fixture.debugElement.query(By.css('div.withdrawn-badge'));
expect(badge).not.toBeNull();
});
});
describe('when the item is not private', () => {
beforeEach(() => {
(comp.object as any).isDiscoverable = true;
comp.initBadges();
fixture.detectChanges();
});
it('should not show the private badge', () => {
const badge = fixture.debugElement.query(By.css('div.private-badge'));
expect(badge).toBeNull();
});
});
describe('when the item is private', () => {
beforeEach(() => {
(comp.object as any).isDiscoverable = false;
comp.initBadges();
fixture.detectChanges();
});
it('should show the private badge', () => {
const badge = fixture.debugElement.query(By.css('div.private-badge'));
expect(badge).not.toBeNull();
});
});
});
}); });

View File

@@ -1,4 +1,4 @@
import { Component, ComponentFactoryResolver, Input, OnInit, ViewChild } from '@angular/core'; import { Component, ComponentFactoryResolver, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
import { ListableObject } from '../listable-object.model'; import { ListableObject } from '../listable-object.model';
import { ViewMode } from '../../../../core/shared/view-mode.model'; import { ViewMode } from '../../../../core/shared/view-mode.model';
import { Context } from '../../../../core/shared/context.model'; import { Context } from '../../../../core/shared/context.model';
@@ -6,6 +6,7 @@ import { getListableObjectComponent } from './listable-object.decorator';
import { GenericConstructor } from '../../../../core/shared/generic-constructor'; import { GenericConstructor } from '../../../../core/shared/generic-constructor';
import { ListableObjectDirective } from './listable-object.directive'; import { ListableObjectDirective } from './listable-object.directive';
import { CollectionElementLinkType } from '../../collection-element-link.type'; import { CollectionElementLinkType } from '../../collection-element-link.type';
import { hasValue } from '../../../empty.util';
@Component({ @Component({
selector: 'ds-listable-object-component-loader', selector: 'ds-listable-object-component-loader',
@@ -56,11 +57,32 @@ export class ListableObjectComponentLoaderComponent implements OnInit {
*/ */
@Input() value: string; @Input() value: string;
/**
* Whether or not informational badges (e.g. Private, Withdrawn) should be hidden
*/
@Input() hideBadges = false;
/** /**
* Directive hook used to place the dynamic child component * Directive hook used to place the dynamic child component
*/ */
@ViewChild(ListableObjectDirective, {static: true}) listableObjectDirective: ListableObjectDirective; @ViewChild(ListableObjectDirective, {static: true}) listableObjectDirective: ListableObjectDirective;
/**
* View on the badges template, to be passed on to the loaded component (which will place the badges in the desired
* location, or on top if not specified)
*/
@ViewChild('badges', { static: true }) badges: ElementRef;
/**
* Whether or not the "Private" badge should be displayed for this listable object
*/
privateBadge = false;
/**
* Whether or not the "Withdrawn" badge should be displayed for this listable object
*/
withdrawnBadge = false;
constructor(private componentFactoryResolver: ComponentFactoryResolver) { constructor(private componentFactoryResolver: ComponentFactoryResolver) {
} }
@@ -68,12 +90,20 @@ export class ListableObjectComponentLoaderComponent implements OnInit {
* Setup the dynamic child component * Setup the dynamic child component
*/ */
ngOnInit(): void { ngOnInit(): void {
this.initBadges();
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.getComponent()); const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.getComponent());
const viewContainerRef = this.listableObjectDirective.viewContainerRef; const viewContainerRef = this.listableObjectDirective.viewContainerRef;
viewContainerRef.clear(); viewContainerRef.clear();
const componentRef = viewContainerRef.createComponent(componentFactory); const componentRef = viewContainerRef.createComponent(
componentFactory,
0,
undefined,
[
[this.badges.nativeElement],
]);
(componentRef.instance as any).object = this.object; (componentRef.instance as any).object = this.object;
(componentRef.instance as any).index = this.index; (componentRef.instance as any).index = this.index;
(componentRef.instance as any).linkType = this.linkType; (componentRef.instance as any).linkType = this.linkType;
@@ -84,6 +114,19 @@ export class ListableObjectComponentLoaderComponent implements OnInit {
(componentRef.instance as any).value = this.value; (componentRef.instance as any).value = this.value;
} }
/**
* Initialize which badges should be visible in the listable component
*/
initBadges() {
let objectAsAny = this.object as any;
if (hasValue(objectAsAny.indexableObject)) {
objectAsAny = objectAsAny.indexableObject;
}
const objectExistsAndValidViewMode = hasValue(objectAsAny) && this.viewMode !== ViewMode.StandalonePage;
this.privateBadge = objectExistsAndValidViewMode && hasValue(objectAsAny.isDiscoverable) && !objectAsAny.isDiscoverable;
this.withdrawnBadge = objectExistsAndValidViewMode && hasValue(objectAsAny.isWithdrawn) && objectAsAny.isWithdrawn;
}
/** /**
* Fetch the component depending on the item's relationship type, view mode and context * Fetch the component depending on the item's relationship type, view mode and context
* @returns {GenericConstructor<Component>} * @returns {GenericConstructor<Component>}

View File

@@ -1,6 +1,8 @@
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'"> <div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
<ds-truncatable [id]="dso.id"> <ds-truncatable [id]="dso.id">
<ng-content></ng-content> <div class="position-absolute ml-1">
<ng-content></ng-content>
</div>
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" <a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"
class="card-img-top full-width"> class="card-img-top full-width">
<div> <div>

View File

@@ -217,6 +217,7 @@ import { AuthorizedCollectionSelectorComponent } from './dso-selector/dso-select
import { DsoPageEditButtonComponent } from './dso-page/dso-page-edit-button/dso-page-edit-button.component'; import { DsoPageEditButtonComponent } from './dso-page/dso-page-edit-button/dso-page-edit-button.component';
import { HoverClassDirective } from './hover-class.directive'; import { HoverClassDirective } from './hover-class.directive';
import { ValidationSuggestionsComponent } from './input-suggestions/validation-suggestions/validation-suggestions.component'; import { ValidationSuggestionsComponent } from './input-suggestions/validation-suggestions/validation-suggestions.component';
import { ItemAlertsComponent } from './item/item-alerts/item-alerts.component';
import { ItemSearchResultGridElementComponent } from './object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component'; import { ItemSearchResultGridElementComponent } from './object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component';
import { ResourcePolicyEditComponent } from './resource-policies/edit/resource-policy-edit.component'; import { ResourcePolicyEditComponent } from './resource-policies/edit/resource-policy-edit.component';
import { ResourcePolicyCreateComponent } from './resource-policies/create/resource-policy-create.component'; import { ResourcePolicyCreateComponent } from './resource-policies/create/resource-policy-create.component';
@@ -522,7 +523,8 @@ const ENTRY_COMPONENTS = [
const SHARED_ITEM_PAGE_COMPONENTS = [ const SHARED_ITEM_PAGE_COMPONENTS = [
MetadataFieldWrapperComponent, MetadataFieldWrapperComponent,
MetadataValuesComponent, MetadataValuesComponent,
DsoPageEditButtonComponent DsoPageEditButtonComponent,
ItemAlertsComponent,
]; ];
const PROVIDERS = [ const PROVIDERS = [

View File

@@ -462,14 +462,10 @@
"admin.search.item.move": "Move", "admin.search.item.move": "Move",
"admin.search.item.private": "Private",
"admin.search.item.reinstate": "Reinstate", "admin.search.item.reinstate": "Reinstate",
"admin.search.item.withdraw": "Withdraw", "admin.search.item.withdraw": "Withdraw",
"admin.search.item.withdrawn": "Withdrawn",
"admin.search.title": "Administrative Search", "admin.search.title": "Administrative Search",
@@ -1345,12 +1341,24 @@
"item.alerts.private": "This item is private",
"item.alerts.withdrawn": "This item has been withdrawn",
"item.edit.authorizations.heading": "With this editor you can view and alter the policies of an item, plus alter policies of individual item components: bundles and bitstreams. Briefly, an item is a container of bundles, and bundles are containers of bitstreams. Containers usually have ADD/REMOVE/READ/WRITE policies, while bitstreams only have READ/WRITE policies.", "item.edit.authorizations.heading": "With this editor you can view and alter the policies of an item, plus alter policies of individual item components: bundles and bitstreams. Briefly, an item is a container of bundles, and bundles are containers of bitstreams. Containers usually have ADD/REMOVE/READ/WRITE policies, while bitstreams only have READ/WRITE policies.",
"item.edit.authorizations.title": "Edit item's Policies", "item.edit.authorizations.title": "Edit item's Policies",
"item.badge.private": "Private",
"item.badge.withdrawn": "Withdrawn",
"item.bitstreams.upload.bundle": "Bundle", "item.bitstreams.upload.bundle": "Bundle",
"item.bitstreams.upload.bundle.placeholder": "Select a bundle", "item.bitstreams.upload.bundle.placeholder": "Select a bundle",