New packages org (#2639)

New packages org
This commit is contained in:
Eugenio Romano
2017-11-16 14:12:52 +00:00
committed by GitHub
parent 6a24c6ef75
commit a52bb5600a
1984 changed files with 17179 additions and 40423 deletions

View File

@@ -0,0 +1,90 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import {
AlfrescoApi, ContentApi, FavoritesApi, NodesApi,
PeopleApi, RenditionsApi, SharedlinksApi, SitesApi,
VersionsApi
} from 'alfresco-js-api';
import * as alfrescoApi from 'alfresco-js-api';
import { AppConfigService } from '../app-config/app-config.service';
import { StorageService } from './storage.service';
@Injectable()
export class AlfrescoApiService {
private alfrescoApi: AlfrescoApi;
getInstance(): AlfrescoApi {
return this.alfrescoApi;
}
get contentApi(): ContentApi {
return this.getInstance().content;
}
get nodesApi(): NodesApi {
return this.getInstance().nodes;
}
get renditionsApi(): RenditionsApi {
return this.getInstance().core.renditionsApi;
}
get sharedLinksApi(): SharedlinksApi {
return this.getInstance().core.sharedlinksApi;
}
get sitesApi(): SitesApi {
return this.getInstance().core.sitesApi;
}
get favoritesApi(): FavoritesApi {
return this.getInstance().core.favoritesApi;
}
get peopleApi(): PeopleApi {
return this.getInstance().core.peopleApi;
}
get searchApi() {
return this.getInstance().search.searchApi;
}
get versionsApi(): VersionsApi {
return this.getInstance().core.versionsApi;
}
constructor(private appConfig: AppConfigService,
private storage: StorageService) {
this.reset();
}
reset() {
this.alfrescoApi = <AlfrescoApi> new alfrescoApi({
provider: this.storage.getItem('AUTH_TYPE'),
ticketEcm: this.storage.getItem('ticket-ECM'),
ticketBpm: this.storage.getItem('ticket-BPM'),
hostEcm: this.appConfig.get<string>('ecmHost'),
hostBpm: this.appConfig.get<string>('bpmHost'),
contextRoot: 'alfresco',
disableCsrf: this.storage.getItem('DISABLE_CSRF') === 'true'
});
}
}

View File

@@ -0,0 +1,113 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { TestBed } from '@angular/core/testing';
import { AppConfigServiceMock } from '../mock/app-config.service.mock';
import { fakeApps } from '../mock/apps-service.mock';
import { AlfrescoApiService } from './alfresco-api.service';
import { AppConfigService } from '../app-config/app-config.service';
import { AppsProcessService } from './apps-process.service';
import { LogService } from './log.service';
import { StorageService } from './storage.service';
declare let jasmine: any;
describe('Apps Service', () => {
let service: AppsProcessService;
beforeEach((() => {
TestBed.configureTestingModule({
providers: [
AppsProcessService,
AlfrescoApiService,
StorageService,
LogService,
{provide: AppConfigService, useClass: AppConfigServiceMock}
]
}).compileComponents();
}));
beforeEach(() => {
service = TestBed.get(AppsProcessService);
});
beforeEach(() => {
jasmine.Ajax.install();
});
afterEach(() => {
jasmine.Ajax.uninstall();
});
it('should get the deployed apps ', (done) => {
service.getDeployedApplications().subscribe(
(res: any) => {
expect(res).toBeDefined();
expect(res.length).toEqual(2);
expect(res[0].name).toEqual('Sales-Fakes-App');
expect(res[0].description).toEqual('desc-fake1');
expect(res[0].deploymentId).toEqual('111');
expect(res[1].name).toEqual('health-care-Fake');
expect(res[1].description).toEqual('desc-fake2');
expect(res[1].deploymentId).toEqual('444');
done();
}
);
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 200,
contentType: 'application/json',
responseText: JSON.stringify(fakeApps)
});
});
it('should get the filter deployed app ', (done) => {
service.getDeployedApplicationsByName('health-care-Fake').subscribe(
(res: any) => {
expect(res).toBeDefined();
expect(res.name).toEqual('health-care-Fake');
expect(res.description).toEqual('desc-fake2');
expect(res.deploymentId).toEqual('444');
done();
}
);
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 200,
contentType: 'application/json',
responseText: JSON.stringify(fakeApps)
});
});
it('should get the deployed app details by id ', (done) => {
service.getApplicationDetailsById(1).subscribe(
(app: any) => {
expect(app).toBeDefined();
expect(app.name).toEqual('Sales-Fakes-App');
expect(app.description).toEqual('desc-fake1');
expect(app.deploymentId).toEqual('111');
done();
}
);
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 200,
contentType: 'application/json',
responseText: JSON.stringify(fakeApps)
});
});
});

View File

@@ -0,0 +1,60 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { AppDefinitionRepresentation } from 'alfresco-js-api';
import { Observable } from 'rxjs/Rx';
import { AlfrescoApiService } from './alfresco-api.service';
import { LogService } from './log.service';
@Injectable()
export class AppsProcessService {
constructor(private apiService: AlfrescoApiService,
private logService: LogService) {
}
getDeployedApplications(): Observable<AppDefinitionRepresentation[]> {
return Observable.fromPromise(this.apiService.getInstance().activiti.appsApi.getAppDefinitions())
.map((response: any) => {
return response.data;
})
.catch(err => this.handleError(err));
}
getDeployedApplicationsByName(name: string): Observable<AppDefinitionRepresentation> {
return Observable.fromPromise(this.apiService.getInstance().activiti.appsApi.getAppDefinitions())
.map((response: any) => {
return response.data.find(app => app.name === name);
})
.catch(err => this.handleError(err));
}
getApplicationDetailsById(appId: number): Observable<AppDefinitionRepresentation> {
return Observable.fromPromise(this.apiService.getInstance().activiti.appsApi.getAppDefinitions())
.map((response: any) => {
return response.data.find(app => app.id === appId);
})
.catch(err => this.handleError(err));
}
private handleError(error: any) {
this.logService.error(error);
return Observable.throw(error || 'Server error');
}
}

View File

@@ -0,0 +1,100 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { async, inject, TestBed } from '@angular/core/testing';
import { Router } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { CookieServiceMock } from './../mock/cookie.service.mock';
import { AlfrescoApiService } from './alfresco-api.service';
import { SettingsService } from './settings.service';
import { AppConfigModule } from '../app-config';
import { AuthGuardBpm } from './auth-guard-bpm.service';
import { AuthenticationService } from './authentication.service';
import { CookieService } from './cookie.service';
import { LogService } from './log.service';
import { StorageService } from './storage.service';
import { TranslateLoaderService } from './translate-loader.service';
import { UserPreferencesService } from './user-preferences.service';
describe('AuthGuardService BPM', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
AppConfigModule,
RouterTestingModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderService
}
})
],
providers: [
AuthGuardBpm,
SettingsService,
AlfrescoApiService,
AuthenticationService,
StorageService,
UserPreferencesService,
{ provide: CookieService, useClass: CookieServiceMock },
LogService
]
}).compileComponents();
}));
it('if the alfresco js api is logged in should canActivate be true',
async(inject([AuthGuardBpm, Router, SettingsService, StorageService, AuthenticationService], (auth, router, settingsService, storage, authService) => {
spyOn(router, 'navigate');
authService.isBpmLoggedIn = () => {
return true;
};
expect(auth.canActivate(null, { url: '' })).toBeTruthy();
expect(router.navigate).not.toHaveBeenCalled();
}))
);
it('if the alfresco js api is NOT logged in should canActivate be false',
async(inject([AuthGuardBpm, Router, SettingsService, StorageService, AuthenticationService], (auth, router, settingsService, storage, authService) => {
spyOn(router, 'navigate');
authService.isBpmLoggedIn = () => {
return false;
};
expect(auth.canActivate(null, { url: '' })).toBeFalsy();
expect(router.navigate).toHaveBeenCalled();
}))
);
it('should set redirect url',
async(inject([AuthGuardBpm, Router, AuthenticationService], (auth, router, authService) => {
const state = { url: 'some-url' };
spyOn(router, 'navigate');
spyOn(authService, 'setRedirectUrl');
auth.canActivate(null , state);
expect(authService.setRedirectUrl).toHaveBeenCalledWith(state.url);
}))
);
});

View File

@@ -0,0 +1,49 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import {
ActivatedRouteSnapshot, CanActivate, CanActivateChild,
Router,
RouterStateSnapshot
} from '@angular/router';
import { AuthenticationService } from './authentication.service';
@Injectable()
export class AuthGuardBpm implements CanActivate, CanActivateChild {
constructor(private authService: AuthenticationService, private router: Router) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
return this.checkLogin(state.url);
}
canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
return this.canActivate(route, state);
}
checkLogin(redirectUrl: string): boolean {
if (this.authService.isBpmLoggedIn()) {
return true;
}
this.authService.setRedirectUrl(redirectUrl);
this.router.navigate(['/login']);
return false;
}
}

View File

@@ -0,0 +1,217 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { async, inject, TestBed } from '@angular/core/testing';
import { Router } from '@angular/router';
import { AlfrescoApiService } from './alfresco-api.service';
import { AuthGuardEcm } from './auth-guard-ecm.service';
import { AuthenticationService } from './authentication.service';
class RouterProvider {
navigate: Function = jasmine.createSpy('RouterProviderNavigate');
}
class AlfrescoApiServiceProvider {
private settings: any = {
validateTicket: true,
isLoggedIn: true
};
constructor(settings: any = {}) {
Object.assign(this.settings, settings);
}
getInstance() {
return {
ecmAuth: this.ecmAuth
};
}
private get ecmAuth() {
return {
validateTicket: this.validateTicket.bind(this),
isLoggedIn: this.isLoggedIn.bind(this)
};
}
private validateTicket() {
const { validateTicket } = this.settings;
return validateTicket
? Promise.resolve('Valid!')
: Promise.reject('Invalid');
}
private isLoggedIn() {
return this.settings.isLoggedIn;
}
}
class AuthenticationServiceProvider {
setRedirectUrl: Function = jasmine.createSpy('setRedirectUrl');
}
class TestConfig {
router: any;
guard: any;
auth: any;
private settings: any = {
validateTicket: true,
isLoggedIn: true
};
constructor(settings: any = {}) {
Object.assign(this.settings, settings);
TestBed.configureTestingModule({
providers: [
this.routerProvider,
this.alfrescoApiServiceProvider,
this.authenticationProvider,
AuthGuardEcm
]
});
inject([ AuthGuardEcm, Router, AuthenticationService ], (guard: AuthGuardEcm, router: Router, auth: AuthenticationService) => {
this.guard = guard;
this.router = router;
this.auth = auth;
})();
}
private get routerProvider() {
return {
provide: Router,
useValue: new RouterProvider()
};
}
private get authenticationProvider() {
return {
provide: AuthenticationService,
useValue: new AuthenticationServiceProvider()
};
}
private get alfrescoApiServiceProvider () {
const { validateTicket, isLoggedIn } = this.settings;
return {
provide: AlfrescoApiService,
useValue: new AlfrescoApiServiceProvider({
validateTicket,
isLoggedIn
})
};
}
get navigateSpy() {
return this.router.navigate;
}
}
describe('CanActivateLoggedIn', () => {
describe('user is not logged in', () => {
beforeEach(async(() => {
this.test = new TestConfig({
isLoggedIn: false
});
const { guard, router } = this.test;
guard.canActivate(null, { url: '' }).then((activate) => {
this.activate = activate;
this.navigateSpy = router.navigate;
});
}));
it('does not allow route to activate', () => {
expect(this.activate).toBe(false);
});
it('redirects to /login', () => {
expect(this.navigateSpy).toHaveBeenCalledWith([ '/login' ]);
});
});
describe('user is logged in but ticket is invalid', () => {
beforeEach(async(() => {
this.test = new TestConfig({
isLoggedIn: true,
validateTicket: false
});
const { guard, router } = this.test;
guard.canActivate(null, { url: '' }).then((activate) => {
this.activate = activate;
this.navigateSpy = router.navigate;
});
}));
it('does not allow route to activate', () => {
expect(this.activate).toBe(false);
});
it('redirects to /login', () => {
expect(this.navigateSpy).toHaveBeenCalledWith([ '/login' ]);
});
});
describe('user is logged in and ticket is valid', () => {
beforeEach(async(() => {
this.test = new TestConfig({
isLoggedIn: true,
validateTicket: true
});
const { guard, router } = this.test;
guard.canActivate(null, { url: '' }).then((activate) => {
this.activate = activate;
this.navigateSpy = router.navigate;
});
}));
it('allows route to activate', () => {
expect(this.activate).toBe(true);
});
it('does not redirect', () => {
expect(this.navigateSpy).not.toHaveBeenCalled();
});
});
describe('redirect url', () => {
beforeEach(async(() => {
this.test = new TestConfig({
isLoggedIn: false
});
const { guard, auth } = this.test;
guard.canActivate(null, { url: 'some-url' }).then((activate) => {
this.auth = auth;
});
}));
it('should set redirect url', () => {
expect(this.auth.setRedirectUrl).toHaveBeenCalledWith('some-url');
});
});
});

View File

@@ -0,0 +1,57 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
import { AlfrescoApiService } from './alfresco-api.service';
import { AuthenticationService } from './authentication.service';
@Injectable()
export class AuthGuardEcm implements CanActivate {
constructor(
private authService: AuthenticationService,
private apiService: AlfrescoApiService,
private router: Router) {
}
private get authApi() {
return this.apiService.getInstance().ecmAuth;
}
private isLoggedIn(): Promise<boolean> {
if (!this.authApi.isLoggedIn()) {
return Promise.resolve(false);
}
return this.authApi
.validateTicket()
.then(() => true, () => false)
.catch(() => false);
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
return this.isLoggedIn().then(isLoggedIn => {
if (!isLoggedIn) {
this.authService.setRedirectUrl(state.url);
this.router.navigate([ '/login' ]);
}
return isLoggedIn;
});
}
}

View File

@@ -0,0 +1,106 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { async, inject, TestBed } from '@angular/core/testing';
import { Router } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { CookieServiceMock } from './../mock/cookie.service.mock';
import { AlfrescoApiService } from './alfresco-api.service';
import { SettingsService } from './settings.service';
import { AppConfigModule } from '../app-config';
import { AuthGuard } from './auth-guard.service';
import { AuthenticationService } from './authentication.service';
import { CookieService } from './cookie.service';
import { LogService } from './log.service';
import { StorageService } from './storage.service';
import { TranslateLoaderService } from './translate-loader.service';
import { UserPreferencesService } from './user-preferences.service';
describe('AuthGuardService', () => {
let state;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
AppConfigModule,
RouterTestingModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderService
}
})
],
providers: [
AuthGuard,
SettingsService,
AlfrescoApiService,
AuthenticationService,
UserPreferencesService,
StorageService,
{ provide: CookieService, useClass: CookieServiceMock },
LogService
]
}).compileComponents();
}));
beforeEach(() => {
state = { url: '' };
});
it('if the alfresco js api is logged in should canActivate be true',
async(inject([AuthGuard, Router, SettingsService, StorageService, AuthenticationService], (auth, router, settingsService, storage, authService) => {
spyOn(router, 'navigate');
authService.isLoggedIn = () => {
return true;
};
expect(auth.canActivate(null, state)).toBeTruthy();
expect(router.navigate).not.toHaveBeenCalled();
}))
);
it('if the alfresco js api is NOT logged in should canActivate be false',
async(inject([AuthGuard, Router, SettingsService, StorageService, AuthenticationService], (auth, router, settingsService, storage, authService) => {
spyOn(router, 'navigate');
authService.isLoggedIn = () => {
return false;
};
expect(auth.canActivate(null, state)).toBeFalsy();
expect(router.navigate).toHaveBeenCalled();
}))
);
it('should set redirect url',
async(inject([AuthGuard, Router, AuthenticationService], (auth, router, authService) => {
state.url = 'some-url';
spyOn(router, 'navigate');
spyOn(authService, 'setRedirectUrl');
auth.canActivate(null , state);
expect(authService.setRedirectUrl).toHaveBeenCalledWith(state.url);
}))
);
});

View File

@@ -0,0 +1,51 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import {
ActivatedRouteSnapshot, CanActivate, CanActivateChild,
Router,
RouterStateSnapshot
} from '@angular/router';
import { AuthenticationService } from './authentication.service';
@Injectable()
export class AuthGuard implements CanActivate, CanActivateChild {
constructor(private authService: AuthenticationService, private router: Router) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
const redirectUrl = state.url;
return this.checkLogin(redirectUrl);
}
canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
return this.canActivate(route, state);
}
checkLogin(redirectUrl: string): boolean {
if (this.authService.isLoggedIn()) {
return true;
}
this.authService.setRedirectUrl(redirectUrl);
this.router.navigate(['/login']);
return false;
}
}

View File

@@ -0,0 +1,473 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { async, TestBed } from '@angular/core/testing';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { CookieServiceMock } from './../mock/cookie.service.mock';
import { AlfrescoApiService } from './alfresco-api.service';
import { SettingsService } from './settings.service';
import { AppConfigModule } from '../app-config';
import { AuthenticationService } from './authentication.service';
import { CookieService } from './cookie.service';
import { LogService } from './log.service';
import { StorageService } from './storage.service';
import { TranslateLoaderService } from './translate-loader.service';
import { UserPreferencesService } from './user-preferences.service';
declare let jasmine: any;
describe('AuthenticationService', () => {
let apiService: AlfrescoApiService;
let authService: AuthenticationService;
let settingsService: SettingsService;
let preferences: UserPreferencesService;
let storage: StorageService;
let cookie: CookieService;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
AppConfigModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderService
}
})
],
providers: [
SettingsService,
AlfrescoApiService,
AuthenticationService,
StorageService,
UserPreferencesService,
{ provide: CookieService, useClass: CookieServiceMock },
LogService
]
}).compileComponents();
}));
beforeEach(() => {
apiService = TestBed.get(AlfrescoApiService);
authService = TestBed.get(AuthenticationService);
settingsService = TestBed.get(SettingsService);
preferences = TestBed.get(UserPreferencesService);
cookie = TestBed.get(CookieService);
storage = TestBed.get(StorageService);
storage.clear();
jasmine.Ajax.install();
});
afterEach(() => {
jasmine.Ajax.uninstall();
});
describe('remembe me', () => {
beforeEach(() => {
preferences.authType = 'ECM';
});
it('[ECM] should save the remember me cookie as a session cookie after successful login', (done) => {
authService.login('fake-username', 'fake-password', false).subscribe(() => {
expect(cookie['ALFRESCO_REMEMBER_ME']).not.toBeUndefined();
expect(cookie['ALFRESCO_REMEMBER_ME'].expiration).toBeNull();
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 201,
contentType: 'application/json',
responseText: JSON.stringify({'entry': {'id': 'fake-post-ticket', 'userId': 'admin'}})
});
});
it('[ECM] should save the remember me cookie as a persistent cookie after successful login', (done) => {
authService.login('fake-username', 'fake-password', true).subscribe(() => {
expect(cookie['ALFRESCO_REMEMBER_ME']).not.toBeUndefined();
expect(cookie['ALFRESCO_REMEMBER_ME'].expiration).not.toBeNull();
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 201,
contentType: 'application/json',
responseText: JSON.stringify({'entry': {'id': 'fake-post-ticket', 'userId': 'admin'}})
});
});
it('[ECM] should not save the remember me cookie after failed login', (done) => {
authService.login('fake-username', 'fake-password').subscribe(
(res) => {},
(err: any) => {
expect(cookie['ALFRESCO_REMEMBER_ME']).toBeUndefined();
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 403,
contentType: 'application/json',
responseText: JSON.stringify({
'error': {
'errorKey': 'Login failed',
'statusCode': 403,
'briefSummary': '05150009 Login failed',
'stackTrace': 'For security reasons the stack trace is no longer displayed, but the property is kept for previous versions.',
'descriptionURL': 'https://api-explorer.alfresco.com'
}
})
});
});
});
describe('when the setting is ECM', () => {
beforeEach(() => {
preferences.authType = 'ECM';
});
it('[ECM] should return an ECM ticket after the login done', (done) => {
authService.login('fake-username', 'fake-password').subscribe(() => {
expect(authService.isLoggedIn()).toBe(true);
expect(authService.getTicketEcm()).toEqual('fake-post-ticket');
expect(authService.isEcmLoggedIn()).toBe(true);
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 201,
contentType: 'application/json',
responseText: JSON.stringify({'entry': {'id': 'fake-post-ticket', 'userId': 'admin'}})
});
});
it('[ECM] should save only ECM ticket on localStorage', (done) => {
authService.login('fake-username', 'fake-password').subscribe(() => {
expect(authService.isLoggedIn()).toBe(true);
expect(authService.getTicketBpm()).toBeNull();
expect(apiService.getInstance().bpmAuth.isLoggedIn()).toBeFalsy();
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 201,
contentType: 'application/json',
responseText: JSON.stringify({'entry': {'id': 'fake-post-ticket', 'userId': 'admin'}})
});
});
xit('[ECM] should return ticket undefined when the credentials are wrong', (done) => {
authService.login('fake-wrong-username', 'fake-wrong-password').subscribe(
(res) => {
},
(err: any) => {
expect(authService.isLoggedIn()).toBe(false);
expect(authService.getTicketEcm()).toBe(null);
expect(authService.isEcmLoggedIn()).toBe(false);
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 403,
contentType: 'application/json',
responseText: JSON.stringify({
'error': {
'errorKey': 'Login failed',
'statusCode': 403,
'briefSummary': '05150009 Login failed',
'stackTrace': 'For security reasons the stack trace is no longer displayed, but the property is kept for previous versions.',
'descriptionURL': 'https://api-explorer.alfresco.com'
}
})
});
});
it('[ECM] should login in the ECM if no provider are defined calling the login', (done) => {
authService.login('fake-username', 'fake-password').subscribe(() => {
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 201,
contentType: 'application/json',
responseText: JSON.stringify({'entry': {'id': 'fake-post-ticket', 'userId': 'admin'}})
});
});
it('[ECM] should return a ticket undefined after logout', (done) => {
authService.login('fake-username', 'fake-password').subscribe(() => {
authService.logout().subscribe(() => {
expect(authService.isLoggedIn()).toBe(false);
expect(authService.getTicketEcm()).toBe(null);
expect(authService.isEcmLoggedIn()).toBe(false);
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 204
});
});
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 201,
contentType: 'application/json',
responseText: JSON.stringify({'entry': {'id': 'fake-post-ticket', 'userId': 'admin'}})
});
});
it('[ECM] ticket should be deleted only after logout request is accepted', (done) => {
authService.login('fake-username', 'fake-password').subscribe(() => {
let logoutPromise = authService.logout();
expect(authService.getTicketEcm()).toBe('fake-post-ticket');
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 204
});
logoutPromise.subscribe(() => {
expect(authService.isLoggedIn()).toBe(false);
expect(authService.isEcmLoggedIn()).toBe(false);
done();
});
});
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 201,
contentType: 'application/json',
responseText: JSON.stringify({'entry': {'id': 'fake-post-ticket', 'userId': 'admin'}})
});
});
it('[ECM] should return false if the user is not logged in', () => {
expect(authService.isLoggedIn()).toBe(false);
expect(authService.isEcmLoggedIn()).toBe(false);
});
});
describe('when the setting is BPM', () => {
beforeEach(() => {
preferences.authType = 'BPM';
});
it('[BPM] should return an BPM ticket after the login done', (done) => {
authService.login('fake-username', 'fake-password').subscribe(() => {
expect(authService.isLoggedIn()).toBe(true);
expect(authService.getTicketBpm()).toEqual('Basic ZmFrZS11c2VybmFtZTpmYWtlLXBhc3N3b3Jk');
expect(authService.isBpmLoggedIn()).toBe(true);
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 200
});
});
it('[BPM] should save only BPM ticket on localStorage', (done) => {
authService.login('fake-username', 'fake-password').subscribe(() => {
expect(authService.isLoggedIn()).toBe(true);
expect(authService.getTicketEcm()).toBeNull();
expect(apiService.getInstance().ecmAuth.isLoggedIn()).toBeFalsy();
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 201,
contentType: 'application/json',
responseText: JSON.stringify({'entry': {'id': 'fake-post-ticket', 'userId': 'admin'}})
});
});
xit('[BPM] should return ticket undefined when the credentials are wrong', (done) => {
authService.login('fake-wrong-username', 'fake-wrong-password').subscribe(
(res) => {
},
(err: any) => {
expect(authService.isLoggedIn()).toBe(false, 'isLoggedIn');
expect(authService.getTicketBpm()).toBe(null, 'getTicketBpm');
expect(authService.isBpmLoggedIn()).toBe(false, 'isBpmLoggedIn');
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 403
});
});
it('[BPM] ticket should be deleted only after logout request is accepted', (done) => {
authService.login('fake-username', 'fake-password').subscribe(() => {
let logoutPromise = authService.logout();
expect(authService.getTicketBpm()).toBe('Basic ZmFrZS11c2VybmFtZTpmYWtlLXBhc3N3b3Jk');
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 200
});
logoutPromise.subscribe(() => {
expect(authService.isLoggedIn()).toBe(false);
expect(authService.isBpmLoggedIn()).toBe(false);
done();
});
});
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 200
});
});
it('[BPM] should return a ticket undefined after logout', (done) => {
authService.login('fake-username', 'fake-password').subscribe(() => {
authService.logout().subscribe(() => {
expect(authService.isLoggedIn()).toBe(false);
expect(authService.getTicketBpm()).toBe(null);
expect(authService.isBpmLoggedIn()).toBe(false);
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 200
});
});
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 200
});
});
it('[BPM] should return an error when the logout return error', (done) => {
authService.logout().subscribe(
(res) => {
},
(err: any) => {
expect(err).toBeDefined();
expect(authService.getTicketBpm()).toBe(null);
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 403
});
});
});
describe('when the setting is both ECM and BPM ', () => {
beforeEach(() => {
preferences.authType = 'ALL';
});
it('[ALL] should return both ECM and BPM tickets after the login done', (done) => {
authService.login('fake-username', 'fake-password').subscribe(() => {
expect(authService.isLoggedIn()).toBe(true);
expect(authService.getTicketEcm()).toEqual('fake-post-ticket');
expect(authService.getTicketBpm()).toEqual('Basic ZmFrZS11c2VybmFtZTpmYWtlLXBhc3N3b3Jk');
expect(authService.isBpmLoggedIn()).toBe(true);
expect(authService.isEcmLoggedIn()).toBe(true);
done();
});
jasmine.Ajax.requests.at(0).respondWith({
'status': 201,
contentType: 'application/json',
responseText: JSON.stringify({'entry': {'id': 'fake-post-ticket', 'userId': 'admin'}})
});
jasmine.Ajax.requests.at(1).respondWith({
'status': 200
});
});
xit('[ALL] should return login fail if only ECM call fail', (done) => {
authService.login('fake-username', 'fake-password').subscribe(
(res) => {
},
(err: any) => {
expect(authService.isLoggedIn()).toBe(false, 'isLoggedIn');
expect(authService.getTicketEcm()).toBe(null, 'getTicketEcm');
expect(authService.getTicketBpm()).toBe(null, 'getTicketBpm');
expect(authService.isEcmLoggedIn()).toBe(false, 'isEcmLoggedIn');
done();
});
jasmine.Ajax.requests.at(0).respondWith({
'status': 403
});
jasmine.Ajax.requests.at(1).respondWith({
'status': 200
});
});
xit('[ALL] should return login fail if only BPM call fail', (done) => {
authService.login('fake-username', 'fake-password').subscribe(
(res) => {
},
(err: any) => {
expect(authService.isLoggedIn()).toBe(false);
expect(authService.getTicketEcm()).toBe(null);
expect(authService.getTicketBpm()).toBe(null);
expect(authService.isBpmLoggedIn()).toBe(false);
done();
});
jasmine.Ajax.requests.at(0).respondWith({
'status': 201,
contentType: 'application/json',
responseText: JSON.stringify({'entry': {'id': 'fake-post-ticket', 'userId': 'admin'}})
});
jasmine.Ajax.requests.at(1).respondWith({
'status': 403
});
});
xit('[ALL] should return ticket undefined when the credentials are wrong', (done) => {
authService.login('fake-username', 'fake-password').subscribe(
(res) => {
},
(err: any) => {
expect(authService.isLoggedIn()).toBe(false);
expect(authService.getTicketEcm()).toBe(null);
expect(authService.getTicketBpm()).toBe(null);
expect(authService.isBpmLoggedIn()).toBe(false);
expect(authService.isEcmLoggedIn()).toBe(false);
done();
});
jasmine.Ajax.requests.at(0).respondWith({
'status': 403
});
jasmine.Ajax.requests.at(1).respondWith({
'status': 403
});
});
});
it('should set/get redirectUrl', () => {
authService.setRedirectUrl('some-url');
expect(authService.getRedirectUrl()).toBe('some-url');
});
});

View File

@@ -0,0 +1,249 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs/Rx';
import { AlfrescoApiService } from './alfresco-api.service';
import { CookieService } from './cookie.service';
import { LogService } from './log.service';
import { StorageService } from './storage.service';
import { UserPreferencesService } from './user-preferences.service';
const REMEMBER_ME_COOKIE_KEY = 'ALFRESCO_REMEMBER_ME';
const REMEMBER_ME_UNTIL = 1000 * 60 * 60 * 24 * 30 ;
@Injectable()
export class AuthenticationService {
private redirectUrl: string = '';
onLogin: Subject<any> = new Subject<any>();
onLogout: Subject<any> = new Subject<any>();
constructor(
private preferences: UserPreferencesService,
private alfrescoApi: AlfrescoApiService,
private storage: StorageService,
private cookie: CookieService,
private logService: LogService) {
}
/**
* The method return true if the user is logged in
* @returns {boolean}
*/
isLoggedIn(): boolean {
return !!this.alfrescoApi.getInstance().isLoggedIn();
}
/**
* Method to delegate to POST login
* @param username
* @param password
* @returns {Observable<R>|Observable<T>}
*/
login(username: string, password: string, rememberMe: boolean = false): Observable<{ type: string, ticket: any }> {
this.removeTicket();
return Observable.fromPromise(this.callApiLogin(username, password))
.map((response: any) => {
this.saveRememberMeCookie(rememberMe);
this.saveTickets();
this.onLogin.next(response);
return {
type: this.preferences.authType,
ticket: response
};
})
.catch(err => this.handleError(err));
}
/**
* The method save the "remember me" cookie as a long life cookie or a session cookie
* depending on the given paramter
*/
private saveRememberMeCookie(rememberMe: boolean): void {
let expiration = null;
if (rememberMe) {
expiration = new Date();
const time = expiration.getTime();
const expireTime = time + REMEMBER_ME_UNTIL;
expiration.setTime(expireTime);
}
this.cookie.setItem(REMEMBER_ME_COOKIE_KEY, '1', expiration, null);
}
/**
* The method retrieve whether the "remember me" cookie was set or not
*
* @returns {boolean}
*/
private isRememberMeSet(): boolean {
return (this.cookie.getItem(REMEMBER_ME_COOKIE_KEY) === null) ? false : true;
}
/**
* Initialize the alfresco Api with user and password end call the login method
* @param username
* @param password
* @returns {*|Observable<any>}
*/
private callApiLogin(username: string, password: string) {
return this.alfrescoApi.getInstance().login(username, password);
}
/**
* The method remove the ticket from the Storage
*
* @returns {Observable<R>|Observable<T>}
*/
logout() {
return Observable.fromPromise(this.callApiLogout())
.do(response => {
this.removeTicket();
this.onLogout.next(response);
return response;
})
.catch(err => this.handleError(err));
}
/**
*
* @returns {*|Observable<string>|Observable<any>|Promise<T>}
*/
private callApiLogout(): Promise<any> {
if (this.alfrescoApi.getInstance()) {
return this.alfrescoApi.getInstance().logout();
}
}
/**
* Remove the login ticket from Storage
*/
removeTicket(): void {
this.storage.removeItem('ticket-ECM');
this.storage.removeItem('ticket-BPM');
this.alfrescoApi.getInstance().setTicket(undefined, undefined);
}
/**
* The method return the ECM ticket stored in the Storage
* @returns ticket
*/
getTicketEcm(): string | null {
return this.storage.getItem('ticket-ECM');
}
/**
* The method return the BPM ticket stored in the Storage
* @returns ticket
*/
getTicketBpm(): string | null {
return this.storage.getItem('ticket-BPM');
}
getTicketEcmBase64(): string | null {
let ticket = this.storage.getItem('ticket-ECM');
if (ticket) {
return 'Basic ' + btoa(ticket);
}
return null;
}
/**
* The method save the ECM and BPM ticket in the Storage
*/
saveTickets(): void {
this.saveTicketEcm();
this.saveTicketBpm();
}
/**
* The method save the ECM ticket in the Storage
*/
saveTicketEcm(): void {
if (this.alfrescoApi.getInstance() && this.alfrescoApi.getInstance().getTicketEcm()) {
this.storage.setItem('ticket-ECM', this.alfrescoApi.getInstance().getTicketEcm());
}
}
/**
* The method save the BPM ticket in the Storage
*/
saveTicketBpm(): void {
if (this.alfrescoApi.getInstance() && this.alfrescoApi.getInstance().getTicketBpm()) {
this.storage.setItem('ticket-BPM', this.alfrescoApi.getInstance().getTicketBpm());
}
}
/**
* The method return true if user is logged in on ecm provider
*
* @returns {boolean}
*/
isEcmLoggedIn(): boolean {
return this.isRememberMeSet() && this.alfrescoApi.getInstance().ecmAuth && !!this.alfrescoApi.getInstance().ecmAuth.isLoggedIn();
}
/**
* The method return true if user is logged in on bpm provider
*
* @returns {boolean}
*/
isBpmLoggedIn(): boolean {
return this.isRememberMeSet() && this.alfrescoApi.getInstance().bpmAuth && !!this.alfrescoApi.getInstance().bpmAuth.isLoggedIn();
}
/**
* Get the ECM username
*
* @returns {string} The username value
*
* @memberof AuthenticationService
*/
getEcmUsername(): string {
return this.alfrescoApi.getInstance().ecmAuth.username;
}
/**
* Get the BPM username
*
* @returns {string} The username value
*
* @memberof AuthenticationService
*/
getBpmUsername(): string {
return this.alfrescoApi.getInstance().bpmAuth.username;
}
setRedirectUrl(url: string) {
this.redirectUrl = url;
}
getRedirectUrl(): string {
return this.redirectUrl;
}
/**
* The method write the error in the console browser
* @param error
* @returns {ErrorObservable}
*/
handleError(error: any): Observable<any> {
this.logService.error('Error when logging in', error);
return Observable.throw(error || 'Server error');
}
}

View File

@@ -0,0 +1,34 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable, Type } from '@angular/core';
import { CardViewDateItemComponent } from '../card-view/card-view-dateitem.component';
import { CardViewMapItemComponent } from '../card-view/card-view-mapitem.component';
import { CardViewTextItemComponent } from '../card-view/card-view-textitem.component';
import { DynamicComponentMapper, DynamicComponentResolveFunction, DynamicComponentResolver } from '../services/dynamic-component-mapper.service';
@Injectable()
export class CardItemTypeService extends DynamicComponentMapper {
protected defaultValue: Type<{}> = CardViewTextItemComponent;
protected types: { [key: string]: DynamicComponentResolveFunction } = {
'text': DynamicComponentResolver.fromType(CardViewTextItemComponent),
'date': DynamicComponentResolver.fromType(CardViewDateItemComponent),
'map': DynamicComponentResolver.fromType(CardViewMapItemComponent)
};
}

View File

@@ -0,0 +1,54 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { CardViewBaseItemModel } from '../models/card-view-baseitem.model';
export interface UpdateNotification {
target: any;
changed: any;
}
export interface ClickNotification {
target: any;
}
@Injectable()
export class CardViewUpdateService {
// Observable sources
private itemUpdatedSource = new Subject<UpdateNotification>();
// Observable streams
public itemUpdated$ = this.itemUpdatedSource.asObservable();
public itemClicked$: Subject<ClickNotification> = new Subject<ClickNotification>();
update(property: CardViewBaseItemModel, changed: any) {
this.itemUpdatedSource.next({
target: property,
changed
});
}
clicked(property: CardViewBaseItemModel) {
this.itemClicked$.next({
target: property
});
}
}

View File

@@ -0,0 +1,194 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { async, TestBed } from '@angular/core/testing';
import { CommentProcessModel } from '../models';
import { AppConfigServiceMock } from '../mock/app-config.service.mock';
import { fakeProcessComment, fakeTasksComment, fakeUser1 } from '../mock/comment-process-service.mock';
import { AlfrescoApiService } from './alfresco-api.service';
import { AppConfigService } from '../app-config/app-config.service';
import { CommentProcessService } from './comment-process.service';
import { LogService } from './log.service';
import { StorageService } from './storage.service';
declare let jasmine: any;
describe('Comment ProcessService Service', () => {
let service: CommentProcessService;
let alfrescoApi: any;
beforeEach((() => {
TestBed.configureTestingModule({
providers: [
CommentProcessService,
AlfrescoApiService,
StorageService,
LogService,
{provide: AppConfigService, useClass: AppConfigServiceMock}
]
}).compileComponents();
}));
beforeEach(() => {
service = TestBed.get(CommentProcessService);
alfrescoApi = TestBed.get(AlfrescoApiService).getInstance();
});
beforeEach(() => {
jasmine.Ajax.install();
});
afterEach(() => {
jasmine.Ajax.uninstall();
});
describe('Process comments', () => {
const processId = '1001';
describe('get comments', () => {
let getProcessInstanceComments: jasmine.Spy;
beforeEach(() => {
getProcessInstanceComments = spyOn(alfrescoApi.activiti.commentsApi, 'getProcessInstanceComments')
.and
.returnValue(Promise.resolve({data: [fakeProcessComment, fakeProcessComment]}));
});
it('should return the correct number of comments', async(() => {
service.getProcessInstanceComments(processId).subscribe((tasks) => {
expect(tasks.length).toBe(2);
});
}));
it('should return the correct comment data', async(() => {
service.getProcessInstanceComments(processId).subscribe((comments) => {
let comment: any = comments[0];
expect(comment.id).toBe(fakeProcessComment.id);
expect(comment.created).toBe(fakeProcessComment.created);
expect(comment.message).toBe(fakeProcessComment.message);
expect(comment.createdBy.id).toBe(fakeProcessComment.createdBy.id);
});
}));
it('should call service to fetch process instance comments', () => {
service.getProcessInstanceComments(processId);
expect(getProcessInstanceComments).toHaveBeenCalledWith(processId);
});
it('should return a default error if no data is returned by the API', async(() => {
getProcessInstanceComments = getProcessInstanceComments.and.returnValue(Promise.reject(null));
service.getProcessInstanceComments(processId).subscribe(
() => {
},
(res) => {
expect(res).toBe('Server error');
}
);
}));
});
describe('add comment', () => {
const message = 'Test message';
let addProcessInstanceComment: jasmine.Spy;
beforeEach(() => {
addProcessInstanceComment = spyOn(alfrescoApi.activiti.commentsApi, 'addProcessInstanceComment')
.and
.returnValue(Promise.resolve(fakeProcessComment));
});
it('should call service to add comment', () => {
service.addProcessInstanceComment(processId, message);
expect(addProcessInstanceComment).toHaveBeenCalledWith({
message: message
}, processId);
});
it('should return the created comment', async(() => {
service.addProcessInstanceComment(processId, message).subscribe((comment) => {
expect(comment.id).toBe(fakeProcessComment.id);
expect(comment.created).toBe(fakeProcessComment.created);
expect(comment.message).toBe(fakeProcessComment.message);
expect(comment.createdBy).toBe(fakeProcessComment.createdBy);
});
}));
it('should return a default error if no data is returned by the API', async(() => {
addProcessInstanceComment = addProcessInstanceComment.and.returnValue(Promise.reject(null));
service.addProcessInstanceComment(processId, message).subscribe(
() => {
},
(res) => {
expect(res).toBe('Server error');
}
);
}));
});
});
describe('Task comments', () => {
it('should add a comment task ', (done) => {
service.addTaskComment('999', 'fake-comment-message').subscribe(
(res: CommentProcessModel) => {
expect(res).toBeDefined();
expect(res.id).not.toEqual(null);
expect(res.message).toEqual('fake-comment-message');
expect(res.created).not.toEqual(null);
expect(res.createdBy.email).toEqual('fake-email@dom.com');
expect(res.createdBy.firstName).toEqual('firstName');
expect(res.createdBy.lastName).toEqual('lastName');
done();
}
);
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 200,
contentType: 'application/json',
responseText: JSON.stringify({
id: '111', message: 'fake-comment-message',
createdBy: fakeUser1,
created: '2016-07-15T11:19:17.440+0000'
})
});
});
it('should return the tasks comments ', (done) => {
service.getTaskComments('999').subscribe(
(res: CommentProcessModel[]) => {
expect(res).toBeDefined();
expect(res.length).toEqual(2);
expect(res[0].message).toEqual('fake-message-1');
expect(res[1].message).toEqual('fake-message-2');
done();
}
);
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 200,
contentType: 'application/json',
responseText: JSON.stringify(fakeTasksComment)
});
});
});
});

View File

@@ -0,0 +1,82 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import { CommentProcessModel } from '../models/comment-process.model';
import { UserProcessModel } from '../models/user-process.model';
import { AlfrescoApiService } from './alfresco-api.service';
import { LogService } from './log.service';
@Injectable()
export class CommentProcessService {
constructor(private apiService: AlfrescoApiService,
private logService: LogService) {
}
addTaskComment(taskId: string, message: string): Observable<CommentProcessModel> {
return Observable.fromPromise(this.apiService.getInstance().activiti.taskApi.addTaskComment({message: message}, taskId))
.map(res => res)
.map((response: CommentProcessModel) => {
return new CommentProcessModel({id: response.id, message: response.message, created: response.created, createdBy: response.createdBy});
}).catch(err => this.handleError(err));
}
getTaskComments(taskId: string): Observable<CommentProcessModel[]> {
return Observable.fromPromise(this.apiService.getInstance().activiti.taskApi.getTaskComments(taskId))
.map(res => res)
.map((response: any) => {
let comments: CommentProcessModel[] = [];
response.data.forEach((comment: CommentProcessModel) => {
let user = new UserProcessModel(comment.createdBy);
comments.push(new CommentProcessModel({id: comment.id, message: comment.message, created: comment.created, createdBy: user}));
});
return comments;
}).catch(err => this.handleError(err));
}
getProcessInstanceComments(processInstanceId: string): Observable<CommentProcessModel[]> {
return Observable.fromPromise(this.apiService.getInstance().activiti.commentsApi.getProcessInstanceComments(processInstanceId))
.map(res => res)
.map((response: any) => {
let comments: CommentProcessModel[] = [];
response.data.forEach((comment: CommentProcessModel) => {
let user = new UserProcessModel(comment.createdBy);
comments.push(new CommentProcessModel({id: comment.id, message: comment.message, created: comment.created, createdBy: user}));
});
return comments;
}).catch(err => this.handleError(err));
}
addProcessInstanceComment(processInstanceId: string, message: string): Observable<CommentProcessModel> {
return Observable.fromPromise(
this.apiService.getInstance().activiti.commentsApi.addProcessInstanceComment({ message: message }, processInstanceId)
)
.map((response: CommentProcessModel) => {
return new CommentProcessModel({id: response.id, message: response.message, created: response.created, createdBy: response.createdBy});
}).catch(err => this.handleError(err));
}
private handleError(error: any) {
this.logService.error(error);
return Observable.throw(error || 'Server error');
}
}

View File

@@ -0,0 +1,168 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { async, TestBed } from '@angular/core/testing';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { CookieServiceMock } from '../mock/cookie.service.mock';
import { AlfrescoApiService } from './alfresco-api.service';
import { ContentService } from './content.service';
import { SettingsService } from './settings.service';
import { AppConfigService } from '../app-config/app-config.service';
import { AppConfigModule } from '../app-config/app-config.module';
import { AuthenticationService } from './authentication.service';
import { CookieService } from './cookie.service';
import { LogService } from './log.service';
import { StorageService } from './storage.service';
import { TranslateLoaderService } from './translate-loader.service';
import { UserPreferencesService } from './user-preferences.service';
declare let jasmine: any;
describe('ContentService', () => {
let contentService: ContentService;
let authService: AuthenticationService;
let settingsService: SettingsService;
let storage: StorageService;
let node: any;
const nodeId = 'fake-node-id';
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
AppConfigModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderService
}
})
],
declarations: [],
providers: [
AlfrescoApiService,
ContentService,
AuthenticationService,
SettingsService,
StorageService,
UserPreferencesService,
{provide: CookieService, useClass: CookieServiceMock},
LogService
]
}).compileComponents();
}));
beforeEach(() => {
authService = TestBed.get(AuthenticationService);
settingsService = TestBed.get(SettingsService);
contentService = TestBed.get(ContentService);
storage = TestBed.get(StorageService);
storage.clear();
node = {
entry: {
id: nodeId
}
};
jasmine.Ajax.install();
let appConfig: AppConfigService = TestBed.get(AppConfigService);
appConfig.config = {
ecmHost: 'http://localhost:9876/ecm'
};
});
afterEach(() => {
jasmine.Ajax.uninstall();
});
beforeEach(() => {
settingsService.setProviders('ECM');
});
it('should return a valid content URL', (done) => {
authService.login('fake-username', 'fake-password').subscribe(() => {
expect(contentService.getContentUrl(node)).toBe('http://localhost:9876/ecm/alfresco/api/' +
'-default-/public/alfresco/versions/1/nodes/fake-node-id/content?attachment=false&alf_ticket=fake-post-ticket');
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 201,
contentType: 'application/json',
responseText: JSON.stringify({'entry': {'id': 'fake-post-ticket', 'userId': 'admin'}})
});
});
it('should return a valid thumbnail URL', (done) => {
authService.login('fake-username', 'fake-password').subscribe(() => {
expect(contentService.getDocumentThumbnailUrl(node))
.toBe('http://localhost:9876/ecm/alfresco/api/-default-/public/alfresco' +
'/versions/1/nodes/fake-node-id/renditions/doclib/content?attachment=false&alf_ticket=fake-post-ticket');
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 201,
contentType: 'application/json',
responseText: JSON.stringify({'entry': {'id': 'fake-post-ticket', 'userId': 'admin'}})
});
});
it('should havePermission be false if allowableOperation is not present in the node', () => {
let permissionNode = {};
expect(contentService.hasPermission(permissionNode, 'create')).toBeFalsy();
});
it('should havePermission be true if allowableOperation is present and you have the permission for the request operation', () => {
let permissionNode = {allowableOperations: ['delete', 'update', 'create', 'updatePermissions']};
expect(contentService.hasPermission(permissionNode, 'create')).toBeTruthy();
});
it('should havePermission be false if allowableOperation is present but you don\'t have the permission for the request operation', () => {
let permissionNode = {allowableOperations: ['delete', 'update', 'updatePermissions']};
expect(contentService.hasPermission(permissionNode, 'create')).toBeFalsy();
});
it('should havePermission works in the opposite way with negate value', () => {
let permissionNode = {allowableOperations: ['delete', 'update', 'updatePermissions']};
expect(contentService.hasPermission(permissionNode, '!create')).toBeTruthy();
});
it('should havePermission return false id no permission parameter are passed', () => {
let permissionNode = {allowableOperations: ['delete', 'update', 'updatePermissions']};
expect(contentService.hasPermission(permissionNode, null)).toBeFalsy();
});
describe('Download blob', () => {
it('Should use native msSaveOrOpenBlob if the browser is IE', (done) => {
let navigatorAny: any = window.navigator;
navigatorAny.__defineGetter__('msSaveOrOpenBlob', () => {
done();
});
let blob = new Blob([''], {type: 'text/html'});
contentService.downloadBlob(blob, 'test_ie');
});
});
});

View File

@@ -0,0 +1,231 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { ContentApi, MinimalNodeEntryEntity } from 'alfresco-js-api';
import { Observable, Subject } from 'rxjs/Rx';
import { FolderCreatedEvent } from '../events/folder-created.event';
import { PermissionsEnum } from '../models/permissions.enum';
import { AlfrescoApiService } from './alfresco-api.service';
import { AuthenticationService } from './authentication.service';
import { LogService } from './log.service';
@Injectable()
export class ContentService {
private saveData: Function;
folderCreated: Subject<FolderCreatedEvent> = new Subject<FolderCreatedEvent>();
folderCreate: Subject<MinimalNodeEntryEntity> = new Subject<MinimalNodeEntryEntity>();
folderEdit: Subject<MinimalNodeEntryEntity> = new Subject<MinimalNodeEntryEntity>();
constructor(public authService: AuthenticationService,
public apiService: AlfrescoApiService,
private logService: LogService,
private sanitizer: DomSanitizer) {
this.saveData = (function () {
let a = document.createElement('a');
document.body.appendChild(a);
a.style.display = 'none';
return function (data, format, fileName) {
let blob = null;
if (format === 'blob' || format === 'data') {
blob = new Blob([data], { type: 'octet/stream' });
}
if (format === 'object' || format === 'json') {
let json = JSON.stringify(data);
blob = new Blob([json], { type: 'octet/stream' });
}
if (blob) {
if (typeof window.navigator !== 'undefined' && window.navigator.msSaveOrOpenBlob) {
navigator.msSaveOrOpenBlob(blob, fileName);
} else {
let url = window.URL.createObjectURL(blob);
a.href = url;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);
}
}
};
}());
}
/**
* Invokes content download for a Blob with a file name.
*
* @param {Blob} blob Content to download.
* @param {string} fileName Name of the resulting file.
*
* @memberOf ContentService
*/
downloadBlob(blob: Blob, fileName: string): void {
this.saveData(blob, 'blob', fileName);
}
/**
* Invokes content download for a data array with a file name.
*
* @param {*} data Data to download.
* @param {string} fileName Name of the resulting file.
*
* @memberOf ContentService
*/
downloadData(data: any, fileName: string): void {
this.saveData(data, 'data', fileName);
}
/**
* Invokes content download for a JSON object with a file name.
*
* @param {*} json JSON object to download.
* @param {any} fileName Name of the resulting file.
*
* @memberOf ContentService
*/
downloadJSON(json: any, fileName): void {
this.saveData(json, 'json', fileName);
}
/**
* Creates a trusted object URL from the Blob.
* WARNING: calling this method with untrusted user data exposes your application to XSS security risks!
* @param {Blob} blob Data to wrap into object URL
* @returns {string} Object URL content.
*
* @memberOf ContentService
*/
createTrustedUrl(blob: Blob): string {
let url = window.URL.createObjectURL(blob);
return <string> this.sanitizer.bypassSecurityTrustUrl(url);
}
private get contentApi(): ContentApi {
return this.apiService.getInstance().content;
}
/**
* Get thumbnail URL for the given document node.
*
* @param {string|MinimalNodeEntity} nodeId Node to get URL for.
* @param {boolean} [attachment] Retrieve content as an attachment for download
* @param {string} [ticket] Custom ticket to use for authentication
* @returns {string} The URL address pointing to the content.
*/
getDocumentThumbnailUrl(nodeId: any, attachment?: boolean, ticket?: string): string {
if (nodeId && nodeId.entry) {
nodeId = nodeId.entry.id;
}
return this.contentApi.getDocumentThumbnailUrl(nodeId, attachment, ticket);
}
/**
* Get content URL for the given node.
*
* @param nodeId {string|MinimalNodeEntity} Node to get URL for.
* @param {boolean} [attachment] Retrieve content as an attachment for download
* @param {string} [ticket] Custom ticket to use for authentication
* @returns {string} The URL address pointing to the content.
*/
getContentUrl(nodeId: any, attachment?: boolean, ticket?: string): string {
if (nodeId && nodeId.entry) {
nodeId = nodeId.entry.id;
}
return this.contentApi.getContentUrl(nodeId, attachment, ticket);
}
/**
* Get content for the given node.
* @param nodeId {string}.
*
* @returns {Observable<any>} URL address.
*/
getNodeContent(nodeId: string): Observable<any> {
return Observable.fromPromise(this.apiService.getInstance().core.nodesApi.getFileContent(nodeId).then((dataContent) => {
return dataContent;
})).catch(this.handleError);
}
/**
* Create a folder
* @param name - the folder name
*/
createFolder(relativePath: string, name: string, parentId?: string): Observable<FolderCreatedEvent> {
return Observable.fromPromise(this.apiService.getInstance().nodes.createFolder(name, relativePath, parentId))
.do(data => {
this.folderCreated.next(<FolderCreatedEvent> {
relativePath: relativePath,
name: name,
parentId: parentId,
node: data
});
})
.catch(err => this.handleError(err));
}
/**
* Check if the user has permissions on that node
* @param MinimalNode - node to check allowableOperations
* @param PermissionsEnum - create, delete, update, updatePermissions, !create, !delete, !update, !updatePermissions
*
* @returns {boolean} has permission
*/
hasPermission(node: any, permission: PermissionsEnum | string): boolean {
let hasPermission = false;
if (this.hasAllowableOperations(node)) {
if (permission && permission.startsWith('!')) {
hasPermission = node.allowableOperations.find(currentPermission => currentPermission === permission.replace('!', '')) ? false : true;
} else {
hasPermission = node.allowableOperations.find(currentPermission => currentPermission === permission) ? true : false;
}
} else {
if (permission && permission.startsWith('!')) {
hasPermission = true;
}
}
return hasPermission;
}
/**
* Check if the node has the properties allowableOperations
* @param MinimalNode - node to check allowableOperations
*
* @returns {boolean} has AllowableOperations
*/
hasAllowableOperations(node: any): boolean {
return node && node.allowableOperations ? true : false;
}
private handleError(error: any) {
this.logService.error(error);
return Observable.throw(error || 'Server error');
}
}

View File

@@ -0,0 +1,48 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
@Injectable()
export class CookieService {
/**
* Retrieve cookie by key.
*
* @returns {string | null}
*/
getItem(key: string): string | null {
const regexp = new RegExp('(?:' + key + '|;\s*' + key + ')=(.*?)(?:;|$)', 'g');
const result = regexp.exec(document.cookie);
return (result === null) ? null : result[1];
}
/**
* Set a cookie.
* @param key
* @param data
* @param expiration
* @param path
*
* @returns {boolean}
*/
setItem(key: string, data: string, expiration: Date | null, path: string | null): void {
document.cookie = `${key}=${data}` +
(expiration ? ';expires=' + expiration.toUTCString() : '') +
(path ? `;path=${path}` : ';path=/');
}
}

View File

@@ -0,0 +1,54 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import { NodePaging } from 'alfresco-js-api';
import { AlfrescoApiService } from './alfresco-api.service';
import { UserPreferencesService } from './user-preferences.service';
@Injectable()
export class DeletedNodesApiService {
constructor(
private apiService: AlfrescoApiService,
private preferences: UserPreferencesService
) {}
private get nodesApi() {
return this.apiService.getInstance().core.nodesApi;
}
getDeletedNodes(options?: Object): Observable<NodePaging> {
const { nodesApi, handleError } = this;
const defaultOptions = {
include: [ 'path', 'properties' ],
maxItems: this.preferences.paginationSize,
skipCount: 0
};
const queryOptions = Object.assign(defaultOptions, options);
const promise = nodesApi.getDeletedNodes(queryOptions);
return Observable
.fromPromise(promise)
.catch(handleError);
}
private handleError(error: any): Observable<any> {
return Observable.of(error);
}
}

View File

@@ -0,0 +1,193 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { async, TestBed } from '@angular/core/testing';
import { BpmProductVersionModel, EcmProductVersionModel } from '../models/product-version.model';
import { AlfrescoApiService } from './alfresco-api.service';
import { SettingsService } from './settings.service';
import { AppConfigService } from '../app-config/app-config.service';
import { AppConfigModule } from '../app-config/app-config.module';
import { AuthenticationService } from './authentication.service';
import { DiscoveryApiService } from './discovery-api.service';
import { StorageService } from './storage.service';
import { UserPreferencesService } from './user-preferences.service';
declare let jasmine: any;
let fakeEcmDiscoveryResponse: any = {
'entry': {
'repository': {
'edition': 'FAKE',
'version': {
'major': '5',
'minor': '2',
'patch': '0',
'hotfix': '0',
'schema': 999999,
'label': 'r134899-b26',
'display': '5.2.0.0 (r134899-b26) schema 10005'
},
'license': {
'issuedAt': '2017-06-22T10:56:45.796+0000',
'expiresAt': '2017-07-22T00:00:00.000+0000',
'remainingDays': 4,
'holder': 'Trial User',
'mode': 'ENTERPRISE',
'entitlements': {
'isClusterEnabled': false,
'isCryptodocEnabled': false
}
},
'status': {
'isReadOnly': false,
'isAuditEnabled': true,
'isQuickShareEnabled': true,
'isThumbnailGenerationEnabled': true
},
'modules': [
{
'id': 'alfresco-fake-services',
'title': 'Alfresco Share Services AMP',
'description': 'Module to be applied to alfresco.war, containing APIs for Alfresco Share',
'version': '5.2.0',
'installDate': '2017-03-07T08:48:14.161+0000',
'installState': 'INSTALLED',
'versionMin': '5.1',
'versionMax': '999'
},
{
'id': 'alfresco-trashcan-fake',
'title': 'alfresco-trashcan-cleaner project',
'description': 'The Alfresco Trash Can Cleaner (Alfresco Module)',
'version': '2.2',
'installState': 'UNKNOWN',
'versionMin': '0',
'versionMax': '999'
}
]
}
}
};
let fakeBPMDiscoveryResponse: any = {
'revisionVersion': '2',
'edition': 'SUPER FAKE EDITION',
'type': 'bpmSuite',
'majorVersion': '1',
'minorVersion': '6'
};
describe('Discovery Api Service', () => {
let service;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
AppConfigModule
],
providers: [
DiscoveryApiService,
AlfrescoApiService,
UserPreferencesService,
AuthenticationService,
SettingsService,
StorageService
]
}).compileComponents();
}));
beforeEach(() => {
let appConfig: AppConfigService = TestBed.get(AppConfigService);
appConfig.config = {
ecmHost: 'http://localhost:9876/ecm'
};
service = TestBed.get(DiscoveryApiService);
jasmine.Ajax.install();
});
afterEach(() => {
jasmine.Ajax.uninstall();
});
describe('For ECM', () => {
it('Should retrieve the info about the product for ECM', (done) => {
service.getEcmProductInfo().subscribe((data: EcmProductVersionModel) => {
expect(data).not.toBeNull();
expect(data.edition).toBe('FAKE');
expect(data.version.schema).toBe(999999);
expect(data.license.isClusterEnabled).toBeFalsy();
expect(data.status.isQuickShareEnabled).toBeTruthy();
expect(data.modules.length).toBe(2);
expect(data.modules[0].id).toBe('alfresco-fake-services');
expect(data.modules[1].id).toBe('alfresco-trashcan-fake');
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: fakeEcmDiscoveryResponse
});
});
it('getEcmProductInfo catch errors call', (done) => {
service.getEcmProductInfo().subscribe(
() => {
},
() => {
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 403
});
});
});
describe('For BPM', () => {
it('Should retrieve the info about the product for BPM', (done) => {
service.getBpmProductInfo().subscribe((data: BpmProductVersionModel) => {
expect(data).not.toBeNull();
expect(data.edition).toBe('SUPER FAKE EDITION');
expect(data.revisionVersion).toBe('2');
expect(data.type).toBe('bpmSuite');
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: fakeBPMDiscoveryResponse
});
});
it('getBpmProductInfo catch errors call', (done) => {
service.getBpmProductInfo().subscribe(
() => {
},
() => {
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 403
});
});
});
});

View File

@@ -0,0 +1,45 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import { BpmProductVersionModel, EcmProductVersionModel } from '../models/product-version.model';
import { AlfrescoApiService } from './alfresco-api.service';
@Injectable()
export class DiscoveryApiService {
constructor(private apiService: AlfrescoApiService) { }
public getEcmProductInfo() {
return Observable.fromPromise(
this.apiService.getInstance().discovery.discoveryApi.getRepositoryInformation())
.map(res => new EcmProductVersionModel(res))
.catch(this.handleError);
}
public getBpmProductInfo() {
return Observable.fromPromise(
this.apiService.getInstance().activiti.aboutApi.getAppVersion())
.map(res => new BpmProductVersionModel(res))
.catch(this.handleError);
}
private handleError(error): Observable<any> {
return Observable.throw(error);
}
}

View File

@@ -0,0 +1,66 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Type } from '@angular/core';
export interface DynamicComponentModel { type: string; }
export type DynamicComponentResolveFunction = (model: DynamicComponentModel) => Type<{}>;
export class DynamicComponentResolver {
static fromType(type: Type<{}>): DynamicComponentResolveFunction {
return (model: DynamicComponentModel) => {
return type;
};
}
}
export abstract class DynamicComponentMapper {
protected defaultValue: Type<{}> = undefined;
protected types: { [key: string]: DynamicComponentResolveFunction } = {};
getComponentTypeResolver(type: string, defaultValue: Type<{}> = this.defaultValue): DynamicComponentResolveFunction {
if (type) {
return this.types[type] || DynamicComponentResolver.fromType(defaultValue);
}
return DynamicComponentResolver.fromType(defaultValue);
}
setComponentTypeResolver(type: string, resolver: DynamicComponentResolveFunction, override: boolean = false) {
if (!type) {
throw new Error(`type is null or not defined`);
}
if (!resolver) {
throw new Error(`resolver is null or not defined`);
}
let existing = this.types[type];
if (existing && !override) {
throw new Error(`already mapped, use override option if you intend replacing existing mapping.`);
}
this.types[type] = resolver;
}
resolveComponentType(model: DynamicComponentModel, defaultValue: Type<{}> = this.defaultValue): Type<{}> {
if (model) {
let resolver = this.getComponentTypeResolver(model.type, defaultValue);
return resolver(model);
}
return defaultValue;
}
}

View File

@@ -0,0 +1,85 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { NodePaging } from 'alfresco-js-api';
import { Observable } from 'rxjs/Rx';
import { AlfrescoApiService } from './alfresco-api.service';
import { UserPreferencesService } from './user-preferences.service';
@Injectable()
export class FavoritesApiService {
static remapFavoritesData(data: any = {}): NodePaging {
const list = (data.list || {});
const pagination = (list.pagination || {});
const entries: any[] = FavoritesApiService
.remapFavoriteEntries(list.entries || []);
return <NodePaging> {
list: { entries, pagination }
};
}
static remapEntry({ entry }: any): any {
entry.properties = {
'cm:title': entry.title,
'cm:description': entry.description
};
return { entry };
}
static remapFavoriteEntries(entries: any[]) {
return entries
.map(({ entry: { target }}: any) => ({
entry: target.file || target.folder
}))
.filter(({ entry }) => (!!entry))
.map(FavoritesApiService.remapEntry);
}
constructor(
private apiService: AlfrescoApiService,
private preferences: UserPreferencesService
) {}
private get favoritesApi() {
return this.apiService.getInstance().core.favoritesApi;
}
getFavorites(personId: string, options?: any): Observable<NodePaging> {
const { favoritesApi, handleError } = this;
const defaultOptions = {
maxItems: this.preferences.paginationSize,
skipCount: 0,
where: '(EXISTS(target/file) OR EXISTS(target/folder))',
include: [ 'properties', 'allowableOperations' ]
};
const queryOptions = Object.assign(defaultOptions, options);
const promise = favoritesApi
.getFavorites(personId, queryOptions)
.then(FavoritesApiService.remapFavoritesData);
return Observable
.fromPromise(promise)
.catch(handleError);
}
private handleError(error): Observable<any> {
return Observable.of(error);
}
}

View File

@@ -0,0 +1,44 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export interface HightlightTransformResult {
text: string;
changed: boolean;
}
export class HighlightTransformService {
public highlight(text: string, search: string, wrapperClass: string = 'highlight'): HightlightTransformResult {
let isMatching = false,
result = text;
if (search && text) {
let pattern = search.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
pattern = pattern.split(' ').filter((t) => {
return t.length > 0;
}).join('|');
const regex = new RegExp(pattern, 'gi');
result = text.replace(regex, (match) => {
isMatching = true;
return `<span class="${wrapperClass}">${match}</span>`;
});
return { text: result, changed: isMatching};
} else {
return { text: result, changed: isMatching};
}
}
}

View File

@@ -0,0 +1,18 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './public-api';

View File

@@ -0,0 +1,181 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { HttpClientModule } from '@angular/common/http';
import { Component } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AppConfigService } from '../app-config/app-config.service';
import { LogService } from './log.service';
describe('Log Service', () => {
let providesLogComponent: ComponentFixture<ProvidesLogComponent>;
let appConfigService: AppConfigService;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
HttpClientModule
],
declarations: [ProvidesLogComponent],
providers: [
LogService,
AppConfigService
]
});
TestBed.compileComponents();
}));
beforeEach(() => {
appConfigService = TestBed.get(AppConfigService);
});
it('should not log anything if is silent', () => {
appConfigService.config['logLevel'] = 'silent';
providesLogComponent = TestBed.createComponent(ProvidesLogComponent);
spyOn(console, 'log');
spyOn(console, 'trace');
spyOn(console, 'debug');
spyOn(console, 'info');
spyOn(console, 'warn');
spyOn(console, 'error');
providesLogComponent.componentInstance.log();
providesLogComponent.componentInstance.trace();
providesLogComponent.componentInstance.debug();
providesLogComponent.componentInstance.info();
providesLogComponent.componentInstance.warn();
providesLogComponent.componentInstance.error();
expect(console.log).not.toHaveBeenCalled();
expect(console.trace).not.toHaveBeenCalled();
expect(console.debug).not.toHaveBeenCalled();
expect(console.info).not.toHaveBeenCalled();
expect(console.warn).not.toHaveBeenCalled();
expect(console.error).not.toHaveBeenCalled();
});
it('should log only warning and errors if is warning level', () => {
appConfigService.config['logLevel'] = 'WARN';
providesLogComponent = TestBed.createComponent(ProvidesLogComponent);
spyOn(console, 'log');
spyOn(console, 'error');
spyOn(console, 'trace');
spyOn(console, 'warn');
providesLogComponent.componentInstance.log();
providesLogComponent.componentInstance.error();
providesLogComponent.componentInstance.trace();
providesLogComponent.componentInstance.warn();
expect(console.log).not.toHaveBeenCalled();
expect(console.error).toHaveBeenCalled();
expect(console.warn).toHaveBeenCalled();
expect(console.trace).not.toHaveBeenCalled();
});
it('should debug level not log trace and log', () => {
appConfigService.config['logLevel'] = 'debug';
providesLogComponent = TestBed.createComponent(ProvidesLogComponent);
spyOn(console, 'log');
spyOn(console, 'trace');
spyOn(console, 'debug');
spyOn(console, 'info');
spyOn(console, 'warn');
spyOn(console, 'error');
providesLogComponent.componentInstance.log();
providesLogComponent.componentInstance.trace();
providesLogComponent.componentInstance.debug();
providesLogComponent.componentInstance.info();
providesLogComponent.componentInstance.warn();
providesLogComponent.componentInstance.error();
expect(console.log).not.toHaveBeenCalled();
expect(console.trace).not.toHaveBeenCalled();
expect(console.debug).toHaveBeenCalled();
expect(console.info).toHaveBeenCalled();
expect(console.warn).toHaveBeenCalled();
expect(console.error).toHaveBeenCalled();
});
it('should trace level log all', () => {
appConfigService.config['logLevel'] = 'trace';
providesLogComponent = TestBed.createComponent(ProvidesLogComponent);
spyOn(console, 'log');
spyOn(console, 'trace');
spyOn(console, 'debug');
spyOn(console, 'info');
spyOn(console, 'warn');
spyOn(console, 'error');
providesLogComponent.componentInstance.log();
providesLogComponent.componentInstance.trace();
providesLogComponent.componentInstance.debug();
providesLogComponent.componentInstance.info();
providesLogComponent.componentInstance.warn();
providesLogComponent.componentInstance.error();
expect(console.log).toHaveBeenCalled();
expect(console.trace).toHaveBeenCalled();
expect(console.debug).toHaveBeenCalled();
expect(console.info).toHaveBeenCalled();
expect(console.warn).toHaveBeenCalled();
expect(console.error).toHaveBeenCalled();
});
});
@Component({
template: '',
providers: [LogService]
})
class ProvidesLogComponent {
constructor(public logService: LogService) {
}
error() {
this.logService.error('Test message');
}
info() {
this.logService.info('Test message');
}
warn() {
this.logService.warn('Test message');
}
log() {
this.logService.log('Test message');
}
debug() {
this.logService.debug('Test message');
}
trace() {
this.logService.trace('Test message');
}
}

View File

@@ -0,0 +1,122 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { AppConfigService } from '../app-config/app-config.service';
import { logLevels, LogLevelsEnum } from '../models/log-levels.model';
@Injectable()
export class LogService {
currentLogLevel: LogLevelsEnum = LogLevelsEnum.TRACE;
constructor(appConfig: AppConfigService) {
if (appConfig) {
let configLevel: string = appConfig.get<string>('logLevel');
if (configLevel) {
this.currentLogLevel = this.getCurrentLogLevel(configLevel);
}
}
}
get error(): (message?: any, ...optionalParams: any[]) => any {
if (this.currentLogLevel >= LogLevelsEnum.ERROR) {
return console.error.bind(console);
}
return (message?: any, ...optionalParams: any[]) => {
};
}
get debug(): (message?: any, ...optionalParams: any[]) => any {
if (this.currentLogLevel >= LogLevelsEnum.DEBUG) {
return console.debug.bind(console);
}
return (message?: any, ...optionalParams: any[]) => {
};
}
get info(): (message?: any, ...optionalParams: any[]) => any {
if (this.currentLogLevel >= LogLevelsEnum.INFO) {
return console.info.bind(console);
}
return (message?: any, ...optionalParams: any[]) => {
};
}
get log(): (message?: any, ...optionalParams: any[]) => any {
if (this.currentLogLevel >= LogLevelsEnum.TRACE) {
return console.log.bind(console);
}
return (message?: any, ...optionalParams: any[]) => {
};
}
get trace(): (message?: any, ...optionalParams: any[]) => any {
if (this.currentLogLevel >= LogLevelsEnum.TRACE) {
return console.trace.bind(console);
}
return (message?: any, ...optionalParams: any[]) => {
};
}
get warn(): (message?: any, ...optionalParams: any[]) => any {
if (this.currentLogLevel >= LogLevelsEnum.WARN) {
return console.warn.bind(console);
}
return (message?: any, ...optionalParams: any[]) => {
};
}
get assert(): (message?: any, ...optionalParams: any[]) => any {
if (this.currentLogLevel !== LogLevelsEnum.SILENT) {
return console.assert.bind(console);
}
return (message?: any, ...optionalParams: any[]) => {
};
}
get group(): (message?: any, ...optionalParams: any[]) => any {
if (this.currentLogLevel !== LogLevelsEnum.SILENT) {
return console.group.bind(console);
}
return (message?: any, ...optionalParams: any[]) => {
};
}
get groupEnd(): (message?: any, ...optionalParams: any[]) => any {
if (this.currentLogLevel !== LogLevelsEnum.SILENT) {
return console.groupEnd.bind(console);
}
return (message?: any, ...optionalParams: any[]) => {
};
}
getCurrentLogLevel(level: string): LogLevelsEnum {
let referencedLevel = logLevels.find((currentLevel: any) => {
return currentLevel.name.toLocaleLowerCase() === level.toLocaleLowerCase();
});
return referencedLevel ? referencedLevel.level : 5;
}
}

View File

@@ -0,0 +1,117 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { MinimalNodeEntity, MinimalNodeEntryEntity, NodePaging } from 'alfresco-js-api';
import { Observable } from 'rxjs/Rx';
import { AlfrescoApiService } from './alfresco-api.service';
import { UserPreferencesService } from './user-preferences.service';
@Injectable()
export class NodesApiService {
constructor(
private api: AlfrescoApiService,
private preferences: UserPreferencesService) {}
private get nodesApi() {
return this.api.getInstance().core.nodesApi;
}
private getEntryFromEntity(entity: MinimalNodeEntity) {
return entity.entry;
}
getNode(nodeId: string, options: any = {}): Observable<MinimalNodeEntryEntity> {
const { nodesApi, handleError, getEntryFromEntity } = this;
const defaults = {
include: [ 'path', 'properties', 'allowableOperations' ]
};
const queryOptions = Object.assign(defaults, options);
const promise = nodesApi
.getNode(nodeId, queryOptions)
.then(getEntryFromEntity);
return Observable
.fromPromise(promise)
.catch(handleError);
}
getNodeChildren(nodeId: string, options: any = {}): Observable<NodePaging> {
const { nodesApi, handleError } = this;
const defaults = {
maxItems: this.preferences.paginationSize,
skipCount: 0,
include: [ 'path', 'properties', 'allowableOperations' ]
};
const queryOptions = Object.assign(defaults, options);
const promise = nodesApi
.getNodeChildren(nodeId, queryOptions);
return Observable
.fromPromise(promise)
.catch(handleError);
}
createNode(parentNodeId: string, nodeBody: any, options: any = {}): Observable<MinimalNodeEntryEntity> {
const { nodesApi, handleError, getEntryFromEntity } = this;
const promise = nodesApi
.addNode(parentNodeId, nodeBody, options)
.then(getEntryFromEntity);
return Observable.fromPromise(promise).catch(handleError);
}
createFolder(parentNodeId: string, nodeBody: any, options: any = {}): Observable<MinimalNodeEntryEntity> {
const body = Object.assign({ nodeType: 'cm:folder' }, nodeBody);
return this.createNode(parentNodeId, body, options);
}
updateNode(nodeId: string, nodeBody: any, options: any = {}): Observable<MinimalNodeEntryEntity> {
const { nodesApi, handleError, getEntryFromEntity } = this;
const promise = nodesApi
.updateNode(nodeId, nodeBody, options)
.then(getEntryFromEntity);
return Observable.fromPromise(promise).catch(handleError);
}
deleteNode(nodeId: string, options: any = {}): Observable<void> {
const { nodesApi, handleError } = this;
const promise = nodesApi
.deleteNode(nodeId, options);
return Observable
.fromPromise(promise)
.catch(handleError);
}
restoreNode(nodeId: string): Observable<MinimalNodeEntryEntity> {
const { nodesApi, handleError, getEntryFromEntity } = this;
const promise = nodesApi
.restoreNode(nodeId)
.then(getEntryFromEntity);
return Observable
.fromPromise(promise)
.catch(handleError);
}
handleError(error: any): Observable<any> {
return Observable.throw(error);
}
}

View File

@@ -0,0 +1,97 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { LiveAnnouncer } from '@angular/cdk/a11y';
import { OVERLAY_PROVIDERS, OverlayModule } from '@angular/cdk/overlay';
import { Component } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MatSnackBar, MatSnackBarModule } from '@angular/material';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NotificationService } from './notification.service';
describe('NotificationService', () => {
let fixture: ComponentFixture<ProvidesNotificationServiceComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
BrowserAnimationsModule,
OverlayModule,
MatSnackBarModule
],
declarations: [ProvidesNotificationServiceComponent],
providers: [
NotificationService,
MatSnackBar,
OVERLAY_PROVIDERS,
LiveAnnouncer
]
});
TestBed.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ProvidesNotificationServiceComponent);
fixture.detectChanges();
});
xit('should open a message notification bar', (done) => {
let promise = fixture.componentInstance.sendMessage();
promise.afterDismissed().subscribe(() => {
done();
});
fixture.detectChanges();
expect(document.querySelector('snack-bar-container')).not.toBeNull();
});
xit('should open a message notification bar with action', (done) => {
let promise = fixture.componentInstance.sendMessageAction();
promise.afterDismissed().subscribe(() => {
done();
});
fixture.detectChanges();
expect(document.querySelector('snack-bar-container')).not.toBeNull();
});
});
@Component({
template: '',
providers: [NotificationService]
})
class ProvidesNotificationServiceComponent {
constructor(public notificationService: NotificationService) {
}
sendMessage() {
let promise = this.notificationService.openSnackMessage('Test notification', 5000);
return promise;
}
sendMessageAction() {
let promise = this.notificationService.openSnackMessageAction('Test notification', 'TestWarn', 5000);
return promise;
}
}

View File

@@ -0,0 +1,40 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { MatSnackBar, MatSnackBarRef } from '@angular/material';
@Injectable()
export class NotificationService {
static DEFAULT_DURATION_MESSAGE: number = 5000;
constructor(public snackbar: MatSnackBar) {
}
public openSnackMessage(message: string, millisecondsDuration?: number): MatSnackBarRef<any> {
return this.snackbar.open(message, null, {
duration: millisecondsDuration || NotificationService.DEFAULT_DURATION_MESSAGE
});
}
public openSnackMessageAction(message: string, action: string, millisecondsDuration?: number): MatSnackBarRef<any> {
return this.snackbar.open(message, action, {
duration: millisecondsDuration || NotificationService.DEFAULT_DURATION_MESSAGE
});
}
}

View File

@@ -0,0 +1,100 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { inject, TestBed } from '@angular/core/testing';
import { Title } from '@angular/platform-browser';
import { AppConfigService } from '../app-config';
import { PageTitleService } from './page-title.service';
class TestConfig {
private setup: any = {
applicationName: undefined
};
titleService: Title = null;
appTitleService: PageTitleService = null;
constructor(setup: any = {}) {
Object.assign(this.setup, setup);
const titleServiceProvider = {
provide: Title,
useValue: {
setTitle: jasmine.createSpy('setTitleSpy')
}
};
const appConfigProvider = {
provide: AppConfigService,
useValue: {
config: {
application: {
name: this.setup.applicationName
}
},
get: () => this.setup.applicationName,
load: () => {}
}
};
TestBed.configureTestingModule({
providers: [
titleServiceProvider,
appConfigProvider,
PageTitleService
]
});
inject([ Title, PageTitleService ], (titleService, appTitleService) => {
this.titleService = titleService;
this.appTitleService = appTitleService;
})();
}
}
describe('AppTitle service', () => {
it('sets default application name', () => {
const { appTitleService, titleService } = new TestConfig({
applicationName: undefined
});
appTitleService.setTitle();
expect(titleService.setTitle)
.toHaveBeenCalledWith('Alfresco ADF Application');
});
it('sets only the application name', () => {
const { appTitleService, titleService } = new TestConfig({
applicationName: 'My application'
});
appTitleService.setTitle();
expect(titleService.setTitle)
.toHaveBeenCalledWith('My application');
});
it('appends application name to the title', () => {
const { appTitleService, titleService } = new TestConfig({
applicationName: 'My application'
});
appTitleService.setTitle('My page');
expect(titleService.setTitle)
.toHaveBeenCalledWith('My page - My application');
});
});

View File

@@ -0,0 +1,35 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { AppConfigService } from '../app-config/app-config.service';
@Injectable()
export class PageTitleService {
constructor(
private titleService: Title,
private appConfig: AppConfigService) {}
setTitle(value: string = '') {
const name = this.appConfig.get('application.name') || 'Alfresco ADF Application';
const title = value ? `${value} - ${name}` : `${name}`;
this.titleService.setTitle(title);
}
}

View File

@@ -0,0 +1,114 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { async, inject, TestBed } from '@angular/core/testing';
import { AlfrescoApiService } from './alfresco-api.service';
import { PeopleContentService } from './people-content.service';
class PeopleContentServiceTest {
service: any = null;
setup: any = {
rejectGetPerson: false
};
constructor(setup: any = {}) {
Object.assign(this.setup, setup);
const { alfrescoApiServiceMock } = this;
const alfrescoApiServiceProvider = {
provide: AlfrescoApiService,
useValue: alfrescoApiServiceMock
};
TestBed.configureTestingModule({
providers: [
alfrescoApiServiceProvider,
PeopleContentService
]
});
inject([ PeopleContentService ], (service) => {
this.service = service;
})();
}
private get alfrescoApiServiceMock(): any {
const { setup } = this;
const peopleApiMock = {
getPerson: jasmine.createSpy('getPersonSpy').and.callFake((personId) => {
return new Promise((resolve, reject) => {
setup.rejectGetPerson
? reject()
: resolve({ id: personId });
});
})
};
return {
getInstance: () => {
return {
core: { peopleApi: peopleApiMock }
};
}
};
}
get peopleApiGetPersonSpy() {
return this.service.peopleApi.getPerson;
}
get peopleApiGetPersonArguments() {
return this.peopleApiGetPersonSpy.calls.mostRecent().args;
}
}
describe('PeopleAPI', () => {
describe('Get persons', () => {
it('calls method by an id', async(() => {
const test = new PeopleContentServiceTest();
test.service.getPerson('person-1').subscribe(() => {
expect(test.peopleApiGetPersonArguments[0])
.toBe('person-1');
});
}));
it('calls method with "-me-"', async(() => {
const test = new PeopleContentServiceTest();
test.service.getCurrentPerson().subscribe(() => {
expect(test.peopleApiGetPersonArguments[0])
.toBe('-me-');
});
}));
it('handles the error when it fails', async(() => {
const test = new PeopleContentServiceTest({
rejectGetPerson: true
});
const handleErrorSpy = spyOn(test.service, 'handleError')
.and.callThrough();
test.service.getPerson().subscribe(() => {
expect(handleErrorSpy).toHaveBeenCalled();
});
}));
});
});

View File

@@ -0,0 +1,47 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import { AlfrescoApiService } from './alfresco-api.service';
@Injectable()
export class PeopleContentService {
constructor(private apiService: AlfrescoApiService) {}
private get peopleApi() {
return this.apiService.getInstance().core.peopleApi;
}
getPerson(personId: string): Observable<any> {
const { peopleApi, handleError } = this;
const promise = peopleApi.getPerson(personId);
return Observable
.fromPromise(promise)
.catch(handleError);
}
getCurrentPerson(): Observable<any> {
return this.getPerson('-me-');
}
private handleError(error): Observable<any> {
return Observable.of(error);
}
}

View File

@@ -0,0 +1,183 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { TestBed } from '@angular/core/testing';
import { UserProcessModel } from '../models';
import { AppConfigServiceMock } from '../mock/app-config.service.mock';
import { AlfrescoApiService } from './alfresco-api.service';
import { AppConfigService } from '../app-config/app-config.service';
import { LogService } from './log.service';
import { PeopleProcessService } from './people-process.service';
import { StorageService } from './storage.service';
declare let jasmine: any;
const firstInvolvedUser: UserProcessModel = new UserProcessModel({
id: 1,
email: 'fake-user1@fake.com',
firstName: 'fakeName1',
lastName: 'fakeLast1'
});
const secondInvolvedUser: UserProcessModel = new UserProcessModel({
id: 2,
email: 'fake-user2@fake.com',
firstName: 'fakeName2',
lastName: 'fakeLast2'
});
const fakeInvolveUserList: UserProcessModel[] = [firstInvolvedUser, secondInvolvedUser];
describe('PeopleProcessService', () => {
let service: PeopleProcessService;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
PeopleProcessService,
AlfrescoApiService,
StorageService,
LogService,
{ provide: AppConfigService, useClass: AppConfigServiceMock }
]
});
service = TestBed.get(PeopleProcessService);
});
describe('when user is logged in', () => {
beforeEach(() => {
jasmine.Ajax.install();
});
afterEach(() => {
jasmine.Ajax.uninstall();
});
it('should be able to retrieve people to involve in the task', (done) => {
service.getWorkflowUsers('fake-task-id', 'fake-filter').subscribe(
(users: UserProcessModel[]) => {
expect(users).toBeDefined();
expect(users.length).toBe(2);
expect(users[0].id).toEqual(1);
expect(users[0].email).toEqual('fake-user1@fake.com');
expect(users[0].firstName).toEqual('fakeName1');
expect(users[0].lastName).toEqual('fakeLast1');
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: {data: fakeInvolveUserList}
});
});
it('should be able to get people images for people retrieved', (done) => {
service.getWorkflowUsers('fake-task-id', 'fake-filter').subscribe(
(users: UserProcessModel[]) => {
expect(users).toBeDefined();
expect(users.length).toBe(2);
expect(service.getUserImage(users[0])).toContain('/users/' + users[0].id + '/picture');
expect(service.getUserImage(users[1])).toContain('/users/' + users[1].id + '/picture');
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: {data: fakeInvolveUserList}
});
});
it('should return user image url', () => {
let url = service.getUserImage(firstInvolvedUser);
expect(url).toContain('/users/' + firstInvolvedUser.id + '/picture');
});
it('should return empty list when there are no users to involve', (done) => {
service.getWorkflowUsers('fake-task-id', 'fake-filter').subscribe(
(users: UserProcessModel[]) => {
expect(users).toBeDefined();
expect(users.length).toBe(0);
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: {}
});
});
it('getWorkflowUsers catch errors call', (done) => {
service.getWorkflowUsers('fake-task-id', 'fake-filter').subscribe(() => {
}, () => {
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 403
});
});
it('should be able to involve people in the task', (done) => {
service.involveUserWithTask('fake-task-id', 'fake-user-id').subscribe(
() => {
expect(jasmine.Ajax.requests.mostRecent().method).toBe('PUT');
expect(jasmine.Ajax.requests.mostRecent().url).toContain('tasks/fake-task-id/action/involve');
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200
});
});
it('involveUserWithTask catch errors call', (done) => {
service.involveUserWithTask('fake-task-id', 'fake-user-id').subscribe(() => {
}, () => {
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 403
});
});
it('should be able to remove involved people from task', (done) => {
service.removeInvolvedUser('fake-task-id', 'fake-user-id').subscribe(
() => {
expect(jasmine.Ajax.requests.mostRecent().method).toBe('PUT');
expect(jasmine.Ajax.requests.mostRecent().url).toContain('tasks/fake-task-id/action/remove-involved');
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200
});
});
it('removeInvolvedUser catch errors call', (done) => {
service.removeInvolvedUser('fake-task-id', 'fake-user-id').subscribe(() => {
}, () => {
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 403
});
});
});
});

View File

@@ -0,0 +1,80 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { Response } from '@angular/http';
import { Observable } from 'rxjs/Rx';
import { UserProcessModel } from '../models/user-process.model';
import { AlfrescoApiService } from './alfresco-api.service';
import { LogService } from './log.service';
@Injectable()
export class PeopleProcessService {
constructor(private alfrescoJsApi: AlfrescoApiService,
private logService: LogService) {
}
getWorkflowUsers(taskId?: string, searchWord?: string): Observable<UserProcessModel[]> {
let option = { excludeTaskId: taskId, filter: searchWord };
return Observable.fromPromise(this.getWorkflowUserApi(option))
.map((response: any) => <UserProcessModel[]> response.data || [])
.catch(err => this.handleError(err));
}
getUserImage(user: UserProcessModel): string {
return this.getUserProfileImageApi(user.id);
}
involveUserWithTask(taskId: string, idToInvolve: string): Observable<UserProcessModel[]> {
let node = {userId: idToInvolve};
return Observable.fromPromise(this.involveUserToTaskApi(taskId, node))
.catch(err => this.handleError(err));
}
removeInvolvedUser(taskId: string, idToRemove: string): Observable<UserProcessModel[]> {
let node = {userId: idToRemove};
return Observable.fromPromise(this.removeInvolvedUserFromTaskApi(taskId, node))
.catch(err => this.handleError(err));
}
private getWorkflowUserApi(options: any) {
return this.alfrescoJsApi.getInstance().activiti.usersWorkflowApi.getUsers(options);
}
private involveUserToTaskApi(taskId: string, node: any) {
return this.alfrescoJsApi.getInstance().activiti.taskActionsApi.involveUser(taskId, node);
}
private removeInvolvedUserFromTaskApi(taskId: string, node: any) {
return this.alfrescoJsApi.getInstance().activiti.taskActionsApi.removeInvolvedUser(taskId, node);
}
private getUserProfileImageApi(userId: number) {
return this.alfrescoJsApi.getInstance().activiti.userApi.getUserProfilePictureUrl(userId);
}
/**
* Throw the error
* @param error
* @returns {ErrorObservable}
*/
private handleError(error: Response) {
this.logService.error(error);
return Observable.throw(error || 'Server error');
}
}

View File

@@ -0,0 +1,53 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './authentication.service';
export * from './alfresco-api.service';
export * from './settings.service';
export * from './content.service';
export * from './auth-guard.service';
export * from './auth-guard-ecm.service';
export * from './auth-guard-bpm.service';
export * from './apps-process.service';
export * from './page-title.service';
export * from './storage.service';
export * from './cookie.service';
export * from './renditions.service';
export * from './notification.service';
export * from './log.service';
export * from './translation.service';
export * from './translate-loader.service';
export * from './thumbnail.service';
export * from './upload.service';
export * from './dynamic-component-mapper.service';
export * from './card-item-types.service';
export * from './card-view-update.service';
export * from './user-preferences.service';
export * from './highlight-transform.service';
export * from './deleted-nodes-api.service';
export * from './favorites-api.service';
export * from './nodes-api.service';
export * from './people-content.service';
export * from './people-process.service';
export * from './search-api.service';
export * from './search.service';
export * from './shared-links-api.service';
export * from './sites-api.service';
export * from './discovery-api.service';
export * from './comment-process.service';
export * from './service.module';

View File

@@ -0,0 +1,172 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { async, TestBed } from '@angular/core/testing';
import { fakeRedition, fakeReditionCreated, fakeReditionsList } from '../mock/renditionsService.mock';
import { AlfrescoApiService } from './alfresco-api.service';
import { SettingsService } from './settings.service';
import { AppConfigModule } from '../app-config';
import { LogService } from './log.service';
import { RenditionsService } from './renditions.service';
import { StorageService } from './storage.service';
declare let jasmine: any;
describe('RenditionsService', () => {
let service: RenditionsService;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
AppConfigModule
],
declarations: [
],
providers: [
AlfrescoApiService,
RenditionsService,
SettingsService,
StorageService,
LogService
]
}).compileComponents();
}));
beforeEach(() => {
jasmine.Ajax.install();
service = TestBed.get(RenditionsService);
});
afterEach(() => {
jasmine.Ajax.uninstall();
});
it('Get redition list service should return the list', (done) => {
service.getRenditionsListByNodeId('fake-node-id').subscribe((res) => {
expect(res.list.entries[0].entry.id).toBe('avatar');
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 200,
contentType: 'application/json',
responseText: JSON.stringify(fakeReditionsList)
});
});
it('Create redition service should call the server with the ID passed and the asked encoding', (done) => {
service.createRendition('fake-node-id', 'pdf').subscribe((res) => {
expect(jasmine.Ajax.requests.mostRecent().method).toBe('POST');
expect(jasmine.Ajax.requests.mostRecent().url).toContain('/ecm/alfresco/api/-default-/public/alfresco/versions/1/nodes/fake-node-id/renditions');
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 200,
contentType: 'application/json',
responseText: ''
});
});
describe('convert', () => {
it('should call the server with the ID passed and the asked encoding for creation', (done) => {
service.convert('fake-node-id', 'pdf', 1000);
expect(jasmine.Ajax.requests.mostRecent().method).toBe('POST');
expect(jasmine.Ajax.requests.mostRecent().url).toContain('/ecm/alfresco/api/-default-/public/alfresco/versions/1/nodes/fake-node-id/renditions');
done();
});
});
it('Get redition service should catch the error', (done) => {
service.getRenditionsListByNodeId('fake-node-id').subscribe((res) => {
}, (res) => {
done();
}
);
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 403,
contentType: 'application/json',
responseText: 'error'
});
});
it('isConversionPossible should return true if is possible convert', (done) => {
service.isConversionPossible('fake-node-id', 'pdf').subscribe((res) => {
expect(res).toBe(true);
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 200,
contentType: 'application/json',
responseText: JSON.stringify(fakeRedition)
});
});
it('isConversionPossible should return false if is not possible to convert', (done) => {
service.isConversionPossible('fake-node-id', 'pdf').subscribe((res) => {
expect(res).toBe(false);
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 403,
contentType: 'application/json'
});
});
it('isRenditionsAvailable should return true if the conversion exist', (done) => {
service.isRenditionAvailable('fake-node-id', 'pdf').subscribe((res) => {
expect(res).toBe(true);
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 200,
contentType: 'application/json',
responseText: JSON.stringify(fakeReditionCreated)
});
});
it('isRenditionsAvailable should return false if the conversion not exist', (done) => {
service.isRenditionAvailable('fake-node-id', 'pdf').subscribe((res) => {
expect(res).toBe(false);
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 200,
contentType: 'application/json',
responseText: JSON.stringify(fakeRedition)
});
});
it('isRenditionsAvailable should return false if the conversion get error', (done) => {
service.isRenditionAvailable('fake-node-id', 'pdf').subscribe((res) => {
expect(res).toBe(false);
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 400,
contentType: 'application/json'
});
});
});

View File

@@ -0,0 +1,96 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { RenditionEntry, RenditionPaging } from 'alfresco-js-api';
import { Observable } from 'rxjs/Rx';
import { AlfrescoApiService } from './alfresco-api.service';
/**
* RenditionsService
*
* @returns {RenditionsService} .
*/
@Injectable()
export class RenditionsService {
constructor(private apiService: AlfrescoApiService) {
}
isRenditionAvailable(nodeId: string, encoding: string): Observable<boolean> {
return Observable.create((observer) => {
this.getRendition(nodeId, encoding).subscribe(
(res) => {
let isAvailable = true;
if (res.entry.status.toString() === 'NOT_CREATED') {
isAvailable = false;
}
observer.next(isAvailable);
observer.complete();
},
() => {
observer.next(false);
observer.complete();
}
);
});
}
isConversionPossible(nodeId: string, encoding: string): Observable<boolean> {
return Observable.create((observer) => {
this.getRendition(nodeId, encoding).subscribe(
() => {
observer.next(true);
observer.complete();
},
() => {
observer.next(false);
observer.complete();
}
);
});
}
getRenditionUrl(nodeId: string, encoding: string): string {
return this.apiService.contentApi.getRenditionUrl(nodeId, 'pdf');
}
getRendition(nodeId: string, encoding: string): Observable<RenditionEntry> {
return Observable.fromPromise(this.apiService.renditionsApi.getRendition(nodeId, encoding));
}
getRenditionsListByNodeId(nodeId: string): Observable<RenditionPaging> {
return Observable.fromPromise(this.apiService.renditionsApi.getRenditions(nodeId));
}
createRendition(nodeId: string, encoding: string): Observable<{}> {
return Observable.fromPromise(this.apiService.renditionsApi.createRendition(nodeId, {id: encoding}));
}
convert(nodeId: string, encoding: string, pollingInterval: number = 1000) {
return this.createRendition(nodeId, encoding)
.concatMap(() => this.pollRendition(nodeId, encoding, pollingInterval));
}
private pollRendition(nodeId: string, encoding: string, interval: number = 1000) {
return Observable.interval(interval)
.switchMap(() => this.getRendition(nodeId, encoding))
.takeWhile((data) => {
return (data.entry.status.toString() !== 'CREATED');
});
}
}

View File

@@ -0,0 +1,46 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import { NodePaging } from 'alfresco-js-api';
import { AlfrescoApiService } from './alfresco-api.service';
@Injectable()
export class SearchApiService {
constructor(private apiService: AlfrescoApiService) {}
private get searchApi() {
return this.apiService.getInstance().search.searchApi;
}
search(query: any): Observable<NodePaging> {
const { searchApi, handleError } = this;
const searchQuery = Object.assign(query);
const promise = searchApi.search(searchQuery);
return Observable
.fromPromise(promise)
.catch(handleError);
}
private handleError(error): Observable<any> {
return Observable.of(error);
}
}

View File

@@ -0,0 +1,129 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { async, TestBed } from '@angular/core/testing';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { fakeApi, fakeError, fakeSearch } from '../mock/search.service.mock';
import { CookieServiceMock } from './../mock/cookie.service.mock';
import { AlfrescoApiService } from './alfresco-api.service';
import { SettingsService } from './settings.service';
import { AppConfigModule } from '../app-config';
import { AuthenticationService } from './authentication.service';
import { CookieService } from './cookie.service';
import { LogService } from './log.service';
import { SearchService } from './search.service';
import { StorageService } from './storage.service';
import { TranslateLoaderService } from './translate-loader.service';
import { UserPreferencesService } from './user-preferences.service';
describe('SearchService', () => {
let service: SearchService;
let apiService: AlfrescoApiService;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
AppConfigModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderService
}
})
],
providers: [
SearchService,
AuthenticationService,
AlfrescoApiService,
SettingsService,
AuthenticationService,
StorageService,
UserPreferencesService,
{ provide: CookieService, useClass: CookieServiceMock },
LogService
]
}).compileComponents();
}));
beforeEach(() => {
service = TestBed.get(SearchService);
apiService = TestBed.get(AlfrescoApiService);
spyOn(apiService, 'getInstance').and.returnValue(fakeApi);
});
it('should call search API with no additional options', (done) => {
let searchTerm = 'searchTerm63688';
spyOn(fakeApi.core.queriesApi, 'findNodes').and.returnValue(Promise.resolve(fakeSearch));
service.getNodeQueryResults(searchTerm).subscribe(
() => {
expect(fakeApi.core.queriesApi.findNodes).toHaveBeenCalledWith(searchTerm, undefined);
done();
}
);
});
it('should call search API with additional options', (done) => {
let searchTerm = 'searchTerm63688', options = {
include: [ 'path' ],
rootNodeId: '-root-',
nodeType: 'cm:content'
};
spyOn(fakeApi.core.queriesApi, 'findNodes').and.returnValue(Promise.resolve(fakeSearch));
service.getNodeQueryResults(searchTerm, options).subscribe(
() => {
expect(fakeApi.core.queriesApi.findNodes).toHaveBeenCalledWith(searchTerm, options);
done();
}
);
});
it('should return search results returned from the API', (done) => {
service.getNodeQueryResults('').subscribe(
(res: any) => {
expect(res).toBeDefined();
expect(res).toEqual(fakeSearch);
done();
}
);
});
it('should notify errors returned from the API', (done) => {
spyOn(fakeApi.core.queriesApi, 'findNodes').and.returnValue(Promise.reject(fakeError));
service.getNodeQueryResults('').subscribe(
() => {},
(res: any) => {
expect(res).toBeDefined();
expect(res).toEqual(fakeError);
done();
}
);
});
it('should notify a general error if the API does not return a specific error', (done) => {
spyOn(fakeApi.core.queriesApi, 'findNodes').and.returnValue(Promise.reject(null));
service.getNodeQueryResults('').subscribe(
() => {},
(res: any) => {
expect(res).toBeDefined();
expect(res).toEqual('Server error');
done();
}
);
});
});

View File

@@ -0,0 +1,64 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { NodePaging } from 'alfresco-js-api';
import { Observable } from 'rxjs/Rx';
import { AlfrescoApiService } from './alfresco-api.service';
import { AuthenticationService } from './authentication.service';
/**
* Internal service used by Document List component.
*/
@Injectable()
export class SearchService {
constructor(public authService: AuthenticationService,
private apiService: AlfrescoApiService) {
}
/**
* Execute a search against the repository
*
* @param term Search term
* @param options Additional options passed to the search
* @returns {Observable<NodePaging>} Search results
*/
getNodeQueryResults(term: string, options?: SearchOptions): Observable<NodePaging> {
return Observable.fromPromise(this.getQueryNodesPromise(term, options))
.map(res => <NodePaging> res)
.catch(err => this.handleError(err));
}
getQueryNodesPromise(term: string, opts: SearchOptions): Promise<NodePaging> {
return this.apiService.getInstance().core.queriesApi.findNodes(term, opts);
}
private handleError(error: any): Observable<any> {
return Observable.throw(error || 'Server error');
}
}
export interface SearchOptions {
skipCount?: number;
maxItems?: number;
rootNodeId?: string;
nodeType?: string;
include?: string[];
orderBy?: string;
fields?: string[];
}

View File

@@ -0,0 +1,96 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { NgModule } from '@angular/core';
import { AlfrescoApiService } from './alfresco-api.service';
import { AppsProcessService } from './apps-process.service';
import { AuthGuardBpm } from './auth-guard-bpm.service';
import { AuthGuardEcm } from './auth-guard-ecm.service';
import { AuthGuard } from './auth-guard.service';
import { AuthenticationService } from './authentication.service';
import { CardItemTypeService } from './card-item-types.service';
import { CardViewUpdateService } from './card-view-update.service';
import { CommentProcessService } from './comment-process.service';
import { ContentService } from './content.service';
import { CookieService } from './cookie.service';
import { DeletedNodesApiService } from './deleted-nodes-api.service';
import { DiscoveryApiService } from './discovery-api.service';
import { FavoritesApiService } from './favorites-api.service';
import { HighlightTransformService } from './highlight-transform.service';
import { LogService } from './log.service';
import { NodesApiService } from './nodes-api.service';
import { NotificationService } from './notification.service';
import { PageTitleService } from './page-title.service';
import { PeopleContentService } from './people-content.service';
import { PeopleProcessService } from './people-process.service';
import { RenditionsService } from './renditions.service';
import { SearchApiService } from './search-api.service';
import { SearchService } from './search.service';
import { SettingsService } from './settings.service';
import { SharedLinksApiService } from './shared-links-api.service';
import { SitesApiService } from './sites-api.service';
import { StorageService } from './storage.service';
import { ThumbnailService } from './thumbnail.service';
import { TranslateLoaderService } from './translate-loader.service';
import { TranslationService } from './translation.service';
import { UploadService } from './upload.service';
import { UserPreferencesService } from './user-preferences.service';
@NgModule({
imports: [],
declarations: [],
providers: [
AuthenticationService,
AlfrescoApiService,
SettingsService,
ContentService,
AuthGuard,
AuthGuardEcm,
AuthGuardBpm,
AppsProcessService,
PageTitleService,
StorageService,
CookieService,
RenditionsService,
NotificationService,
LogService,
TranslationService,
TranslateLoaderService,
ThumbnailService,
UploadService,
CardItemTypeService,
CardViewUpdateService,
UserPreferencesService,
HighlightTransformService,
DeletedNodesApiService,
FavoritesApiService,
NodesApiService,
PeopleContentService,
PeopleProcessService,
SearchApiService,
SearchService,
SharedLinksApiService,
SitesApiService,
DiscoveryApiService,
CommentProcessService
],
exports: [
]
})
export class ServiceModule {
}

View File

@@ -0,0 +1,60 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { async, TestBed } from '@angular/core/testing';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { AlfrescoApiService } from './alfresco-api.service';
import { SettingsService } from './settings.service';
import { AppConfigModule } from '../app-config';
import { LogService } from './log.service';
import { StorageService } from './storage.service';
import { TranslateLoaderService } from './translate-loader.service';
import { UserPreferencesService } from './user-preferences.service';
describe('SettingsService', () => {
let service: SettingsService;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
AppConfigModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderService
}
})
],
providers: [
AlfrescoApiService,
SettingsService,
UserPreferencesService,
StorageService,
LogService
]
}).compileComponents();
}));
beforeEach(() => {
service = TestBed.get(SettingsService);
});
it('should be exposed by the module', () => {
expect(service).toBeDefined();
});
});

View File

@@ -0,0 +1,77 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { AppConfigService } from '../app-config/app-config.service';
import { LogService } from './log.service';
import { UserPreferencesService } from './user-preferences.service';
@Injectable()
export class SettingsService {
constructor(
private appConfig: AppConfigService,
private logService: LogService,
private preferences: UserPreferencesService) {
}
/** @deprecated in 1.6.0 */
public get ecmHost(): string {
this.logService.log('SettingsService.ecmHost is deprecated. Use AppConfigService instead.');
return this.appConfig.get<string>('ecmHost');
}
/** @deprecated in 1.7.0 */
public set csrfDisabled(csrfDisabled: boolean) {
this.logService.log(`SettingsService.csrfDisabled is deprecated. Use UserPreferencesService.disableCSRF instead.`);
this.preferences.disableCSRF = csrfDisabled;
}
/** @deprecated in 1.6.0 */
public set ecmHost(ecmHostUrl: string) {
this.logService.log('SettingsService.ecmHost is deprecated. Use AppConfigService instead.');
}
/** @deprecated in 1.6.0 */
public get bpmHost(): string {
this.logService.log('SettingsService.bpmHost is deprecated. Use AppConfigService instead.');
return this.appConfig.get<string>('bpmHost');
}
/** @deprecated in 1.6.0 */
public set bpmHost(bpmHostUrl: string) {
this.logService.log('SettingsService.bpmHost is deprecated. Use AppConfigService instead.');
}
/** @deprecated in 1.6.0 */
public getBPMApiBaseUrl(): string {
this.logService.log('SettingsService.getBPMApiBaseUrl is deprecated.');
return this.bpmHost + '/activiti-app';
}
/** @deprecated in 1.7.0 */
public getProviders(): string {
this.logService.log(`SettingsService.getProviders is deprecated. Use UserPreferencesService.authType instead.`);
return this.preferences.authType;
}
/** @deprecated in 1.7.0 */
public setProviders(providers: string) {
this.logService.log(`SettingsService.getProviders is deprecated. Use UserPreferencesService.authType instead.`);
this.preferences.authType = providers;
}
}

View File

@@ -0,0 +1,54 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { NodePaging } from 'alfresco-js-api';
import { Observable } from 'rxjs/Rx';
import { AlfrescoApiService } from './alfresco-api.service';
import { UserPreferencesService } from './user-preferences.service';
@Injectable()
export class SharedLinksApiService {
constructor(
private apiService: AlfrescoApiService,
private preferences: UserPreferencesService) {}
private get sharedLinksApi() {
return this.apiService.getInstance().core.sharedlinksApi;
}
getSharedLinks(options: any = {}): Observable<NodePaging> {
const { sharedLinksApi, handleError } = this;
const defaultOptions = {
maxItems: this.preferences.paginationSize,
skipCount: 0,
include: [ 'properties', 'allowableOperations' ]
};
const queryOptions = Object.assign({}, defaultOptions, options);
const promise = sharedLinksApi
.findSharedLinks(queryOptions);
return Observable
.fromPromise(promise)
.catch(handleError);
}
private handleError(error: any): Observable<any> {
return Observable.of(error);
}
}

View File

@@ -0,0 +1,168 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { async, TestBed } from '@angular/core/testing';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { AlfrescoApiService } from './alfresco-api.service';
import { SettingsService } from './settings.service';
import { AppConfigModule, AppConfigService } from '../app-config';
import { AuthenticationService } from './authentication.service';
import { LogService } from './log.service';
import { SitesApiService } from './sites-api.service';
import { StorageService } from './storage.service';
import { TranslateLoaderService } from './translate-loader.service';
import { UserPreferencesService } from './user-preferences.service';
declare let jasmine: any;
describe('Sites service', () => {
let service;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
AppConfigModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderService
}
})
],
providers: [
SitesApiService,
AlfrescoApiService,
UserPreferencesService,
AuthenticationService,
SettingsService,
StorageService,
LogService
]
}).compileComponents();
}));
beforeEach(() => {
let appConfig: AppConfigService = TestBed.get(AppConfigService);
appConfig.config = {
ecmHost: 'http://localhost:9876/ecm',
files: {
excluded: ['.DS_Store', 'desktop.ini', '.git', '*.git']
}
};
service = TestBed.get(SitesApiService);
jasmine.Ajax.install();
});
afterEach(() => {
jasmine.Ajax.uninstall();
});
it('Should get a list of users sites', (done) => {
service.getSites().subscribe((data) => {
expect(data[0].title).toBe('FAKE');
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: {
'list': {
'pagination': {
'count': 1,
'hasMoreItems': false,
'totalItems': 1,
'skipCount': 0,
'maxItems': 100
},
'entries': [
{
'entry': {
'role': 'SiteManager',
'visibility': 'PUBLIC',
'guid': 'b4cff62a-664d-4d45-9302-98723eac1319',
'description': 'This is a Sample Alfresco Team site.',
'id': 'swsdp',
'title': 'FAKE'
}
}
]
}
}
});
});
it('Should get single sites via siteId', (done) => {
service.getSite('fake-site-id').subscribe((data) => {
expect(data.title).toBe('FAKE-SINGLE-TITLE');
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: {
'entry': {
'role': 'SiteManager',
'visibility': 'PUBLIC',
'guid': 'b4cff62a-664d-4d45-9302-98723eac1319',
'description': 'This is a Sample Alfresco Team site.',
'id': 'swsdp',
'preset': 'site-dashboard',
'title': 'FAKE-SINGLE-TITLE'
}
}
});
});
it('deleteSite should perform a call against the server', (done) => {
service.deleteSite('fake-site-id').subscribe(() => {
expect(jasmine.Ajax.requests.mostRecent().method).toBe('DELETE');
expect(jasmine.Ajax.requests.mostRecent().url)
.toContain('alfresco/api/-default-/public/alfresco/versions/1/sites/fake-site-id');
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 204
});
});
it('getSites catch errors call', (done) => {
service.getSites().subscribe(() => {
}, () => {
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 403
});
});
it('getSite catch errors call', (done) => {
service.getSite('error-id').subscribe(() => {
}, () => {
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 403
});
});
});

View File

@@ -0,0 +1,80 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { Response } from '@angular/http';
import { Observable } from 'rxjs/Rx';
import { SiteModel } from '../models/site.model';
import { AlfrescoApiService } from './alfresco-api.service';
@Injectable()
export class SitesApiService {
constructor(
private apiService: AlfrescoApiService) { }
getSites(opts: any = {}): any {
const defaultOptions = {
skipCount: 0,
include: ['properties']
};
const queryOptions = Object.assign({}, defaultOptions, opts);
return Observable.fromPromise(this.apiService.getInstance().core.sitesApi.getSites(queryOptions))
.map((res) => this.convertToModel(res))
.catch(this.handleError);
}
getSite(siteId: string, opts?: any): any {
return Observable.fromPromise(this.apiService.getInstance().core.sitesApi.getSite(siteId, opts))
.map((res: any) => new SiteModel(res))
.catch(this.handleError);
}
deleteSite(siteId: string, permanentFlag: boolean = true): any {
let options: any = {};
options.permanent = permanentFlag;
return Observable.fromPromise(this.apiService.getInstance().core.sitesApi.deleteSite(siteId, options)
.catch(this.handleError));
}
getSiteContent(siteId: string): Observable<any> {
return this.getSite(siteId, { relations: ['containers'] });
}
getSiteMembers(siteId: string): Observable<any> {
return this.getSite(siteId, { relations: ['members'] });
}
private handleError(error: Response): any {
console.error(error);
return Observable.throw(error || 'Server error');
}
private convertToModel(response: any) {
let convertedList: SiteModel[] = [];
if (response &&
response.list &&
response.list.entries &&
response.list.entries.length > 0) {
response.list.entries.forEach((element: any) => {
element.pagination = response.list.pagination;
convertedList.push(new SiteModel(element));
});
}
return convertedList;
}
}

View File

@@ -0,0 +1,82 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
@Injectable()
export class StorageService {
private memoryStore: { [key: string]: any } = {};
private useLocalStorage: boolean = false;
constructor() {
this.useLocalStorage = this.storageAvailable('localStorage');
}
getItem(key: string): string | null {
if (this.useLocalStorage) {
return localStorage.getItem(key);
} else {
return this.memoryStore.hasOwnProperty(key) ? this.memoryStore[key] : null;
}
}
setItem(key: string, data: string) {
if (this.useLocalStorage) {
localStorage.setItem(key, data);
} else {
this.memoryStore[key] = data.toString();
}
}
clear() {
if (this.useLocalStorage) {
localStorage.clear();
} else {
this.memoryStore = {};
}
}
removeItem(key: string) {
if (this.useLocalStorage) {
localStorage.removeItem(key);
} else {
delete this.memoryStore[key];
}
}
hasItem(key: string): boolean {
if (this.useLocalStorage) {
return localStorage.getItem(key) ? true : false;
} else {
return this.memoryStore.hasOwnProperty(key);
}
}
private storageAvailable(type: string): boolean {
try {
let storage = window[type];
const key = '__storage_test__';
storage.setItem(key, key);
storage.removeItem(key, key);
return true;
} catch (e) {
return false;
}
}
}

View File

@@ -0,0 +1,95 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { HttpClientModule } from '@angular/common/http';
import { async, TestBed } from '@angular/core/testing';
import { MatIconRegistry } from '@angular/material';
import { BrowserModule } from '@angular/platform-browser';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { CookieServiceMock } from './../mock/cookie.service.mock';
import { AlfrescoApiService } from './alfresco-api.service';
import { ContentService } from './content.service';
import { SettingsService } from './settings.service';
import { AppConfigService } from '../app-config/app-config.service';
import { AuthenticationService } from './authentication.service';
import { CookieService } from './cookie.service';
import { LogService } from './log.service';
import { StorageService } from './storage.service';
import { ThumbnailService } from './thumbnail.service';
import { TranslateLoaderService } from './translate-loader.service';
import { UserPreferencesService } from './user-preferences.service';
describe('ThumbnailService', () => {
let service: ThumbnailService;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
BrowserModule,
HttpClientModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderService
}
})
],
providers: [
UserPreferencesService,
AuthenticationService,
ContentService,
SettingsService,
AppConfigService,
{ provide: CookieService, useClass: CookieServiceMock },
ThumbnailService,
AlfrescoApiService,
SettingsService,
StorageService,
LogService,
MatIconRegistry
]
}).compileComponents();
}));
beforeEach(() => {
service = TestBed.get(ThumbnailService);
});
it('should return the correct icon for a plain text file', () => {
expect(service.getMimeTypeIcon('text/plain')).toContain('ft_ic_document');
});
it('should return the correct icon for a PNG file', () => {
expect(service.getMimeTypeIcon('image/png')).toContain('ft_ic_raster_image');
});
it('should return the correct icon for a MP4 video file', () => {
expect(service.getMimeTypeIcon('video/mp4')).toContain('ft_ic_video');
});
it('should return a generic icon for an unknown file', () => {
expect(service.getMimeTypeIcon('x-unknown/yyy')).toContain('ft_ic_miscellaneous');
});
it('should return the thumbnail URL for a content item', () => {
spyOn(service.contentService, 'getDocumentThumbnailUrl').and.returnValue('/fake-thumbnail.png');
expect(service.getDocumentThumbnailUrl({})).toContain('/fake-thumbnail.png');
});
});

View File

@@ -0,0 +1,152 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { MatIconRegistry } from '@angular/material';
import { DomSanitizer } from '@angular/platform-browser';
import { ContentService } from './content.service';
declare var require: any;
@Injectable()
export class ThumbnailService {
DEFAULT_ICON: string = require('../assets/images/ft_ic_miscellaneous.svg');
mimeTypeIcons: any = {
'image/png': require('../assets/images/ft_ic_raster_image.svg'),
'image/jpeg': require('../assets/images/ft_ic_raster_image.svg'),
'image/gif': require('../assets/images/ft_ic_raster_image.svg'),
'application/pdf': require('../assets/images/ft_ic_pdf.svg'),
'application/vnd.ms-excel': require('../assets/images/ft_ic_ms_excel.svg'),
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': require('../assets/images/ft_ic_ms_excel.svg'),
'application/vnd.openxmlformats-officedocument.spreadsheetml.template': require('../assets/images/ft_ic_ms_excel.svg'),
'application/msword': require('../assets/images/ft_ic_ms_word.svg'),
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': require('../assets/images/ft_ic_ms_word.svg'),
'application/vnd.openxmlformats-officedocument.wordprocessingml.template': require('../assets/images/ft_ic_ms_word.svg'),
'application/vnd.ms-powerpoint': require('../assets/images/ft_ic_ms_powerpoint.svg'),
'application/vnd.openxmlformats-officedocument.presentationml.presentation': require('../assets/images/ft_ic_ms_powerpoint.svg'),
'application/vnd.openxmlformats-officedocument.presentationml.template': require('../assets/images/ft_ic_ms_powerpoint.svg'),
'application/vnd.openxmlformats-officedocument.presentationml.slideshow': require('../assets/images/ft_ic_ms_powerpoint.svg'),
'video/mp4': require('../assets/images/ft_ic_video.svg'),
'text/plain': require('../assets/images/ft_ic_document.svg'),
'application/x-javascript': require('../assets/images/ft_ic_document.svg'),
'application/json': require('../assets/images/ft_ic_document.svg'),
'image/svg+xml': require('../assets/images/ft_ic_vector_image.svg'),
'text/html': require('../assets/images/ft_ic_website.svg'),
'application/x-compressed': require('../assets/images/ft_ic_archive.svg'),
'application/x-zip-compressed': require('../assets/images/ft_ic_archive.svg'),
'application/zip': require('../assets/images/ft_ic_archive.svg'),
'application/vnd.apple.keynote': require('../assets/images/ft_ic_presentation.svg'),
'application/vnd.apple.pages': require('../assets/images/ft_ic_document.svg'),
'application/vnd.apple.numbers': require('../assets/images/ft_ic_spreadsheet.svg'),
'folder': require('../assets/images/ft_ic_folder.svg'),
'disable/folder': require('../assets/images/ft_ic_folder_disable.svg'),
'selected': require('../assets/images/ft_ic_selected.svg')
};
constructor(public contentService: ContentService, matIconRegistry: MatIconRegistry, sanitizer: DomSanitizer) {
matIconRegistry.addSvgIcon('image/png',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_raster_image.svg')));
matIconRegistry.addSvgIcon('image/jpeg',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_raster_image.svg')));
matIconRegistry.addSvgIcon('image/gif',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_raster_image.svg')));
matIconRegistry.addSvgIcon('application/pdf',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_pdf.svg')));
matIconRegistry.addSvgIcon('application/vnd.ms-excel',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_ms_excel.svg')));
matIconRegistry.addSvgIcon('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_ms_excel.svg')));
matIconRegistry.addSvgIcon('application/vnd.openxmlformats-officedocument.spreadsheetml.template',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_ms_excel.svg')));
matIconRegistry.addSvgIcon('application/msword',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_ms_word.svg')));
matIconRegistry.addSvgIcon('application/vnd.openxmlformats-officedocument.wordprocessingml.document',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_ms_word.svg')));
matIconRegistry.addSvgIcon('application/vnd.openxmlformats-officedocument.wordprocessingml.template',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_ms_word.svg')));
matIconRegistry.addSvgIcon('application/vnd.ms-powerpoint',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_ms_powerpoint.svg')));
matIconRegistry.addSvgIcon('application/vnd.openxmlformats-officedocument.presentationml.presentation',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_ms_powerpoint.svg')));
matIconRegistry.addSvgIcon('application/vnd.openxmlformats-officedocument.presentationml.template',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_ms_powerpoint.svg')));
matIconRegistry.addSvgIcon('application/vnd.openxmlformats-officedocument.presentationml.slideshow',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_ms_powerpoint.svg')));
matIconRegistry.addSvgIcon('video/mp4',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_video.svg')));
matIconRegistry.addSvgIcon('text/plain',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_document.svg')));
matIconRegistry.addSvgIcon('application/x-javascript',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_document.svg')));
matIconRegistry.addSvgIcon('application/json',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_document.svg')));
matIconRegistry.addSvgIcon('image/svg+xml',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_vector_image.svg')));
matIconRegistry.addSvgIcon('text/html',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_website.svg')));
matIconRegistry.addSvgIcon('application/x-compressed',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_archive.svg')));
matIconRegistry.addSvgIcon('application/x-zip-compressed',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_archive.svg')));
matIconRegistry.addSvgIcon('application/zip',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_archive.svg')));
matIconRegistry.addSvgIcon('application/vnd.apple.keynote',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_presentation.svg')));
matIconRegistry.addSvgIcon('application/vnd.apple.pages',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_document.svg')));
matIconRegistry.addSvgIcon('application/vnd.apple.numbers',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_spreadsheet.svg')));
matIconRegistry.addSvgIcon('folder',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_folder.svg')));
matIconRegistry.addSvgIcon('disable/folder',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_folder_disable.svg')));
matIconRegistry.addSvgIcon('selected',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_selected.svg')));
matIconRegistry.addSvgIcon('default',
sanitizer.bypassSecurityTrustResourceUrl(require('../assets/images/ft_ic_miscellaneous.svg')));
}
/**
* Get thumbnail URL for the given document node.
* @param document Node to get URL for.
* @returns {string} URL address.
*/
public getDocumentThumbnailUrl(document: any): string {
let thumbnail = this.contentService.getDocumentThumbnailUrl(document);
return thumbnail || this.DEFAULT_ICON;
}
/**
* Get mimeType SVG
* @param mimeType
* @returns {string} URL SVG address.
*/
public getMimeTypeIcon(mimeType: string): string {
let icon = this.mimeTypeIcons[mimeType];
return (icon || this.DEFAULT_ICON);
}
/**
* Get default SVG
* @returns {string} URL SVG default.
*/
public getDefaultMimeTypeIcon(): string {
return this.DEFAULT_ICON;
}
}

View File

@@ -0,0 +1,138 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Response } from '@angular/http';
import { TranslateLoader } from '@ngx-translate/core';
import { Observable } from 'rxjs/Rx';
import { ComponentTranslationModel } from '../models/component.model';
import { ObjectUtils } from '../utils/object-utils';
import { LogService } from './log.service';
@Injectable()
export class TranslateLoaderService implements TranslateLoader {
private prefix: string = 'i18n';
private suffix: string = '.json';
private providers: ComponentTranslationModel[] = [];
private queue: string [][] = [];
constructor(private http: HttpClient,
private logService: LogService) {
}
registerProvider(name: string, path: string) {
let registered = this.providers.find(provider => provider.name === name);
if (registered) {
registered.path = path;
} else {
this.providers.push(new ComponentTranslationModel({ name: name, path: path }));
}
}
providerRegistered(name: string): boolean {
return this.providers.find(x => x.name === name) ? true : false;
}
getComponentToFetch(lang: string) {
let observableBatch = [];
if (!this.queue[lang]) {
this.queue[lang] = [];
}
this.providers.forEach((component) => {
if (!this.isComponentInQueue(lang, component.name)) {
this.queue[lang].push(component.name);
let currentObserv = Observable.create(observer => {
this.http.get(`${component.path}/${this.prefix}/${lang}${this.suffix}`)
.map((res: Response) => {
component.json[lang] = res;
}).subscribe((result) => {
observer.next(result);
observer.complete();
}, () => {
observer.next('');
observer.complete();
});
});
observableBatch.push(currentObserv);
}
});
return observableBatch;
}
init(lang: string) {
if (this.queue[lang] === undefined) {
this.queue[lang] = [];
}
}
isComponentInQueue(lang: string, name: string) {
return (this.queue[lang] || []).find(x => x === name) ? true : false;
}
getFullTranslationJSON(lang: string): any {
let result = {};
this.providers
.slice(0)
.sort((a, b) => {
if (a.name === 'app') {
return 1;
}
if (b.name === 'app') {
return -1;
}
return a.name.localeCompare(b.name);
})
.forEach(model => {
if (model.json && model.json[lang]) {
result = ObjectUtils.merge(result, model.json[lang]);
}
});
return result;
}
getTranslation(lang: string): Observable<any> {
let observableBatch = this.getComponentToFetch(lang);
return Observable.create(observer => {
if (observableBatch.length > 0) {
Observable.forkJoin(observableBatch).subscribe(
() => {
let fullTranslation = this.getFullTranslationJSON(lang);
if (fullTranslation) {
observer.next(fullTranslation);
}
observer.complete();
},
(err: any) => {
this.logService.error(err);
});
} else {
let fullTranslation = this.getFullTranslationJSON(lang);
if (fullTranslation) {
observer.next(fullTranslation);
}
}
});
}
}

View File

@@ -0,0 +1,106 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { HttpClientModule } from '@angular/common/http';
import { Injector } from '@angular/core';
import { getTestBed, TestBed } from '@angular/core/testing';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { AppConfigService } from '../app-config/app-config.service';
import { AlfrescoApiService } from '../services/alfresco-api.service';
import { LogService } from './log.service';
import { StorageService } from './storage.service';
import { TranslateLoaderService } from './translate-loader.service';
import { TRANSLATION_PROVIDER, TranslationService } from './translation.service';
import { UserPreferencesService } from './user-preferences.service';
let componentJson1 = ' {"TEST": "This is a test", "TEST2": "This is another test"} ' ;
declare let jasmine: any;
describe('TranslateLoader', () => {
let injector: Injector;
let translationService: TranslationService;
let customLoader: TranslateLoaderService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
HttpClientModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderService
}
})
],
providers: [
TranslationService,
LogService,
AlfrescoApiService,
StorageService,
UserPreferencesService,
AppConfigService,
{
provide: TRANSLATION_PROVIDER,
multi: true,
useValue: {
name: '@alfresco/core',
source: 'assets/ng2-alfresco-core'
}
}
]
});
injector = getTestBed();
translationService = injector.get(TranslationService);
customLoader = <TranslateLoaderService> translationService.translate.currentLoader;
jasmine.Ajax.install();
});
afterEach(() => {
jasmine.Ajax.uninstall();
});
it('should be able to provide any TranslateLoader', () => {
expect(translationService).toBeDefined();
expect(translationService.translate.currentLoader).toBeDefined();
expect(translationService.translate.currentLoader instanceof TranslateLoaderService).toBeTruthy();
});
it('should add the component to the list', () => {
customLoader.registerProvider('login', 'path/login');
expect(customLoader.providerRegistered('login')).toBeTruthy();
});
it('should return the Json translation ', () => {
customLoader.registerProvider('login', 'path/login');
customLoader.getTranslation('en').subscribe(
(response) => {
expect(response).toBeDefined();
expect(response).toEqual(JSON.parse(componentJson1));
}
);
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 200,
contentType: 'application/json',
responseText: componentJson1
});
});
});

View File

@@ -0,0 +1,106 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { HttpClientModule } from '@angular/common/http';
import { Injector } from '@angular/core';
import { getTestBed, TestBed } from '@angular/core/testing';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { AlfrescoApiService } from '../services/alfresco-api.service';
import { AppConfigService } from '../app-config/app-config.service';
import { LogService } from './log.service';
import { StorageService } from './storage.service';
import { TranslateLoaderService } from './translate-loader.service';
import { TRANSLATION_PROVIDER, TranslationService } from './translation.service';
import { UserPreferencesService } from './user-preferences.service';
declare let jasmine: any;
describe('TranslationService', () => {
let injector: Injector;
let translationService: TranslationService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
HttpClientModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderService
}
})
],
providers: [
TranslationService,
LogService,
AlfrescoApiService,
StorageService,
UserPreferencesService,
AppConfigService,
{
provide: TRANSLATION_PROVIDER,
multi: true,
useValue: {
name: '@alfresco/core',
source: 'assets/ng2-alfresco-core'
}
}
]
});
jasmine.Ajax.install();
injector = getTestBed();
translationService = injector.get(TranslationService);
translationService.addTranslationFolder('fake-name', 'fake-path');
});
afterEach(() => {
jasmine.Ajax.uninstall();
});
it('is defined', () => {
expect(translationService).toBeDefined();
expect(translationService instanceof TranslationService).toBeTruthy();
});
it('should be able to get translations of the KEY: TEST', () => {
translationService.get('TEST').subscribe((res: string) => {
expect(res).toEqual('This is a test');
});
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 200,
contentType: 'application/json',
responseText: JSON.stringify({'TEST': 'This is a test', 'TEST2': 'This is another test'})
});
});
it('should be able to get translations of the KEY: TEST2', () => {
translationService.get('TEST2').subscribe((res: string) => {
expect(res).toEqual('This is another test');
});
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 200,
contentType: 'application/json',
responseText: JSON.stringify({'TEST': 'This is a test', 'TEST2': 'This is another test'})
});
});
});

View File

@@ -0,0 +1,86 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Inject, Injectable, InjectionToken, Optional } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { Observable } from 'rxjs/Rx';
import { TranslateLoaderService } from './translate-loader.service';
import { UserPreferencesService } from './user-preferences.service';
export const TRANSLATION_PROVIDER = new InjectionToken('Injection token for translation providers.');
export interface TranslationProvider {
name: string;
source: string;
}
@Injectable()
export class TranslationService {
defaultLang: string;
userLang: string;
customLoader: TranslateLoaderService;
constructor(public translate: TranslateService,
private userPreference: UserPreferencesService,
@Optional() @Inject(TRANSLATION_PROVIDER) providers: TranslationProvider[]) {
this.customLoader = <TranslateLoaderService> this.translate.currentLoader;
this.defaultLang = 'en';
translate.setDefaultLang(this.defaultLang);
if (providers && providers.length > 0) {
for (let provider of providers) {
this.addTranslationFolder(provider.name, provider.source);
}
}
this.userPreference.locale$.subscribe( (locale) => {
this.userLang = locale;
this.use(this.userLang);
});
}
addTranslationFolder(name: string = '', path: string = '') {
if (!this.customLoader.providerRegistered(name)) {
this.customLoader.registerProvider(name, path);
if (this.userLang !== this.defaultLang) {
this.translate.getTranslation(this.defaultLang).subscribe(() => {
this.translate.getTranslation(this.userLang).subscribe(
() => {
this.translate.use(this.userLang);
}
);
});
} else {
this.translate.getTranslation(this.userLang).subscribe(
() => {
this.translate.use(this.userLang);
}
);
}
}
}
use(lang: string): Observable<any> {
this.customLoader.init(lang);
return this.translate.use(lang);
}
get(key: string|Array<string>, interpolateParams?: Object): Observable<string|any> {
return this.translate.get(key, interpolateParams);
}
}

View File

@@ -0,0 +1,251 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { EventEmitter } from '@angular/core';
import { async, TestBed } from '@angular/core/testing';
import { FileModel, FileUploadOptions, FileUploadStatus } from '../models/file.model';
import { AlfrescoApiService } from './alfresco-api.service';
import { SettingsService } from './settings.service';
import { AppConfigService } from '../app-config/app-config.service';
import { AppConfigModule } from '../app-config/app-config.module';
import { AuthenticationService } from './authentication.service';
import { StorageService } from './storage.service';
import { UploadService } from './upload.service';
declare let jasmine: any;
describe('UploadService', () => {
let service: UploadService;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
AppConfigModule
],
providers: [
UploadService,
AlfrescoApiService,
SettingsService,
AuthenticationService,
StorageService
]
}).compileComponents();
}));
beforeEach(() => {
let appConfig: AppConfigService = TestBed.get(AppConfigService);
appConfig.config = {
ecmHost: 'http://localhost:9876/ecm',
files: {
excluded: ['.DS_Store', 'desktop.ini', '.git', '*.git']
}
};
service = TestBed.get(UploadService);
jasmine.Ajax.install();
});
afterEach(() => {
jasmine.Ajax.uninstall();
});
it('should return an empty queue if no elements are added', () => {
expect(service.getQueue().length).toEqual(0);
});
it('should add an element in the queue and returns it', () => {
let filesFake = new FileModel(<File> { name: 'fake-name', size: 10 });
service.addToQueue(filesFake);
expect(service.getQueue().length).toEqual(1);
});
it('should add two elements in the queue and returns them', () => {
let filesFake = [
new FileModel(<File> { name: 'fake-name', size: 10 }),
new FileModel(<File> { name: 'fake-name2', size: 20 })
];
service.addToQueue(...filesFake);
expect(service.getQueue().length).toEqual(2);
});
it('should skip hidden macOS files', () => {
const file1 = new FileModel(new File([''], '.git'));
const file2 = new FileModel(new File([''], 'readme.md'));
const result = service.addToQueue(file1, file2);
expect(result.length).toBe(1);
expect(result[0]).toBe(file2);
});
it('should make XHR done request after the file is added in the queue', (done) => {
let emitter = new EventEmitter();
emitter.subscribe(e => {
expect(e.value).toBe('File uploaded');
done();
});
let fileFake = new FileModel(
<File> { name: 'fake-name', size: 10 },
<FileUploadOptions> { parentId: '-root-', path: 'fake-dir' }
);
service.addToQueue(fileFake);
service.uploadFilesInTheQueue(emitter);
let request = jasmine.Ajax.requests.mostRecent();
expect(request.url).toBe('http://localhost:9876/ecm/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children?autoRename=true');
expect(request.method).toBe('POST');
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 200,
contentType: 'text/plain',
responseText: 'File uploaded'
});
});
it('should make XHR error request after an error occur', (done) => {
let emitter = new EventEmitter();
emitter.subscribe(e => {
expect(e.value).toBe('Error file uploaded');
done();
});
let fileFake = new FileModel(
<File> { name: 'fake-name', size: 10 },
<FileUploadOptions> { parentId: '-root-' }
);
service.addToQueue(fileFake);
service.uploadFilesInTheQueue(emitter);
expect(jasmine.Ajax.requests.mostRecent().url)
.toBe('http://localhost:9876/ecm/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children?autoRename=true');
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 404,
contentType: 'text/plain',
responseText: 'Error file uploaded'
});
});
it('should make XHR abort request after the xhr abort is called', (done) => {
let emitter = new EventEmitter();
emitter.subscribe(e => {
expect(e.value).toEqual('File aborted');
done();
});
let fileFake = new FileModel(<File> { name: 'fake-name', size: 10 });
service.addToQueue(fileFake);
service.uploadFilesInTheQueue(emitter);
let file = service.getQueue();
service.cancelUpload(...file);
});
it('If versioning is true autoRename should not be present and majorVersion should be a param', () => {
let emitter = new EventEmitter();
const filesFake = new FileModel(<File> { name: 'fake-name', size: 10 }, { newVersion: true });
service.addToQueue(filesFake);
service.uploadFilesInTheQueue(emitter);
expect(jasmine.Ajax.requests.mostRecent().url.endsWith('autoRename=true')).toBe(false);
expect(jasmine.Ajax.requests.mostRecent().params.has('majorVersion')).toBe(true);
});
it('should use custom root folder ID given to the service', (done) => {
let emitter = new EventEmitter();
emitter.subscribe(e => {
expect(e.value).toBe('File uploaded');
done();
});
let filesFake = new FileModel(
<File> { name: 'fake-name', size: 10 },
<FileUploadOptions> { parentId: '123', path: 'fake-dir' }
);
service.addToQueue(filesFake);
service.uploadFilesInTheQueue(emitter);
let request = jasmine.Ajax.requests.mostRecent();
expect(request.url).toBe('http://localhost:9876/ecm/alfresco/api/-default-/public/alfresco/versions/1/nodes/123/children?autoRename=true');
expect(request.method).toBe('POST');
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 200,
contentType: 'text/plain',
responseText: 'File uploaded'
});
});
it('should start downloading the next one if a file of the list is aborted', (done) => {
let emitter = new EventEmitter();
service.fileUploadAborted.subscribe(e => {
expect(e).not.toBeNull();
});
service.fileUploadCancelled.subscribe(e => {
expect(e).not.toBeNull();
done();
});
let fileFake1 = new FileModel(<File> { name: 'fake-name1', size: 10 });
let fileFake2 = new FileModel(<File> { name: 'fake-name2', size: 10 });
let filelist = [fileFake1, fileFake2];
service.addToQueue(...filelist);
service.uploadFilesInTheQueue(emitter);
let file = service.getQueue();
service.cancelUpload(...file);
});
it('should remove from the queue all the files in the exluded list', () => {
const file1 = new FileModel(new File([''], '.git'));
const file2 = new FileModel(new File([''], '.DS_Store'));
const file3 = new FileModel(new File([''], 'desktop.ini'));
const file4 = new FileModel(new File([''], 'readme.md'));
const file5 = new FileModel(new File([''], 'test.git'));
const result = service.addToQueue(file1, file2, file3, file4, file5);
expect(result.length).toBe(1);
expect(result[0]).toBe(file4);
});
it('should call onUploadDeleted if file was deleted', () => {
const file = <any> ({ status: FileUploadStatus.Deleted });
spyOn(service.fileUploadDeleted, 'next');
service.cancelUpload(file);
expect(service.fileUploadDeleted.next).toHaveBeenCalled();
});
it('should call fileUploadError if file has error status', () => {
const file = <any> ({ status: FileUploadStatus.Error });
spyOn(service.fileUploadError, 'next');
service.cancelUpload(file);
expect(service.fileUploadError.next).toHaveBeenCalled();
});
it('should call fileUploadCancelled if file is in pending', () => {
const file = <any> ({ status: FileUploadStatus.Pending });
spyOn(service.fileUploadCancelled, 'next');
service.cancelUpload(file);
expect(service.fileUploadCancelled.next).toHaveBeenCalled();
});
});

View File

@@ -0,0 +1,294 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { EventEmitter, Injectable } from '@angular/core';
import * as minimatch from 'minimatch';
import { Subject } from 'rxjs/Rx';
import { AppConfigService } from '../app-config/app-config.service';
import { FileUploadCompleteEvent, FileUploadDeleteEvent, FileUploadErrorEvent, FileUploadEvent } from '../events/file.event';
import { FileModel, FileUploadProgress, FileUploadStatus } from '../models/file.model';
import { AlfrescoApiService } from './alfresco-api.service';
@Injectable()
export class UploadService {
private queue: FileModel[] = [];
private cache: { [key: string]: any } = {};
private totalComplete: number = 0;
private totalAborted: number = 0;
private totalError: number = 0;
private activeTask: Promise<any> = null;
private excludedFileList: String[] = [];
queueChanged: Subject<FileModel[]> = new Subject<FileModel[]>();
fileUpload: Subject<FileUploadEvent> = new Subject<FileUploadEvent>();
fileUploadStarting: Subject<FileUploadEvent> = new Subject<FileUploadEvent>();
fileUploadCancelled: Subject<FileUploadEvent> = new Subject<FileUploadEvent>();
fileUploadProgress: Subject<FileUploadEvent> = new Subject<FileUploadEvent>();
fileUploadAborted: Subject<FileUploadEvent> = new Subject<FileUploadEvent>();
fileUploadError: Subject<FileUploadErrorEvent> = new Subject<FileUploadErrorEvent>();
fileUploadComplete: Subject<FileUploadCompleteEvent> = new Subject<FileUploadCompleteEvent>();
fileUploadDeleted: Subject<FileUploadDeleteEvent> = new Subject<FileUploadDeleteEvent>();
fileDeleted: Subject<string> = new Subject<string>();
constructor(private apiService: AlfrescoApiService, private appConfigService: AppConfigService) {
this.excludedFileList = <String[]> this.appConfigService.get('files.excluded');
}
/**
* Checks whether the service is uploading a file.
*
* @returns {boolean}
*
* @memberof UploadService
*/
isUploading(): boolean {
return this.activeTask ? true : false;
}
/**
* Returns the file Queue
*
* @return {FileModel[]} - files in the upload queue.
*/
getQueue(): FileModel[] {
return this.queue;
}
/**
* Add files to the uploading queue to be uploaded.
*
* Examples:
* addToQueue(file); // pass one file
* addToQueue(file1, file2, file3); // pass multiple files
* addToQueue(...[file1, file2, file3]); // pass an array of files
*/
addToQueue(...files: FileModel[]): FileModel[] {
const allowedFiles = files.filter(f => this.filterElement(f));
this.queue = this.queue.concat(allowedFiles);
this.queueChanged.next(this.queue);
return allowedFiles;
}
private filterElement(file: FileModel) {
let isAllowed = true;
if (this.excludedFileList) {
isAllowed = this.excludedFileList.filter(expr => minimatch(file.name, expr)).length === 0;
}
return isAllowed;
}
/**
* Pick all the files in the queue that are not been uploaded yet and upload it into the directory folder.
*
* @param {EventEmitter<any>} emitter @deprecated emitter to invoke on file status change
*
* @memberof UploadService
*/
uploadFilesInTheQueue(emitter: EventEmitter<any>): void {
if (!this.activeTask) {
let file = this.queue.find(f => f.status === FileUploadStatus.Pending);
if (file) {
this.onUploadStarting(file);
const promise = this.beginUpload(file, emitter);
this.activeTask = promise;
this.cache[file.id] = promise;
let next = () => {
this.activeTask = null;
setTimeout(() => this.uploadFilesInTheQueue(emitter), 100);
};
promise.next = next;
promise.then(
() => next(),
() => next()
);
}
}
}
cancelUpload(...files: FileModel[]) {
files.forEach(file => {
const promise = this.cache[file.id];
if (promise) {
promise.abort();
delete this.cache[file.id];
} else {
const performAction = this.getAction(file);
performAction();
}
});
}
clearQueue() {
this.queue = [];
this.totalComplete = 0;
this.totalAborted = 0;
this.totalError = 0;
}
getUploadPromise(file: FileModel) {
let opts: any = {
renditions: 'doclib'
};
if (file.options.newVersion === true) {
opts.overwrite = true;
opts.majorVersion = true;
} else {
opts.autoRename = true;
}
return this.apiService.getInstance().upload.uploadFile(
file.file,
file.options.path,
file.options.parentId,
null,
opts
);
}
private beginUpload(file: FileModel, /* @deprecated */emitter: EventEmitter<any>): any {
let promise = this.getUploadPromise(file);
promise.on('progress', (progress: FileUploadProgress) => {
this.onUploadProgress(file, progress);
})
.on('abort', () => {
this.onUploadAborted(file);
emitter.emit({ value: 'File aborted' });
})
.on('error', err => {
this.onUploadError(file, err);
emitter.emit({ value: 'Error file uploaded' });
})
.on('success', data => {
this.onUploadComplete(file, data);
emitter.emit({ value: data });
})
.catch(err => {
throw err;
});
return promise;
}
private onUploadStarting(file: FileModel): void {
if (file) {
file.status = FileUploadStatus.Starting;
const event = new FileUploadEvent(file, FileUploadStatus.Starting);
this.fileUpload.next(event);
this.fileUploadStarting.next(event);
}
}
private onUploadProgress(file: FileModel, progress: FileUploadProgress): void {
if (file) {
file.progress = progress;
file.status = FileUploadStatus.Progress;
const event = new FileUploadEvent(file, FileUploadStatus.Progress);
this.fileUpload.next(event);
this.fileUploadProgress.next(event);
}
}
private onUploadError(file: FileModel, error: any): void {
if (file) {
file.status = FileUploadStatus.Error;
this.totalError++;
const promise = this.cache[file.id];
if (promise) {
delete this.cache[file.id];
}
const event = new FileUploadErrorEvent(file, error, this.totalError);
this.fileUpload.next(event);
this.fileUploadError.next(event);
}
}
private onUploadComplete(file: FileModel, data: any): void {
if (file) {
file.status = FileUploadStatus.Complete;
file.data = data;
this.totalComplete++;
const promise = this.cache[file.id];
if (promise) {
delete this.cache[file.id];
}
const event = new FileUploadCompleteEvent(file, this.totalComplete, data, this.totalAborted);
this.fileUpload.next(event);
this.fileUploadComplete.next(event);
}
}
private onUploadAborted(file: FileModel): void {
if (file) {
file.status = FileUploadStatus.Aborted;
this.totalAborted++;
const promise = this.cache[file.id];
if (promise) {
delete this.cache[file.id];
}
const event = new FileUploadEvent(file, FileUploadStatus.Aborted);
this.fileUpload.next(event);
this.fileUploadAborted.next(event);
promise.next();
}
}
private onUploadCancelled(file: FileModel): void {
if (file) {
file.status = FileUploadStatus.Cancelled;
const event = new FileUploadEvent(file, FileUploadStatus.Cancelled);
this.fileUpload.next(event);
this.fileUploadCancelled.next(event);
}
}
private onUploadDeleted(file: FileModel): void {
if (file) {
file.status = FileUploadStatus.Deleted;
this.totalComplete--;
const event = new FileUploadDeleteEvent(file, this.totalComplete);
this.fileUpload.next(event);
this.fileUploadDeleted.next(event);
}
}
private getAction(file) {
const actions = {
[FileUploadStatus.Pending]: () => this.onUploadCancelled(file),
[FileUploadStatus.Deleted]: () => this.onUploadDeleted(file),
[FileUploadStatus.Error]: () => this.onUploadError(file, null)
};
return actions[file.status];
}
}

View File

@@ -0,0 +1,135 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { async, TestBed } from '@angular/core/testing';
import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core';
import { DirectiveModule } from '../directives';
import { AppConfigService } from '../app-config/app-config.service';
import { AppConfigModule } from '../app-config/app-config.module';
import { StorageService } from './storage.service';
import { TranslateLoaderService } from './translate-loader.service';
import { UserPreferencesService } from './user-preferences.service';
describe('UserPreferencesService', () => {
const defaultPaginationSize: number = 10;
let preferences: UserPreferencesService;
let storage: StorageService;
let appConfig: AppConfigService;
let translate: TranslateService;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
AppConfigModule,
DirectiveModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderService
}
})
],
providers: [
UserPreferencesService
]
}).compileComponents();
}));
beforeEach(() => {
appConfig = TestBed.get(AppConfigService);
appConfig.config = {
pagination: {
size: 10
}
};
preferences = TestBed.get(UserPreferencesService);
storage = TestBed.get(StorageService);
translate = TestBed.get(TranslateService);
});
it('should get default pagination from app config', () => {
expect(preferences.paginationSize).toBe(defaultPaginationSize);
});
it('should use [GUEST] as default storage prefix', () => {
expect(preferences.getStoragePrefix()).toBe('GUEST');
});
it('should change storage prefix', () => {
preferences.setStoragePrefix('USER_A');
expect(preferences.getStoragePrefix()).toBe('USER_A');
});
it('should format property key for default prefix', () => {
preferences.setStoragePrefix(null);
expect(preferences.getPropertyKey('propertyA')).toBe('GUEST__propertyA');
});
it('should format property key for custom prefix', () => {
preferences.setStoragePrefix('USER_A');
expect(preferences.getPropertyKey('propertyA')).toBe('USER_A__propertyA');
});
it('should save value with default prefix', () => {
preferences.set('propertyA', 'valueA');
const propertyKey = preferences.getPropertyKey('propertyA');
expect(storage.getItem(propertyKey)).toBe('valueA');
});
it('should save value with custom prefix', () => {
preferences.setStoragePrefix('USER_A');
preferences.set('propertyA', 'valueA');
const propertyKey = preferences.getPropertyKey('propertyA');
expect(storage.getItem(propertyKey)).toBe('valueA');
});
it('should store custom pagination settings for default prefix', () => {
preferences.paginationSize = 5;
expect(preferences.paginationSize).toBe(5);
});
it('should return default paginationSize value', () => {
preferences.set('PAGINATION_SIZE', 0);
expect(preferences.paginationSize).toBe(defaultPaginationSize);
});
it('should return as default locale the app.config locate as first', () => {
appConfig.config.locale = 'fake-locate-config';
spyOn(translate, 'getBrowserLang').and.returnValue('fake-locate-browser');
expect(preferences.getDefaultLocale()).toBe('fake-locate-config');
});
it('should return as default locale the browser locale as second', () => {
spyOn(translate, 'getBrowserLang').and.returnValue('fake-locate-browser');
expect(preferences.getDefaultLocale()).toBe('fake-locate-browser');
});
it('should return as default locale the component propery as third ', () => {
spyOn(translate, 'getBrowserLang').and.stub();
expect(preferences.getDefaultLocale()).toBe('en');
});
it('should return as locale the store locate', () => {
preferences.locale = 'fake-store-locate';
appConfig.config.locale = 'fake-locate-config';
spyOn(translate, 'getBrowserLang').and.returnValue('fake-locate-browser');
expect(preferences.locale).toBe('fake-store-locate');
});
});

View File

@@ -0,0 +1,119 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Rx';
import { AppConfigService } from '../app-config/app-config.service';
import { AlfrescoApiService } from './alfresco-api.service';
import { StorageService } from './storage.service';
@Injectable()
export class UserPreferencesService {
private defaults = {
paginationSize: 25,
locale: 'en'
};
private localeSubject: BehaviorSubject<string> ;
locale$: Observable<string>;
constructor(
public translate: TranslateService,
private appConfig: AppConfigService,
private storage: StorageService,
private apiService: AlfrescoApiService
) {
const currentLocale = this.locale || this.getDefaultLocale();
this.localeSubject = new BehaviorSubject(currentLocale);
this.locale$ = this.localeSubject.asObservable();
this.defaults.paginationSize = appConfig.get('pagination.size', 25);
}
get(property: string, defaultValue?: string): string {
const key = this.getPropertyKey(property);
const value = this.storage.getItem(key);
if (value === undefined) {
return defaultValue;
}
return value;
}
set(property: string, value: any) {
if (!property) { return; }
this.storage.setItem(
this.getPropertyKey(property),
value
);
}
getStoragePrefix(): string {
return this.storage.getItem('USER_PROFILE') || 'GUEST';
}
setStoragePrefix(value: string) {
this.storage.setItem('USER_PROFILE', value || 'GUEST');
}
getPropertyKey(property: string): string {
return `${this.getStoragePrefix()}__${property}`;
}
set authType(value: string) {
this.storage.setItem('AUTH_TYPE', value);
this.apiService.reset();
}
get authType(): string {
return this.storage.getItem('AUTH_TYPE') || 'ALL';
}
set disableCSRF(value: boolean) {
this.set('DISABLE_CSRF', value);
this.apiService.reset();
}
get disableCSRF(): boolean {
return this.get('DISABLE_CSRF') === 'true';
}
set paginationSize(value: number) {
this.set('PAGINATION_SIZE', value);
}
get paginationSize(): number {
return Number(this.get('PAGINATION_SIZE')) || this.defaults.paginationSize;
}
get locale(): string {
const locale = this.get('LOCALE');
return locale;
}
set locale(value: string) {
this.localeSubject.next(value);
this.set('LOCALE', value);
}
public getDefaultLocale(): string {
return this.appConfig.get<string>('locale') || this.translate.getBrowserLang() || 'en';
}
}