diff --git a/.angular-cli.json b/.angular-cli.json
index 5d35d54f9..a446cd5b7 100644
--- a/.angular-cli.json
+++ b/.angular-cli.json
@@ -10,6 +10,7 @@
"assets": [
"assets",
"favicon-96x96.png",
+ "app.config.json",
{ "glob": "**/*", "input": "../node_modules/ng2-alfresco-core/bundles/assets", "output": "./assets/" },
{ "glob": "**/*", "input": "../node_modules/ng2-alfresco-datatable/bundles/assets", "output": "./assets/" },
diff --git a/package.json b/package.json
index a81acdfd6..1ad3470f6 100644
--- a/package.json
+++ b/package.json
@@ -22,7 +22,7 @@
"@angular/material": "2.0.0-beta.10",
"@angular/platform-browser": "4.3.6",
"@angular/platform-browser-dynamic": "4.3.6",
- "@angular/router": "~3.1.1",
+ "@angular/router": "4.3.6",
"@ngx-translate/core": "7.0.0",
"alfresco-js-api": "1.9.0",
"core-js": "^2.4.1",
diff --git a/src/app.config.json b/src/app.config.json
new file mode 100644
index 000000000..38f5a8381
--- /dev/null
+++ b/src/app.config.json
@@ -0,0 +1,18 @@
+{
+ "ecmHost": "http://{hostname}:{port}/ecm",
+ "application": {
+ "name": "Alfresco Example Content Application",
+ "build": "1234"
+ },
+ "document-list": {
+ "supportedPageSizes": [25, 50, 100]
+ },
+ "files": {
+ "excluded": [
+ ".DS_Store",
+ "desktop.ini",
+ "thumbs.db",
+ ".git"
+ ]
+ }
+}
diff --git a/src/app/app.component.html b/src/app/app.component.html
index 46d517b68..0680b43f9 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -1,20 +1 @@
-
-
-
- Welcome to {{title}}!
-
-

-
-Here are some links to help you start:
-
-
+
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 7d943bc90..82c93e10f 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -1,10 +1,60 @@
-import { Component } from '@angular/core';
+/*!
+ * @license
+ * Copyright 2017 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 { Component, OnInit } from '@angular/core';
+import { Router, ActivatedRoute, NavigationEnd } from '@angular/router';
+import { TranslationService, PageTitleService } from 'ng2-alfresco-core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
-export class AppComponent {
- title = 'app';
+export class AppComponent implements OnInit {
+ constructor(
+ private route: ActivatedRoute,
+ private router: Router,
+ private pageTitle: PageTitleService,
+ private translateService: TranslationService) {
+ }
+
+ ngOnInit() {
+ const { router, pageTitle, route, translateService } = this;
+
+ router
+ .events
+ .filter(event => event instanceof NavigationEnd)
+ .subscribe(() => {
+ let currentRoute = route.root;
+
+ while (currentRoute.firstChild) {
+ currentRoute = currentRoute.firstChild;
+ }
+
+ const snapshot: any = currentRoute.snapshot || {};
+ const data: any = snapshot.data || {};
+
+ if (data.i18nTitle) {
+ translateService.get(data.i18nTitle).subscribe(title => {
+ pageTitle.setTitle(title);
+ });
+ } else {
+ pageTitle.setTitle(data.title || '');
+ }
+ });
+ }
}
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 8a86c93b3..da472df9c 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -1,17 +1,25 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
+import { RouterModule } from '@angular/router';
import { AdfModule } from './adf.module';
import { AppComponent } from './app.component';
+import { APP_ROUTES } from './app.routes';
+
+import { LoginComponent } from './components/login/login.component';
@NgModule({
- declarations: [
- AppComponent
- ],
imports: [
BrowserModule,
+ RouterModule.forRoot(APP_ROUTES, {
+ enableTracing: true
+ }),
AdfModule
],
+ declarations: [
+ AppComponent,
+ LoginComponent
+ ],
providers: [],
bootstrap: [AppComponent]
})
diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts
new file mode 100644
index 000000000..7c65b7533
--- /dev/null
+++ b/src/app/app.routes.ts
@@ -0,0 +1,35 @@
+/*!
+ * @license
+ * Copyright 2017 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 { Routes } from '@angular/router';
+
+import { LoginComponent } from './components/login/login.component';
+
+export const APP_ROUTES: Routes = [
+ {
+ path: '**',
+ redirectTo: '/login',
+ pathMatch: 'full'
+ },
+ {
+ path: 'login',
+ component: LoginComponent,
+ data: {
+ title: 'Sign in'
+ }
+ }
+];
diff --git a/src/app/components/login/login.component.html b/src/app/components/login/login.component.html
new file mode 100644
index 000000000..c545e59f7
--- /dev/null
+++ b/src/app/components/login/login.component.html
@@ -0,0 +1,8 @@
+
+
diff --git a/src/app/components/login/login.component.spec.ts b/src/app/components/login/login.component.spec.ts
new file mode 100644
index 000000000..1580b1e82
--- /dev/null
+++ b/src/app/components/login/login.component.spec.ts
@@ -0,0 +1,144 @@
+/*!
+ * @license
+ * Copyright 2017 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 { RouterTestingModule } from '@angular/router/testing';
+import { Router, ActivatedRoute } from '@angular/router';
+import { TestBed, async } from '@angular/core/testing';
+import { Observable } from 'rxjs/Rx';
+import { AlfrescoAuthenticationService, UserPreferencesService } from 'ng2-alfresco-core';
+import { LoginModule } from 'ng2-alfresco-login';
+
+import { LoginComponent } from './login.component';
+
+describe('LoginComponent', () => {
+ let router: Router;
+ let route: ActivatedRoute;
+ let authService: AlfrescoAuthenticationService;
+ let userPrefService: UserPreferencesService;
+
+ class TestConfig {
+ private testBed;
+ private componentInstance;
+ private fixture;
+
+ constructor(config: any = {}) {
+ const routerProvider = {
+ provide: Router,
+ useValue: {
+ navigateByUrl: jasmine.createSpy('navigateByUrl'),
+ navigate: jasmine.createSpy('navigate')
+ }
+ };
+
+ const authProvider = {
+ provide: AlfrescoAuthenticationService,
+ useValue: {
+ isEcmLoggedIn: jasmine.createSpy('navigateByUrl')
+ .and.returnValue(config.isEcmLoggedIn || false)
+ }
+ };
+
+ const preferencesProvider = {
+ provide: UserPreferencesService,
+ useValue: {
+ setStoragePrefix: jasmine.createSpy('setStoragePrefix')
+ }
+ };
+
+ this.testBed = TestBed.configureTestingModule({
+ imports: [
+ RouterTestingModule,
+ LoginModule
+ ],
+ declarations: [
+ LoginComponent
+ ],
+ providers: [
+ routerProvider,
+ authProvider,
+ preferencesProvider,
+ {
+ provide: ActivatedRoute,
+ useValue: {
+ params: Observable.of({ redirect: config.redirect })
+ }
+ }
+ ]
+ });
+
+ this.fixture = TestBed.createComponent(LoginComponent);
+ this.componentInstance = this.fixture.componentInstance;
+ this.fixture.detectChanges();
+ }
+
+ get userPrefService() {
+ return TestBed.get(UserPreferencesService);
+ }
+
+ get authService() {
+ return TestBed.get(AlfrescoAuthenticationService);
+ }
+
+ get routerService() {
+ return TestBed.get(Router);
+ }
+
+ get component() {
+ return this.componentInstance;
+ }
+ }
+
+ it('load app when user is already logged in', () => {
+ const testConfig = new TestConfig({
+ isEcmLoggedIn: true
+ });
+
+ expect(testConfig.routerService.navigateByUrl).toHaveBeenCalled();
+ });
+
+ it('requires user to be logged in', () => {
+ const testConfig = new TestConfig({
+ isEcmLoggedIn: false,
+ redirect: '/personal-files'
+ });
+
+ expect(testConfig.routerService.navigate).toHaveBeenCalledWith(['/login', {}]);
+ });
+
+ describe('onLoginSuccess()', () => {
+ let testConfig;
+
+ beforeEach(() => {
+ testConfig = new TestConfig({
+ isEcmLoggedIn: false,
+ redirect: 'somewhere-over-the-rainbow'
+ });
+ });
+
+ it('redirects on success', () => {
+ testConfig.component.onLoginSuccess();
+
+ expect(testConfig.routerService.navigateByUrl).toHaveBeenCalledWith('somewhere-over-the-rainbow');
+ });
+
+ it('sets user preference store prefix', () => {
+ testConfig.component.onLoginSuccess({ username: 'bogus' });
+
+ expect(testConfig.userPrefService.setStoragePrefix).toHaveBeenCalledWith('bogus');
+ });
+ });
+});
diff --git a/src/app/components/login/login.component.ts b/src/app/components/login/login.component.ts
new file mode 100644
index 000000000..bd0713c51
--- /dev/null
+++ b/src/app/components/login/login.component.ts
@@ -0,0 +1,69 @@
+/*!
+ * @license
+ * Copyright 2017 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 { Component } from '@angular/core';
+import { Router, ActivatedRoute } from '@angular/router';
+import { Validators } from '@angular/forms';
+
+import { AlfrescoAuthenticationService, UserPreferencesService } from 'ng2-alfresco-core';
+
+const skipRedirectUrls: string[] = [
+ '/logout',
+ '/personal-files'
+];
+
+@Component({
+ templateUrl: './login.component.html'
+})
+export class LoginComponent {
+
+ private redirectUrl = '';
+
+ constructor(
+ private router: Router,
+ private route: ActivatedRoute,
+ private auth: AlfrescoAuthenticationService,
+ private userPreferences: UserPreferencesService
+ ) {
+ if (auth.isEcmLoggedIn()) {
+ this.redirect();
+ }
+
+ route.params.subscribe((params: any) => {
+ if (skipRedirectUrls.indexOf(params.redirect) > -1) {
+ const remainingParams = Object.assign({}, params);
+
+ delete remainingParams.redirect;
+
+ router.navigate(['/login', remainingParams]);
+ }
+
+ this.redirectUrl = params.redirect;
+ });
+ }
+
+ redirect() {
+ this.router.navigateByUrl(this.redirectUrl || '');
+ }
+
+ onLoginSuccess(data) {
+ if (data && data.username) {
+ this.userPreferences.setStoragePrefix(data.username);
+ }
+ this.redirect();
+ }
+}
diff --git a/yarn.lock b/yarn.lock
index 5aeddac5d..7105a9694 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -164,10 +164,6 @@
dependencies:
tslib "^1.7.1"
-"@angular/router@~3.1.1":
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/@angular/router/-/router-3.1.2.tgz#1046c943ace795027e2e5057df8c897da968fcd6"
-
"@angular/tsc-wrapped@4.3.6":
version "4.3.6"
resolved "https://registry.yarnpkg.com/@angular/tsc-wrapped/-/tsc-wrapped-4.3.6.tgz#1aa66e0ab2c4799a4ad14b675e13953aa5fcd436"