mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
Merge pull request #1472 from 4Science/CST-4875-Feedback-form
Feedback form
This commit is contained in:
@@ -32,6 +32,12 @@ export function getBitstreamRequestACopyRoute(item, bitstream): { routerLink: st
|
||||
};
|
||||
}
|
||||
|
||||
export const HOME_PAGE_PATH = 'admin';
|
||||
|
||||
export function getHomePageRoute() {
|
||||
return `/${HOME_PAGE_PATH}`;
|
||||
}
|
||||
|
||||
export const ADMIN_MODULE_PATH = 'admin';
|
||||
|
||||
export function getAdminModuleRoute() {
|
||||
|
@@ -77,6 +77,7 @@ import { MetadataSchema } from './metadata/metadata-schema.model';
|
||||
import { MetadataService } from './metadata/metadata.service';
|
||||
import { RegistryService } from './registry/registry.service';
|
||||
import { RoleService } from './roles/role.service';
|
||||
import { FeedbackDataService } from './feedback/feedback-data.service';
|
||||
|
||||
import { ApiService } from './services/api.service';
|
||||
import { ServerResponseService } from './services/server-response.service';
|
||||
@@ -286,7 +287,8 @@ const PROVIDERS = [
|
||||
VocabularyService,
|
||||
VocabularyTreeviewService,
|
||||
SequenceService,
|
||||
GroupDataService
|
||||
GroupDataService,
|
||||
FeedbackDataService,
|
||||
];
|
||||
|
||||
/**
|
||||
|
@@ -27,4 +27,5 @@ export enum FeatureID {
|
||||
CanDeleteVersion = 'canDeleteVersion',
|
||||
CanCreateVersion = 'canCreateVersion',
|
||||
CanViewUsageStatistics = 'canViewUsageStatistics',
|
||||
CanSendFeedback = 'canSendFeedback',
|
||||
}
|
||||
|
88
src/app/core/feedback/feedback-data.service.spec.ts
Normal file
88
src/app/core/feedback/feedback-data.service.spec.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { FeedbackDataService } from './feedback-data.service';
|
||||
import { HALLink } from '../shared/hal-link.model';
|
||||
import { Item } from '../shared/item.model';
|
||||
import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { getMockRequestService } from '../../shared/mocks/request.service.mock';
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { CoreState } from '../core.reducers';
|
||||
import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
|
||||
import { Feedback } from './models/feedback.model';
|
||||
|
||||
describe('FeedbackDataService', () => {
|
||||
let service: FeedbackDataService;
|
||||
let requestService;
|
||||
let halService;
|
||||
let rdbService;
|
||||
let notificationsService;
|
||||
let http;
|
||||
let comparator;
|
||||
let objectCache;
|
||||
let store;
|
||||
let item;
|
||||
let bundleLink;
|
||||
let bundleHALLink;
|
||||
|
||||
const feedbackPayload = Object.assign(new Feedback(), {
|
||||
email: 'test@email.com',
|
||||
message: 'message',
|
||||
page: '/home'
|
||||
});
|
||||
|
||||
|
||||
function initTestService(): FeedbackDataService {
|
||||
bundleLink = '/items/0fdc0cd7-ff8c-433d-b33c-9b56108abc07/bundles';
|
||||
bundleHALLink = new HALLink();
|
||||
bundleHALLink.href = bundleLink;
|
||||
item = new Item();
|
||||
item._links = {
|
||||
bundles: bundleHALLink
|
||||
};
|
||||
requestService = getMockRequestService();
|
||||
halService = new HALEndpointServiceStub('url') as any;
|
||||
rdbService = {} as RemoteDataBuildService;
|
||||
notificationsService = {} as NotificationsService;
|
||||
http = {} as HttpClient;
|
||||
comparator = new DSOChangeAnalyzer() as any;
|
||||
objectCache = {
|
||||
|
||||
addPatch: () => {
|
||||
/* empty */
|
||||
},
|
||||
getObjectBySelfLink: () => {
|
||||
/* empty */
|
||||
}
|
||||
} as any;
|
||||
store = {} as Store<CoreState>;
|
||||
return new FeedbackDataService(
|
||||
requestService,
|
||||
rdbService,
|
||||
store,
|
||||
objectCache,
|
||||
halService,
|
||||
notificationsService,
|
||||
http,
|
||||
comparator,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
beforeEach(() => {
|
||||
service = initTestService();
|
||||
});
|
||||
|
||||
|
||||
describe('getFeedback', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(service, 'getFeedback');
|
||||
service.getFeedback('3');
|
||||
});
|
||||
|
||||
it('should call getFeedback with the feedback link', () => {
|
||||
expect(service.getFeedback).toHaveBeenCalledWith('3');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
49
src/app/core/feedback/feedback-data.service.ts
Normal file
49
src/app/core/feedback/feedback-data.service.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { DataService } from '../data/data.service';
|
||||
import { Feedback } from './models/feedback.model';
|
||||
import { FEEDBACK } from './models/feedback.resource-type';
|
||||
import { dataService } from '../cache/builders/build-decorators';
|
||||
import { RequestService } from '../data/request.service';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
|
||||
import { getFirstSucceededRemoteData, getRemoteDataPayload } from '../shared/operators';
|
||||
|
||||
/**
|
||||
* Service for checking and managing the feedback
|
||||
*/
|
||||
@Injectable()
|
||||
@dataService(FEEDBACK)
|
||||
export class FeedbackDataService extends DataService<Feedback> {
|
||||
protected linkPath = 'feedbacks';
|
||||
|
||||
constructor(
|
||||
protected requestService: RequestService,
|
||||
protected rdbService: RemoteDataBuildService,
|
||||
protected store: Store<any>,
|
||||
protected objectCache: ObjectCacheService,
|
||||
protected halService: HALEndpointService,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected http: HttpClient,
|
||||
protected comparator: DSOChangeAnalyzer<Feedback>,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get feedback from its id
|
||||
* @param uuid string the id of the feedback
|
||||
*/
|
||||
getFeedback(uuid: string): Observable<Feedback> {
|
||||
return this.findById(uuid).pipe(
|
||||
getFirstSucceededRemoteData(),
|
||||
getRemoteDataPayload(),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
20
src/app/core/feedback/feedback.guard.ts
Normal file
20
src/app/core/feedback/feedback.guard.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot, UrlTree } from '@angular/router';
|
||||
import { Observable } from 'rxjs';
|
||||
import { AuthorizationDataService } from '../data/feature-authorization/authorization-data.service';
|
||||
import { FeatureID } from '../data/feature-authorization/feature-id';
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
/**
|
||||
* An guard for redirecting users to the feedback page if user is authorized
|
||||
*/
|
||||
@Injectable()
|
||||
export class FeedbackGuard implements CanActivate {
|
||||
|
||||
constructor(private authorizationService: AuthorizationDataService) {
|
||||
}
|
||||
|
||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> {
|
||||
return this.authorizationService.isAuthorized(FeatureID.CanSendFeedback);
|
||||
}
|
||||
|
||||
}
|
34
src/app/core/feedback/models/feedback.model.ts
Normal file
34
src/app/core/feedback/models/feedback.model.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { autoserialize, inheritSerialization } from 'cerialize';
|
||||
import { typedObject } from '../../cache/builders/build-decorators';
|
||||
|
||||
import { DSpaceObject } from '../../shared/dspace-object.model';
|
||||
import { HALLink } from '../../shared/hal-link.model';
|
||||
import { FEEDBACK } from './feedback.resource-type';
|
||||
|
||||
@typedObject
|
||||
@inheritSerialization(DSpaceObject)
|
||||
export class Feedback extends DSpaceObject {
|
||||
static type = FEEDBACK;
|
||||
|
||||
/**
|
||||
* The email address
|
||||
*/
|
||||
@autoserialize
|
||||
public email: string;
|
||||
|
||||
/**
|
||||
* A string representing message the user inserted
|
||||
*/
|
||||
@autoserialize
|
||||
public message: string;
|
||||
/**
|
||||
* A string representing the page from which the user came from
|
||||
*/
|
||||
@autoserialize
|
||||
public page: string;
|
||||
|
||||
_links: {
|
||||
self: HALLink;
|
||||
};
|
||||
|
||||
}
|
9
src/app/core/feedback/models/feedback.resource-type.ts
Normal file
9
src/app/core/feedback/models/feedback.resource-type.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { ResourceType } from '../../shared/resource-type';
|
||||
|
||||
/**
|
||||
* The resource type for Feedback
|
||||
*
|
||||
* Needs to be in a separate file to prevent circular
|
||||
* dependencies in webpack.
|
||||
*/
|
||||
export const FEEDBACK = new ResourceType('feedback');
|
@@ -75,6 +75,10 @@
|
||||
<a class="text-white"
|
||||
routerLink="info/end-user-agreement">{{ 'footer.link.end-user-agreement' | translate}}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="text-white"
|
||||
routerLink="info/feedback">{{ 'footer.link.feedback' | translate}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -0,0 +1,45 @@
|
||||
<div class="row row-offcanvas row-offcanvas-right">
|
||||
<div class="col-xs-12 col-sm-12 col-md-9 main-content">
|
||||
<form class="primary" [formGroup]="feedbackForm" (ngSubmit)="createFeedback()">
|
||||
<h2>{{ 'info.feedback.head' | translate }}</h2>
|
||||
<p>{{ 'info.feedback.info' | translate }}</p>
|
||||
<fieldset class="col p-0">
|
||||
<div class="row">
|
||||
<div class="control-group col-sm-12">
|
||||
<label class="control-label" for="email">{{ 'info.feedback.email-label' | translate }} </label>
|
||||
<input id="email" class="form-control" name="email" type="text" value="" formControlName="email" autofocus="autofocus" title="{{ 'info.feedback.email_help' | translate }}">
|
||||
<small class="text-muted">{{ 'info.feedback.email_help' | translate }}</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="feedbackForm.controls.email.invalid && (feedbackForm.controls.email.dirty || feedbackForm.controls.email.touched)"
|
||||
class="alert">
|
||||
<ds-error *ngIf="feedbackForm.controls.email.errors?.required" message="{{'info.feedback.error.email.required' | translate}}"></ds-error>
|
||||
<ds-error *ngIf="feedbackForm.controls.email.errors?.pattern" message="{{'info.feedback.error.email.required' | translate}}"></ds-error>
|
||||
</ng-container>
|
||||
<div class="row">
|
||||
<div class="control-group col-sm-12">
|
||||
<label class="control-label" for="comments">{{ 'info.feedback.comments' | translate }}: </label>
|
||||
<textarea id="comments" formControlName="message" class="form-control" name="message" cols="20" rows="5"> </textarea>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="feedbackForm.controls.message.invalid && (feedbackForm.controls.message.dirty || feedbackForm.controls.message.touched)"
|
||||
class="alert">
|
||||
<ds-error *ngIf="feedbackForm.controls.message.errors?.required" message="{{'info.feedback.error.message.required' | translate}}"></ds-error>
|
||||
</ng-container>
|
||||
<div class="row">
|
||||
<div class="control-group col-sm-12">
|
||||
<label class="control-label" for="page">{{ 'info.feedback.page-label' | translate }} </label>
|
||||
<input id="page" readonly class="form-control" name="page" type="text" value="" formControlName="page" autofocus="autofocus" title="{{ 'info.feedback.page_help' | translate }}">
|
||||
<small class="text-muted">{{ 'info.feedback.page_help' | translate }}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row py-2">
|
||||
<div class="control-group col-sm-12 text-right">
|
||||
<button [disabled]="!feedbackForm.valid" class="btn btn-primary" name="submit" type="submit">{{ 'info.feedback.send' | translate }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
@@ -0,0 +1,3 @@
|
||||
ds-error{
|
||||
color:red;
|
||||
}
|
@@ -0,0 +1,97 @@
|
||||
import { EPersonMock } from '../../../shared/testing/eperson.mock';
|
||||
import { FeedbackDataService } from '../../../core/feedback/feedback-data.service';
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { FeedbackFormComponent } from './feedback-form.component';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { RouteService } from '../../../core/services/route.service';
|
||||
import { routeServiceStub } from '../../../shared/testing/route-service.stub';
|
||||
import { FormBuilder } from '@angular/forms';
|
||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service.stub';
|
||||
import { AuthService } from '../../../core/auth/auth.service';
|
||||
import { AuthServiceStub } from '../../../shared/testing/auth-service.stub';
|
||||
import { of } from 'rxjs';
|
||||
import { Feedback } from '../../../core/feedback/models/feedback.model';
|
||||
import { Router } from '@angular/router';
|
||||
import { RouterMock } from '../../../shared/mocks/router.mock';
|
||||
import { NativeWindowService } from '../../../core/services/window.service';
|
||||
import { NativeWindowMockFactory } from '../../../shared/mocks/mock-native-window-ref';
|
||||
|
||||
|
||||
describe('FeedbackFormComponent', () => {
|
||||
let component: FeedbackFormComponent;
|
||||
let fixture: ComponentFixture<FeedbackFormComponent>;
|
||||
let de: DebugElement;
|
||||
const notificationService = new NotificationsServiceStub();
|
||||
const feedbackDataServiceStub = jasmine.createSpyObj('feedbackDataService', {
|
||||
create: of(new Feedback())
|
||||
});
|
||||
const authService: AuthServiceStub = Object.assign(new AuthServiceStub(), {
|
||||
getAuthenticatedUserFromStore: () => {
|
||||
return of(EPersonMock);
|
||||
}
|
||||
});
|
||||
const routerStub = new RouterMock();
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot()],
|
||||
declarations: [FeedbackFormComponent],
|
||||
providers: [
|
||||
{ provide: RouteService, useValue: routeServiceStub },
|
||||
{ provide: FormBuilder, useValue: new FormBuilder() },
|
||||
{ provide: NotificationsService, useValue: notificationService },
|
||||
{ provide: FeedbackDataService, useValue: feedbackDataServiceStub },
|
||||
{ provide: AuthService, useValue: authService },
|
||||
{ provide: NativeWindowService, useFactory: NativeWindowMockFactory },
|
||||
{ provide: Router, useValue: routerStub },
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(FeedbackFormComponent);
|
||||
component = fixture.componentInstance;
|
||||
de = fixture.debugElement;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should have page value', () => {
|
||||
expect(component.feedbackForm.controls.page.value).toEqual('http://localhost/home');
|
||||
});
|
||||
|
||||
it('should have email if ePerson', () => {
|
||||
expect(component.feedbackForm.controls.email.value).toEqual('test@test.com');
|
||||
});
|
||||
|
||||
it('should have disabled button', () => {
|
||||
expect(de.query(By.css('button')).nativeElement.disabled).toBeTrue();
|
||||
});
|
||||
|
||||
describe('when message is inserted', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
component.feedbackForm.patchValue({ message: 'new feedback' });
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should not have disabled button', () => {
|
||||
expect(de.query(By.css('button')).nativeElement.disabled).toBeFalse();
|
||||
});
|
||||
|
||||
it('on submit should call createFeedback of feedbackDataServiceStub service', () => {
|
||||
component.createFeedback();
|
||||
fixture.detectChanges();
|
||||
expect(feedbackDataServiceStub.create).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
@@ -0,0 +1,83 @@
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { NoContent } from '../../../core/shared/NoContent.model';
|
||||
import { FeedbackDataService } from '../../../core/feedback/feedback-data.service';
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { RouteService } from '../../../core/services/route.service';
|
||||
import { FormBuilder, Validators } from '@angular/forms';
|
||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { AuthService } from '../../../core/auth/auth.service';
|
||||
import { EPerson } from '../../../core/eperson/models/eperson.model';
|
||||
import { getFirstCompletedRemoteData } from '../../../core/shared/operators';
|
||||
import { Router } from '@angular/router';
|
||||
import { getHomePageRoute } from '../../../app-routing-paths';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { NativeWindowRef, NativeWindowService } from '../../../core/services/window.service';
|
||||
import { URLCombiner } from '../../../core/url-combiner/url-combiner';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-feedback-form',
|
||||
templateUrl: './feedback-form.component.html',
|
||||
styleUrls: ['./feedback-form.component.scss']
|
||||
})
|
||||
/**
|
||||
* Component displaying the contents of the Feedback Statement
|
||||
*/
|
||||
export class FeedbackFormComponent implements OnInit {
|
||||
|
||||
/**
|
||||
* Form builder created used from the feedback from
|
||||
*/
|
||||
feedbackForm = this.fb.group({
|
||||
email: ['', [Validators.required, Validators.pattern('^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,4}$')]],
|
||||
message: ['', Validators.required],
|
||||
page: [''],
|
||||
});
|
||||
|
||||
constructor(
|
||||
@Inject(NativeWindowService) protected _window: NativeWindowRef,
|
||||
public routeService: RouteService,
|
||||
private fb: FormBuilder,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected translate: TranslateService,
|
||||
private feedbackDataService: FeedbackDataService,
|
||||
private authService: AuthService,
|
||||
private router: Router) {
|
||||
}
|
||||
|
||||
/**
|
||||
* On init check if user is logged in and use its email if so
|
||||
*/
|
||||
ngOnInit() {
|
||||
|
||||
this.authService.getAuthenticatedUserFromStore().pipe(take(1)).subscribe((user: EPerson) => {
|
||||
if (!!user) {
|
||||
this.feedbackForm.patchValue({ email: user.email });
|
||||
}
|
||||
});
|
||||
|
||||
this.routeService.getPreviousUrl().pipe(take(1)).subscribe((url: string) => {
|
||||
if (!url) {
|
||||
url = getHomePageRoute();
|
||||
}
|
||||
const relatedUrl = new URLCombiner(this._window.nativeWindow.origin, url).toString();
|
||||
this.feedbackForm.patchValue({ page: relatedUrl });
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to create the feedback from form values
|
||||
*/
|
||||
createFeedback(): void {
|
||||
const url = this.feedbackForm.value.page.replace(this._window.nativeWindow.origin, '');
|
||||
this.feedbackDataService.create(this.feedbackForm.value).pipe(getFirstCompletedRemoteData()).subscribe((response: RemoteData<NoContent>) => {
|
||||
if (response.isSuccess) {
|
||||
this.notificationsService.success(this.translate.instant('info.feedback.create.success'));
|
||||
this.feedbackForm.reset();
|
||||
this.router.navigateByUrl(url);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
3
src/app/info/feedback/feedback.component.html
Normal file
3
src/app/info/feedback/feedback.component.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<div class="container">
|
||||
<ds-feedback-form></ds-feedback-form>
|
||||
</div>
|
0
src/app/info/feedback/feedback.component.scss
Normal file
0
src/app/info/feedback/feedback.component.scss
Normal file
27
src/app/info/feedback/feedback.component.spec.ts
Normal file
27
src/app/info/feedback/feedback.component.spec.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { FeedbackComponent } from './feedback.component';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
|
||||
describe('FeedbackComponent', () => {
|
||||
let component: FeedbackComponent;
|
||||
let fixture: ComponentFixture<FeedbackComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot()],
|
||||
declarations: [FeedbackComponent],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(FeedbackComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
12
src/app/info/feedback/feedback.component.ts
Normal file
12
src/app/info/feedback/feedback.component.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-feedback',
|
||||
templateUrl: './feedback.component.html',
|
||||
styleUrls: ['./feedback.component.scss']
|
||||
})
|
||||
/**
|
||||
* Component displaying the Feedback Statement
|
||||
*/
|
||||
export class FeedbackComponent {
|
||||
}
|
26
src/app/info/feedback/themed-feedback.component.ts
Normal file
26
src/app/info/feedback/themed-feedback.component.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ThemedComponent } from '../../shared/theme-support/themed.component';
|
||||
import { FeedbackComponent } from './feedback.component';
|
||||
|
||||
/**
|
||||
* Themed wrapper for FeedbackComponent
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-themed-feedback',
|
||||
styleUrls: [],
|
||||
templateUrl: '../../shared/theme-support/themed.component.html',
|
||||
})
|
||||
export class ThemedFeedbackComponent extends ThemedComponent<FeedbackComponent> {
|
||||
protected getComponentName(): string {
|
||||
return 'FeedbackComponent';
|
||||
}
|
||||
|
||||
protected importThemedComponent(themeName: string): Promise<any> {
|
||||
return import(`../../../themes/${themeName}/app/info/feedback/feedback.component`);
|
||||
}
|
||||
|
||||
protected importUnthemedComponent(): Promise<any> {
|
||||
return import(`./feedback.component`);
|
||||
}
|
||||
|
||||
}
|
@@ -2,6 +2,7 @@ import { getInfoModulePath } from '../app-routing-paths';
|
||||
|
||||
export const END_USER_AGREEMENT_PATH = 'end-user-agreement';
|
||||
export const PRIVACY_PATH = 'privacy';
|
||||
export const FEEDBACK_PATH = 'feedback';
|
||||
|
||||
export function getEndUserAgreementPath() {
|
||||
return getSubPath(END_USER_AGREEMENT_PATH);
|
||||
@@ -11,6 +12,10 @@ export function getPrivacyPath() {
|
||||
return getSubPath(PRIVACY_PATH);
|
||||
}
|
||||
|
||||
export function getFeedbackPath() {
|
||||
return getSubPath(FEEDBACK_PATH);
|
||||
}
|
||||
|
||||
function getSubPath(path: string) {
|
||||
return `${getInfoModulePath()}/${path}`;
|
||||
}
|
||||
|
@@ -1,9 +1,12 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||
import { PRIVACY_PATH, END_USER_AGREEMENT_PATH } from './info-routing-paths';
|
||||
import { PRIVACY_PATH, END_USER_AGREEMENT_PATH, FEEDBACK_PATH } from './info-routing-paths';
|
||||
import { ThemedEndUserAgreementComponent } from './end-user-agreement/themed-end-user-agreement.component';
|
||||
import { ThemedPrivacyComponent } from './privacy/themed-privacy.component';
|
||||
import { ThemedFeedbackComponent } from './feedback/themed-feedback.component';
|
||||
import { FeedbackGuard } from '../core/feedback/feedback.guard';
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -22,6 +25,15 @@ import { ThemedPrivacyComponent } from './privacy/themed-privacy.component';
|
||||
resolve: { breadcrumb: I18nBreadcrumbResolver },
|
||||
data: { title: 'info.privacy.title', breadcrumbKey: 'info.privacy' }
|
||||
}
|
||||
]),
|
||||
RouterModule.forChild([
|
||||
{
|
||||
path: FEEDBACK_PATH,
|
||||
component: ThemedFeedbackComponent,
|
||||
resolve: { breadcrumb: I18nBreadcrumbResolver },
|
||||
data: { title: 'info.feedback.title', breadcrumbKey: 'info.feedback' },
|
||||
canActivate: [FeedbackGuard]
|
||||
}
|
||||
])
|
||||
]
|
||||
})
|
||||
|
@@ -8,6 +8,11 @@ import { PrivacyComponent } from './privacy/privacy.component';
|
||||
import { PrivacyContentComponent } from './privacy/privacy-content/privacy-content.component';
|
||||
import { ThemedEndUserAgreementComponent } from './end-user-agreement/themed-end-user-agreement.component';
|
||||
import { ThemedPrivacyComponent } from './privacy/themed-privacy.component';
|
||||
import { FeedbackComponent } from './feedback/feedback.component';
|
||||
import { FeedbackFormComponent } from './feedback/feedback-form/feedback-form.component';
|
||||
import { ThemedFeedbackComponent } from './feedback/themed-feedback.component';
|
||||
import { FeedbackGuard } from '../core/feedback/feedback.guard';
|
||||
|
||||
|
||||
const DECLARATIONS = [
|
||||
EndUserAgreementComponent,
|
||||
@@ -15,21 +20,25 @@ const DECLARATIONS = [
|
||||
EndUserAgreementContentComponent,
|
||||
PrivacyComponent,
|
||||
PrivacyContentComponent,
|
||||
ThemedPrivacyComponent
|
||||
ThemedPrivacyComponent,
|
||||
FeedbackComponent,
|
||||
FeedbackFormComponent,
|
||||
ThemedFeedbackComponent
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
InfoRoutingModule
|
||||
InfoRoutingModule,
|
||||
],
|
||||
declarations: [
|
||||
...DECLARATIONS
|
||||
],
|
||||
exports: [
|
||||
...DECLARATIONS
|
||||
]
|
||||
],
|
||||
providers: [FeedbackGuard]
|
||||
})
|
||||
export class InfoModule {
|
||||
}
|
||||
|
@@ -7,7 +7,8 @@ export const MockWindow = {
|
||||
get href() {
|
||||
return this._href;
|
||||
}
|
||||
}
|
||||
},
|
||||
origin: 'http://localhost'
|
||||
};
|
||||
|
||||
export class NativeWindowRefMock {
|
||||
|
@@ -30,6 +30,9 @@ export const routeServiceStub: any = {
|
||||
},
|
||||
getHistory: () => {
|
||||
return observableOf(['/home', '/collection/123', '/home']);
|
||||
},
|
||||
getPreviousUrl: () => {
|
||||
return observableOf('/home');
|
||||
}
|
||||
/* tslint:enable:no-empty */
|
||||
};
|
||||
|
@@ -1377,6 +1377,8 @@
|
||||
|
||||
"footer.link.end-user-agreement":"End User Agreement",
|
||||
|
||||
"footer.link.feedback":"Send Feedback",
|
||||
|
||||
|
||||
|
||||
"forgot-email.form.header": "Forgot Password",
|
||||
@@ -1575,6 +1577,32 @@
|
||||
|
||||
"info.privacy.title": "Privacy Statement",
|
||||
|
||||
"info.feedback.breadcrumbs": "Feedback",
|
||||
|
||||
"info.feedback.head": "Feedback",
|
||||
|
||||
"info.feedback.title": "Feedback",
|
||||
|
||||
"info.feedback.info": "Thanks for sharing your feedback about the DSpace system. Your comments are appreciated!",
|
||||
|
||||
"info.feedback.email_help": "This address will be used to follow up on your feedback.",
|
||||
|
||||
"info.feedback.send": "Send Feedback",
|
||||
|
||||
"info.feedback.comments": "Comments",
|
||||
|
||||
"info.feedback.email-label": "Your Email",
|
||||
|
||||
"info.feedback.create.success" : "Feedback Sent Successfully!",
|
||||
|
||||
"info.feedback.error.email.required" : "A valid email address is required",
|
||||
|
||||
"info.feedback.error.message.required" : "A comment is required",
|
||||
|
||||
"info.feedback.page-label" : "Page",
|
||||
|
||||
"info.feedback.page_help" : "Tha page related to your feedback",
|
||||
|
||||
|
||||
|
||||
"item.alerts.private": "This item is private",
|
||||
|
15
src/themes/custom/app/info/feedback/feedback.component.ts
Normal file
15
src/themes/custom/app/info/feedback/feedback.component.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { FeedbackComponent as BaseComponent } from '../../../../../app/info/feedback/feedback.component';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-feedback',
|
||||
// styleUrls: ['./feedback.component.scss'],
|
||||
styleUrls: ['../../../../../app/info/feedback/feedback.component.scss'],
|
||||
// templateUrl: './feedback.component.html'
|
||||
templateUrl: '../../../../../app/info/feedback/feedback.component.html'
|
||||
})
|
||||
|
||||
/**
|
||||
* Component displaying the feedback Statement
|
||||
*/
|
||||
export class FeedbackComponent extends BaseComponent { }
|
@@ -83,6 +83,7 @@ import { FileSectionComponent } from './app/item-page/simple/field-components/fi
|
||||
import { SearchModule } from '../../app/shared/search/search.module';
|
||||
import { ResourcePoliciesModule } from '../../app/shared/resource-policies/resource-policies.module';
|
||||
import { ComcolModule } from '../../app/shared/comcol/comcol.module';
|
||||
import { FeedbackComponent } from './app/info/feedback/feedback.component';
|
||||
|
||||
const DECLARATIONS = [
|
||||
FileSectionComponent,
|
||||
@@ -124,7 +125,8 @@ const DECLARATIONS = [
|
||||
HeaderComponent,
|
||||
NavbarComponent,
|
||||
HeaderNavbarWrapperComponent,
|
||||
BreadcrumbsComponent
|
||||
BreadcrumbsComponent,
|
||||
FeedbackComponent
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
Reference in New Issue
Block a user