* fix upload area snackbar behaviour

* SASS support for components

ability to use '.scss' files from within components

* [ADF-610] Upload button and DnD area should not upload hidden files and folders (#1908)

[ADF-610]  upload cleanup
- more strongly typing
- api improvements

* Upload cleanup and api improvements

- remove old unused settings (formFields variable)
- individual options for uploaded files (i.e. versioning)
- upload button and drag-and-drop area now set individual settings for file versioning

* exclude hidden files from upload

* [ADF-640] reload document list on folder upload (#1895)

* reload document list on folder upload

- extend UploadService with 'folderCreated' event to be able reacting on folder uploads globally
- extend Demo Shell to reload document list on UploadService events (folderCreated)

* readme updates

* [ADF-621] show drop effect on folders only (#1897)

* show drop effect on folders only

- fix `hasValue` api for data rows (avoid 'false' value to be evaluated as missing value)
- support for evaluating drop support for rows
- document list enables upload zones for folders only

* api improvements as per code review

* [ADF-242] Fixed behaviour for saving/deleting reports (#1905)

* [ADF-242] - fix for deleting - saving a report

* [ADF - 242] added test for fixed feature on reports save - delete

* Added translation key

* [ADF-604] Upgrade @angular/material to latest version (#1909)

* update dependencies and module imports

* fix template warnings and fix import issues

* migrate Activiti Form to MdTabsModule

* fix unit tests

* fix tests

* fix unit tests

* fix unit tests

* disable test that fails only on travis

* upgrade activiti form component to angular/material

* fix test (remove MDL class check)

* [ADF-613] Add plain text viewer (#1873)

* add plain text viewer

* different devices optimizations

* returns types

* [ADF-573] support for toggling enabled state (#1912)

* [ADF-602] Accordion component - Add basic documentation (#1913)

* Add basic documentation Accordion component

* Update README.md

* [ADF-680] Added previous page check when page has no more elements (#1911)

* [ADF-242] - fix for deleting - saving a report

* [ADF - 242] added test for fixed feature on reports save - delete

* [ADF-680] - Fixed behaviour when deleting all files on last page of document list

* Start adding test for documentlist check

* Added test for check double page load

* [ADF-680] - removed commented test code

* [ADF-680] Added changes from peer review

* [ADF-680] added return type

* [ADF-667] selection mode and row styles (#1914)

* selection mode and row styles

- single/multiple/none selection modes for DataTable component (and Document List)
- support for custom row styles (inline and classname values)
- fix karma config (material themes)
- readme updates
- package-lock.json files for NPM5 support
- updated DataTable demo to demonstrate selection modes and row styles

* remove package lock files

* move demo projects to webpack (#1915)

* wav and Mp3 enabling viewer (#1916)

* add option only demo shell version change for update version script

* ADF-402 add the show diagram button (#1917)

* [ADF-707] Ability to select a row on a dynamic table (#1921)

* [ADF-710] Create an Process Attachment List component (#1919)

* added new component to list the process attachments with view, download and delete functionality

* added unit test cases for activit-process-attachment-list component

* exported new process attachment list component

* added documentation for process-attachment-list component

* [ADF-712] Task Attachment - Provide a way to attach a new content (#1898)

* create button, download, view functionality added in task attachment list component

* created sevice to attach document to task

* added new component to create/uplaod attachment to task

* added new component to create/uplaod attachment to task

* added test case for create task attachment component

* added test case for create task attachment component

* added input to block upload document  to ECM

* fixed create task attachment spec file issue

* changed alfresco-upload to alfresco-core upload directive

* removed attachCreate button and emitter from task-attachment-list

* removed uploadToEcm input and checkValidity method from alfresco-upload

* added documentation for task-attachment-list and create-task-attachment components

* [ADF-696] Entire accordion group header should be clickable (#1918)

* #ADF-696 Added new input to show/hide expand icon, click event is activated for the complete heading

* #ADF-696 tslint fix

* #ADF-696 Added documentation for new input and removed unwanted div

* [ADF-721] Fix translation reference for dev task (#1923)

* move translation files in the bundles folder

* fix after review
ripristinate tslint and remove override tsconfig

* [ADF-709] add autofocus when a new row is added on dynamic table widget (#1927)

* [ADF-709] add autofocus when a new row is added on dynamic table widget

* [ADF-709] removed wrong reference for template

* [ADF-713] Process Attachment - Provide a way to attach a new content (#1920)

* added service to get all the related content of the process instance

* added new component to create/upload attachment for process instance

* added unit test cases for create-process-attachment component

* exported create-process-attachment component

* added documentation for create-process-attachment component

* Add data-automation-id to multi select checkbox (#1924)

* [ADF-571] upload feature rework (#1922)

* upload feature rework

lots of improvements for upload dialog and underlying services

* readme update

- readme cleanup
- remove some old comments from code
- update readme with new events for Upload Service

* restore prerequisites section in readme

* fix i18n issue with webpack

* exported report and chart models (#1925)

* fix file upload bug (#1928)

- proper extraction of File objects from the FileList

* lock files for npm 5 (#1930)

add lock files for npm v5; does nothing for earlier versions, so is not harmful

* Source Mapping is not working on test debugging (#1931)

* coverage single components run fix

* remove spec.ts from coverage

* make the coverage and the istanbul-instrumenter-loader works only over the console test because a problem on the remapping for the browser test

* move tslint on the main folder of any component

* remove build:w from readme

* stop build tslint error also in spec files

* clear karma file from unnecessary files

* add set -f for build all script in order to accept *

* fix lint problem and failing tests

* fix failing test search component

* add loader test for viewer

* fix tslint error userinfo

* --max_old_space_size=2048 remove

* fix tslint error uploader unused EventEmitter

* remove spec|index|.*mock|.*model|.*event from coverage

* move coverage separate file and get component to calculate coverage as input

* remove old 'banned' demo from login screen (#1929)

* add sleep time flag in publish script

* rollback demo tag

* fix pacakge.json tag

* [ADF-686] add blobFile as input (#1933)

* coverage fix (#1934)

* [ADF-702] Task/Process Filter - Provide a way to change the default filter (#1932)

* [ADF-702] Task/Process Filter - Provide a way to change the default filter

* Provide a way to select a custom menu filter

* Improve activiti process filter

* Add internal link

* Change link name

* add link

* [ADF-744] Attachment List is not displayed within Processes. (#1937)

* Use the adf process attachment list indise demo shell

* Change documentation

* support for healdess chrome (#1939)

* #ADF-696 Now accordion opens/closes on click of group header along with emitting heading click event (#1936)

* add info and link on current last git commit (#1940)

* [ADF-754] toolbar component (#1938)

* toolbar component

- simple toolbar component (core lib)
- readme updates (core lib)
- update demo shell with toolbar component demo (document list)

* update unit tests

* [ADF-763] Add Chrome default browser for karma chrome launcher for Chrome versi… (#1941)

* Add Chrome default browser for karma chrome launcher for Chrome version<59

* Fixing intermittently failing test in ng2-activiti-analytics component

* Adding new icon (sent) for Bootstrap to Material icon mapping (#1943)

* fix blob input in text viewer (#1942)

* GitHub issue & pull request template change (#1945)

* Update ISSUE_TEMPLATE.md

* Update PULL_REQUEST_TEMPLATE.md

* [ADF-689] Fix alfresco-document-menu-action styling (z-index) (#1944)

* fix translation wrong folder creation issue

* [ADF-773] Fix datatable custom template render (#1947)

* [ADF-780] centralised call for process filters api (#1950)

* [ADF-780] centralised call for process filters api

* [ADF-780] updated conversion to string

* [ADF-741] Add the create task attachment component to the demo shell (#1946)

* Add the create task attachment component to the demo shell

* Add translation keys

* Add return to methods

* fix thumbnail task process list (#1951)

* [ADF-643] upload enhancements (#1949)

* rework folder uploading

- flatterns hierarchy on folder upload
- performs a single traversal for the entire folder heirarchy and ends with a comple file list
- allows now dropping folders on existing folders
- overall code improvements

* fix unit tests

* readme updates

* clean old and unused code

* code cleanup

* limit concurrent uploads

* update code as per review

* fix upload button for Safari

* fixes for Safari

- Safari compatibility
- code updates based on review

* fix code

* fix unit tests

* [ADF-589] Login component different bug fixes (#1953)

* Basic style changes

* Further design changes

* Responsive design fixes

* Different sign in button style for the different login steps

* #ADF-780 Fixed getProcessFilterByName to get the correct filter for the given appId and name (#1952)

* fix issues with the require keyword (angular cli) (#1954)

* [ADF-799] add HappyPack to webpack conf (#1956)

* update npm5 lock files

* [ADF-740] Add button for process attachment list (#1955)

* [ADF-740] adding button to allow user to upload related content on process instance

* [ADF-740] add button for attachment content list for process

* changed locatin for translation

* [ADF-740] added test for add button for process attach

* [ADF-740] added PR request changes

* [ADF-802] fix error on uploading file to attachment list (#1957)

* [ADF-802] fix error on uploading file to attachment list

* [ADF-802] improved for loop

* [ADF-797] remove dist folder from npm distributed package , leave src and bundles (#1961)

* [ADF-804] webpack proxy setup to avoid CORS problem (#1960)

* package lock update

* update travis to node 8 (#1965)

* upload service exposes created nodes (#1964)

* [ADF-591] documentation refinements (#1959)

* refine ng2-activiti-analytics

* refine ng2-activiti-diagrams docs

* refine ng2-activiti-form

* refine ng2-activiti-processlist

* refine ng2-activiti-tasklist

* refine ng2-alfresco-core

* refine ng2-alfresco-datatable

* refine ng2-alfresco-datatable

* refine gn2-alfresco-login

* refine ng2-alfresco-search

* refine ng2-alfresco-social

* refine ng2-alfresco-tag

* refine ng2-alfresco-upload

* refine ng2-alfresco-userinfo

* refine ng2-alfresco-viewer

* refine ng2-alfresco-webscript

* various readme cleanups

* fix builds related to node-sass library (#1966)

* update dependencies and remove old lock files

* update sass loader

* updated lock files

* [ADF-578] Remember me functionality (#1962)

* Remember me functionality

* Happy pack to ng2-components' package.json

* Build fix

* Adding tabindices to viewer control elements (#1968)

* karma conf all single browser

* Fix current page number issue (#1970)

* [ADF-524] Datatable loading state (#1958)

* loading state datatable

* modify readme after review

* [ADF-78]  Update CORS help (#1973)

* Fix host configuration in demo-shell when no port is present (#1971)

* remove brachet

* [ADF-494] fixed readonly rendering for forms (#1972)

* [ADF-494] improved disabling for form

* [ADF-494] fixed readonly rendering for forms

* [ADF-814] application configuration service (#1969)

* app configuration service

* api improvements and readme update

* extend readme and add defaults

* unit tests for app config service

* [ADF-716] Task Header - Use a custom view inside the component (#1967)

* Use a generic custom view inside the task header

* Move the component into core component and change name

* Missing file

* Fix unit test

* fix unit test component name

* fix issue with shared Code settings

- remove obsolete rules for .js/.ts
- hide .happypack folder in the project tree

* [ADF-810] fix default value radio button (#1975)

* [ADF-510] Drag&Drop check permission to allow user to upload a file (#1948)

* [ADF-510] added permission check for drag&drop

* Improved code for drag and drop side

* Added test for drag and drop upload area changes

* Added test for document list permissions check

* [ADF-510] rebased branch after changes applied to upload

* [ADF-510] rebased branch and fixed tests

* [ADF-717] upgrade i18n and charting dependencies (#1976)

* remove app-specific polyfill dependencies

remove polyfill dependencies never used by component libraries

* upgrade i18n dependencies

* upgrade ng2-charts dependency

* fix unit tests

* update demo projects

* [ADF-524] Fix empty state after the loading introduction (#1980)

* fix empty state after the loading introduction

* Update document-list.component.spec.ts

remove typo

* [ADF-838] Table of content automatic creation (#1981)

* readonly value set

* Table of content automatic creation (#1982)

* add missing intl dependency for demo shell (#1984)

* [ADF-833] DataTable -  improve the single and double click event (#1979)

* Improve the single and double click event

* Fix unit test

* Task header basic documentation (#1985)

* Disable upload attachment when the task is completed (#1987)

* [ADF-847] upgrade to use application configuration service (#1986)

* migrate core lib to use server-side app config

* fix unit tests

* update Search tests

- update tests
- upgrade tests to use TestBed

* update UserInfo tests

* update Social tests

* update tests

* update unit tests

* cleanup old code

* update about page

* update demo shell readme

* dev and prod configurations

* updated package-lock file and removed duplicates in package.json

* [ADF-851] execute-outcome event for form service (#1989)

* execute-outcome event for form service

* readme updates

* fix loading state excluding other state during the loading (#1991)

* Fix compilation error (#1993)

* [ADF-883] Fix build errors (#1992)

* [ADF-793] Ability to create PDF renditions in case of non supported formats (#1994)

* Style changes and button

* Convert to PDF button

* Convert to PDF button part II.

* Convert within the Not Supported Format component

* Rendition loading skeleton

* Conversion is working.

* Convert button behaviour tests

* Rebasing fix.

* app settings page (#1997)

- custom app setttings service to use isolated storage (demo shell)
- restore settings UI
- redirect angular and rxjs to the same version as components use.

* [ADF-822] Added the npm-prepublish script (#1978)

* added the npm prepublish script

* changed permissions to prepublish script

* changed to npm run prepublish

* prepare the pr

* removed useless code to the script

* remove flags lib from demo shell (#1983)

* remove flags lib from demo shell

greatly reduce demo shell webpack resources by switching off flags (only 3 icons were displayed in the past)

* merge package.json

* add icons

* Fix typo error

* [ADF-794] Add people assignment component (#1977)

* Add people component

* exported people service

* added people-list component to show the involved user list

* changed people-search component layout

* changed people-list usage in people component

* changed people-list data table from custom template to data adapter

* changes people-search component related to people-list

* changes in activiti-people related to people-list and people-search component

* changed data adapter to direct data column setting to data-table

* removed ngChanges and added User and UserEvent models

* added User and UserEvent model in emitter and other emitter handler

* added user event model

* changed activiti-people component with latest UX changes

* addedand changed translate keys to the components

* added hasUser method to check the condition in html

* fixed tslint issue and test case issue in activiti-people component

* added test case for actviti-people-list component

* test case added for activiti-people-search component

* changed activiti-people test cases according to latest UX changes

* added description for activiti-people component

* changed test case to fix component.upgradeElement issue

* changes requested by Vito Albano #1

* splitted getDisplayUser into getDisplayUser and getInitialUsername

* introduce check type definition

* [ADF-897] - ActivitiPeopleList - use the adf prefix (#2001)

* Use the adf as prexif instead of activiti

* Fix typo

* Fix wrong import

* support binding [form] data directly (#1996)

- ability to bind [form] data directly inside `<activiti-form>` component
- ability to parse forms with FormService
- demo of the custom form in demo shell

* [ADF-778] cancel window for upload dialog shows only on complete (#2003)

* [ADF-778] Added new behaviour to upload dialog

* [ADF-778] cancel window for upload dialog shows only on complete

* [ADF-778] changed variable name to showCloseButton

* Create task/process attachment Compilation error (#2004)

* fix tslint errors

minor fix for "Unnecessary semicolon" TSLint rule

* [ADF-842] Fixed type for taskdetails (#2009)

* fix type definition (#2002)

* Use the activiti people with the new look and feel inside the demo shell (#2008)

* add rxjs and @angular in tsconfig.json

* [ADF-843] Form events bus (#1990)

* form events bus

* event test bus fix

* fix test after code review

* fix types errors

* change to public formservice

* make optional formservice

* [ADF-915] Add option to change the JS-API with different version in the update package

* Missing keys (#2011)

* [ADF-845] breadcrumb root option added and style review (#1999)

* breadcrumb root option added and style review

* new breadcrumbs

* split onchange in a method

* update readme with a note for old pefix tag

* fix tslint errors

* fix breadcrumb test

* [ADF-922] Regenerate package-lock.json files for every package and create script for doing that in the future (#2012)

* Updated package-lock.json files

* npm-relock-pkgs.sh

* Update README.md

* Fix ng2-alfresco-search sass problem

* SASS version update (#2013)

* sass update

* update sass loader

* vjsapi option prepublish

* prepublish script deprecation in favour of prepublishOnly node 8 (#2010)

* modify prePublish script with preoPublishOnly

* install rimraf globally

* fix clean scripts demo folders

* move appveyor to node 8

* Appveyor test (#1998)

* reduce memory

* remove max-old-space

* remove increase memory

* create new TaskDetailsModel in loadNextTask (#2017)

* Fix readme document list

* [ADF-907] - Form reacts to data added in input (#2016)

* [ADF-907] Enable activiti form to react on value data changes

* [ADF-907] - Form reacts to data added in input]

* [ADF - 907] added mock json for form

* [ADF-907] added new event of the form to the event list

* [ADF - 907] Added return column to README

* [ADF - 907] Added return column to README

* Script add pkg and clean update

* install globally pkg pre build

* Fix upload related content (#2019)

* regeneration TOC and add automatic list component generator (#2022)

* Fix upload process attachment (#2024)

* update typescript (#2026)

* update viewer readme

* fix type definition variables

* NgZone type passed parameter

* fix tslint error in tasklist

* Add screenshot (#2028)

* fix search miss typing

* bump version 1.6.0 (#2027)
This commit is contained in:
Eugenio Romano
2017-06-29 15:57:37 +01:00
committed by GitHub
parent c6f6227da7
commit b15ab3b988
822 changed files with 168022 additions and 13865 deletions

View File

@@ -8,7 +8,6 @@ dist
src/**/*.js
src/**/*.js.map
src/**/*.d.ts
demo/**/*.js
demo/**/*.js.map
demo/**/*.d.ts
index.js
@@ -18,3 +17,4 @@ index.js.map
/package/
/bundles/
index.d.ts
/.happypack

View File

@@ -4,6 +4,7 @@ npm-debug.log
coverage/
demo/
dist/
node_modules
typings/
fonts/
@@ -15,3 +16,4 @@ fonts/
/karma.conf.js
/gulpfile.ts
/.npmignore
/.happypack

View File

@@ -1,69 +1,78 @@
# Alfresco Angular 2 Components core
# Alfresco Core Library
<p>
<a title='Build Status Travis' href="https://travis-ci.org/Alfresco/alfresco-ng2-components">
<img src='https://travis-ci.org/Alfresco/alfresco-ng2-components.svg?branch=master' alt='travis
Status' />
</a>
<a title='Build Status AppVeyor' href="https://ci.appveyor.com/project/alfresco/alfresco-ng2-components">
<img src='https://ci.appveyor.com/api/projects/status/github/Alfresco/alfresco-ng2-components' alt='travis
Status' />
</a>
<a href='https://codecov.io/gh/Alfresco/alfresco-ng2-components'>
<img src='https://img.shields.io/codecov/c/github/Alfresco/alfresco-ng2-components/master.svg?maxAge=2592000' alt='Coverage Status' />
</a>
<a href='https://www.npmjs.com/package/ng2-alfresco-core'>
<img src='https://img.shields.io/npm/dt/ng2-alfresco-core.svg' alt='npm downloads' />
</a>
<a href='https://github.com/Alfresco/alfresco-ng2-components/blob/master/LICENSE'>
<img src='https://img.shields.io/hexpm/l/plug.svg' alt='license' />
</a>
<a href='https://www.alfresco.com/'>
<img src='https://img.shields.io/badge/style-component-green.svg?label=alfresco' alt='alfresco component' />
</a>
<a href='https://angular.io/'>
<img src='https://img.shields.io/badge/style-2-red.svg?label=angular' alt='angular 2' />
</a>
<a href='https://www.typescriptlang.org/docs/tutorial.html'>
<img src='https://img.shields.io/badge/style-lang-blue.svg?label=typescript' alt='typescript' />
</a>
<a href='https://www.alfresco.com/'>
<img src='https://img.shields.io/badge/style-%3E5.0.0-blue.svg?label=node%20version' alt='node version' />
</a>
</p>
<!-- markdown-toc start - Don't edit this section. npm run toc to generate it-->
<!-- toc -->
- [Prerequisites](#prerequisites)
- [Install](#install)
- [Toolbar Component](#toolbar-component)
* [Properties](#properties)
- [Upload Directive](#upload-directive)
* [Basic usage](#basic-usage)
* [Modes](#modes)
+ [Click mode](#click-mode)
+ [Drop mode](#drop-mode)
* [Events](#events)
* [Styling](#styling)
- [Alfresco Api Service](#alfresco-api-service)
- [AppConfigService](#appconfigservice)
* [Different configurations based on environment settings](#different-configurations-based-on-environment-settings)
- [Notification Service](#notification-service)
- [Context Menu directive](#context-menu-directive)
- [Accordion Component](#accordion-component)
* [Properties](#properties-1)
- [Authentication Service](#authentication-service)
* [Events](#events-1)
- [ADF Card View](#adf-card-view)
* [Properties](#properties-2)
* [CardViewModel](#cardviewmodel)
- [AlfrescoTranslationService](#alfrescotranslationservice)
- [Renditions Service](#renditions-service)
- [Build from sources](#build-from-sources)
- [Build from sources](#build-from-sources-1)
- [NPM scripts](#npm-scripts)
- [Demo](#demo)
- [License](#license)
<!-- tocstop -->
<!-- markdown-toc end -->
## Prerequisites
Before you start using this development framework, make sure you have installed all required software and done all the
necessary configuration, see this [page](https://github.com/Alfresco/alfresco-ng2-components/blob/master/PREREQUISITES.md).
> If you plan using this component with projects generated by Angular CLI, please refer to the following article: [Using ADF with Angular CLI](https://github.com/Alfresco/alfresco-ng2-components/wiki/Angular-CLI)
## Install
```sh
npm install --save ng2-alfresco-core
npm install ng2-alfresco-core
```
## Library content
## Toolbar Component
- Components
- Context Menu directive
- Material Design directives
- [mdl]
- [alfresco-mdl-button]
- [alfresco-mdl-menu]
- [alfresco-mdl-tabs]
- Directives
- UploadDirective
- Services
- **LogService**, log service implementation
- **NotificationService**, Notification service implementation
- **AlfrescoApiService**, provides access to Alfresco JS API instance
- **AlfrescoAuthenticationService**, main authentication APIs
- **AlfrescoTranslationService**, various i18n-related APIs
- **ContextMenuService**, global context menu APIs
```html
<adf-toolbar title="Toolbar">
<button md-icon-button>
<md-icon>create_new_folder</md-icon>
</button>
<button md-icon-button>
<md-icon>delete</md-icon>
</button>
</adf-toolbar>
```
### Properties
## UploadDirective
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| title | string | | Toolbar title |
| color | string | | Toolbar color, can be changed to empty value (default), `primary`, `accent` or `warn`. |
## Upload Directive
Allows your components or common HTML elements reacting on File drag and drop in order to upload content.
Used by attaching to an element or component.
@@ -227,7 +236,6 @@ for example you may want drawing a dashed border around the table row on drag:
Provides access to initialized **AlfrescoJSApi** instance.
```ts
export class MyComponent implements OnInit {
constructor(private apiService: AlfrescoApiService) {
@@ -254,6 +262,98 @@ let api: any = this.apiService.getInstance();
api.nodes.addNode('-root-', body, {});
```
## AppConfigService
The `AppConfigService` service provides support for loading and accessing global application configuration settings that you store on the server side in the form of a JSON file.
> You may need this service when deploying your ADF-based application to production servers.
> There can be more than one server running web apps with different settings, like different addresses for Alfreco Content/Process services.
> Or there is a need to change global settings for all the clients.
The service is already pre-configured to look for the "app.config.json" file in the application root address.
That allows deploying ADF-based web applications to multiple servers together with different settings files, for example having development, staging or production environments.
Example of the default settings file content:
**app.config.json**
```json
{
"ecmHost": "http://localhost:3000/ecm",
"bpmHost": "http://localhost:3000/bpm",
"application": {
"name": "Alfresco"
}
}
```
Please note that settings above are default ones coming with the server.
You can override the values in your custom `app.config.json` file if needed.
You can also change the path or name of the configuration file when importing the CoreModule in your main application.
```ts
...
@NgModule({
imports: [
...
CoreModule.forRoot({
appConfigFile: 'app.production.config.json'
})
],
...
}
export class AppModule { }
```
Below is a simple example of using the AppConfigService in practice.
**app.component.ts**
```ts
import { AppConfigService } from 'ng2-alfresco-core';
@Component({...})
export class AppComponent {
constructor(appConfig: AppConfigService) {
// get nested properties by the path
console.log(appConfig.get('application.name'));
// use generics for type safety
let version: number = appConfig.get<number>('version');
console.log(version);
}
}
```
You custom components can also benefit from the `AppConfigService`,
you can put an unlimited number of settings and optionally a nested JSON hierarchy.
### Different configurations based on environment settings
The CoreModule allows you to provide custom application configuration path.
That means you can evaluate the final file name based on conditions, for example environment settings:
```ts
let appConfigFile = 'app.config-dev.json';
if (process.env.ENV === 'production') {
appConfigFile = 'app.config-prod.json';
}
@NgModule({
imports: [
...
CoreModule.forRoot({
appConfigFile: appConfigFile
}),
...
]
})
```
## Notification Service
The Notification Service is implemented on top of the Angular 2 Material Design snackbar.
@@ -302,7 +402,7 @@ _See **Demo Shell** or **DocumentList** implementation for more details and use
```ts
@Component({
selector: 'my-component
selector: 'my-component'
})
export class MyComponent implements OnInit {
@@ -327,6 +427,42 @@ export class MyComponent implements OnInit {
}
```
## Accordion Component
The component provide a way to easy create an accordion menu. You can customize the header and the icon.
```html
<adf-accordion>
<adf-accordion-group [heading]="titleHeading" [isSelected]="true" [headingIcon]="'assignment'">
<my-list></my-list>
</adf-accordion-group>
</adf-accordion>
```
```ts
@Component({
selector: 'my-component'
})
export class MyComponent implements OnInit {
titleHeading: string;
constructor() {
this.titleHeading = 'My Group';
}
}
```
### Properties
| Name | Type | Description |
| --- | --- | --- |
| heading | string | The header title. |
| isSelected | boolean | Define if the accordion group is selected or not. |
| headingIcon | string | The material design icon. |
| hasAccordionIcon | boolean | Define if the accordion (expand) icon needs to be shown or not, the default value is true |
## Authentication Service
The authentication service is used inside the [login component](../ng2-alfresco-login) and is possible to find there an example of how to use it.
@@ -338,76 +474,63 @@ The authentication service is used inside the [login component](../ng2-alfresco-
| onLogin | Raised when user logs in |
| onLogout | Raised when user logs out |
**app.component.ts**
```ts
import { NgModule, Component } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { CoreModule, AlfrescoSettingsService, AlfrescoAuthenticationService } from 'ng2-alfresco-core';
import { AlfrescoAuthenticationService } from 'ng2-alfresco-core';
@Component({
selector: 'alfresco-app-demo',
template: `
<div *ngIf="!authenticated" >
Authentication failed to ip {{ ecmHost }} with user: admin, admin
</div>
<div *ngIf="authenticated">
<H5>ECM</H5>
Authentication successfull to ip {{ ecmHost }} with user: admin, admin<br>
your token is {{ tokenEcm }}<br>
<H5>BPM</H5>
Authentication successfull to ip {{ bpmHost }} with user: admin, admin<br>
your token is {{ tokenBpm }}<br>
</div>
`
})
class MyDemoApp {
authenticated: boolean = false;
ecmHost: string = 'http://localhost:8080';
bpmHost: string = 'http://localhost:9999';
tokenBpm: string;
tokenEcm: string;
constructor(public alfrescoAuthenticationService: AlfrescoAuthenticationService,
private alfrescoSettingsService: AlfrescoSettingsService) {
alfrescoSettingsService.ecmHost = this.ecmHost;
alfrescoSettingsService.bpmHost = this.bpmHost;
alfrescoSettingsService.setProviders('ALL');
}
ngOnInit() {
this.login();
}
login() {
@Component({...})
export class AppComponent {
constructor(authService: AlfrescoAuthenticationService) {
this.alfrescoAuthenticationService.login('admin', 'admin').subscribe(
token => {
this.tokenBpm = this.alfrescoAuthenticationService.getTicketBpm();
this.tokenEcm = this.alfrescoAuthenticationService.getTicketEcm();
this.authenticated = true;
console.log(token);
},
error => {
console.log(error);
this.authenticated = false;
});
}
);
}
}
@NgModule({
imports: [
BrowserModule,
CoreModule.forRoot()
],
declarations: [MyDemoApp],
bootstrap: [MyDemoApp]
})
export class AppModule {
}
platformBrowserDynamic().bootstrapModule(AppModule);
```
## ADF Card View
The component shows the [CardViewModel](#cardviewmodel)} object.
```html
<adf-card-view
[properties]="[{label: 'My Label', value: 'My value'}]">
</adf-card-view>
```
### Properties
| Name | Type | Description |
| --- | --- | --- |
| properties | {array[CardViewModel](#cardviewmodel)} | (**required**) The custom view to render |
### CardViewModel
```json
{
"label": "string",
"value": "any",
"format": "string",
"default": "string"
}
```
| Name | Type | Description |
| --- | --- | --- |
| label | string | The label to render |
| value | string | The value to render |
| format | string | The format to use in case the value is a date |
| default | string | The default value to render in case the value is empty |
![adf-custom-view](docs/assets/adf-custom-view.png)
## AlfrescoTranslationService
In order to enable localisation support you will need creating a `/resources/i18n/en.json` file
@@ -415,7 +538,7 @@ and registering path to it's parent `i18n` folder:
```ts
class MainApplication {
constructor(private translateService: AlfrescoTranslationService) {
constructor(translateService: AlfrescoTranslationService) {
translateService.addTranslationFolder('app', 'resources');
}
}
@@ -470,40 +593,15 @@ npm run build
## Build from sources
Alternatively you can build component from sources with the following commands:
You can build component from sources with the following commands:
```sh
npm install
npm run build
```
### Build the files and keep watching for changes
```sh
$ npm run build:w
```
## Running unit tests
```sh
npm test
```
### Running unit tests in browser
```sh
npm test-browser
```
This task rebuilds all the code, runs tslint, license checks and other quality check tools
before performing unit testing.
### Code coverage
```sh
npm run coverage
```
> The `build` task rebuilds all the code, runs tslint, license checks
> and other quality check tools before performing unit testing.
## NPM scripts
@@ -514,6 +612,16 @@ npm run coverage
| npm run test-browser | Run unit tests in the browser
| npm run coverage | Run unit tests and display code coverage report |
## Demo
Please check the demo folder for a demo project
```sh
cd demo
npm install
npm start
```
## License
[Apache Version 2.0](https://github.com/Alfresco/alfresco-ng2-components/blob/master/LICENSE)
[Apache Version 2.0](https://github.com/Alfresco/alfresco-ng2-components/blob/master/LICENSE)

View File

@@ -0,0 +1,35 @@
const webpack = require('webpack');
const webpackMerge = require('webpack-merge');
const commonConfig = require('./webpack.common.js');
module.exports = webpackMerge(commonConfig, {
devtool: 'cheap-module-source-map',
externals: [
/^\@angular\//,
/^rxjs\//,
'moment',
'raphael',
'ng2-charts',
'alfresco-js-api',
'ng2-alfresco-core',
'ng2-alfresco-datatable',
'ng2-activiti-analytics',
'ng2-activiti-diagrams',
'ng2-activiti-form',
"ng2-activiti-tasklist",
'ng2-alfresco-documentlist'
],
output: {
filename: './bundles/[name].js',
library: '[name]',
libraryTarget: 'umd',
chunkFilename: '[id].chunk.js'
},
entry: {
"ng2-alfresco-core": "./index.ts"
}
});

View File

@@ -2,6 +2,11 @@ const webpack = require('webpack');
const helpers = require('./helpers');
const fs = require('fs');
const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin');
var HappyPack = require('happypack');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const ENV = process.env.NODE_ENV = process.env.ENV = 'production';
module.exports = {
@@ -12,22 +17,11 @@ module.exports = {
}
},
// require those dependencies but don't bundle them
externals: [
/^\@angular\//,
/^rxjs\//,
'moment',
'raphael',
'ng2-charts',
'alfresco-js-api',
'ng2-alfresco-core',
'ng2-alfresco-datatable',
'ng2-activiti-analytics',
'ng2-activiti-diagrams',
'ng2-activiti-form',
"ng2-activiti-tasklist",
'ng2-alfresco-documentlist'
],
resolve: {
extensions: ['.ts', '.js'],
symlinks: false,
modules: [helpers.root('../../ng2-components'), helpers.root('node_modules')]
},
module: {
rules: [
@@ -37,25 +31,19 @@ module.exports = {
loader: 'source-map-loader',
exclude: [/node_modules/, /bundles/, /dist/, /demo/]
},
{
enforce: 'pre',
test: /\.ts$/,
use: 'source-map-loader',
exclude: [/node_modules/, /bundles/, /dist/, /demo/]
},
{
enforce: 'pre',
test: /\.ts$/,
loader: 'tslint-loader',
options: {
emitErrors: true,
configFile: path.resolve(__dirname, './assets/tslint.json')
failOnHint: true
},
exclude: [/node_modules/, /bundles/, /dist/, /demo/]
},
{
test: /\.ts$/,
use: ['ts-loader', 'angular2-template-loader'],
loader: ['happypack/loader?id=ts', 'angular2-template-loader'],
exclude: [/node_modules/, /bundles/, /dist/, /demo/]
},
{
@@ -67,7 +55,13 @@ module.exports = {
test: /\.css$/,
loader: ['to-string-loader', 'css-loader'],
exclude: [/node_modules/, /bundles/, /dist/, /demo/]
},{
},
{
test: /\.component.scss$/,
use: ['to-string-loader', 'raw-loader', 'sass-loader'],
exclude: [/node_modules/, /bundles/, /dist/, /demo/]
},
{
enforce: 'pre',
test: /\.ts$/,
loader: 'license-check',
@@ -95,15 +89,29 @@ module.exports = {
]
},
resolve: {
extensions: ['.ts', '.js'],
symlinks: false,
modules: [
'../ng2-components', 'node_modules'
]
},
plugins: [
new ForkTsCheckerWebpackPlugin(),
new HappyPack({
id: 'ts',
threads: 8,
loaders: [
{
path: 'ts-loader',
query: {
happyPackMode: true,
"compilerOptions": {
"paths": {}
}
}
}
]
}),
new CopyWebpackPlugin([{
from: `src/i18n/`,
to: `bundles/assets/${path.basename(helpers.root(''))}/i18n/`
}]),
new webpack.NoEmitOnErrorsPlugin(),
new webpack.BannerPlugin(fs.readFileSync(path.resolve(__dirname, './assets/license_header_add.txt'), 'utf8')),
@@ -112,11 +120,19 @@ module.exports = {
/angular(\\|\/)core(\\|\/)@angular/,
helpers.root('./src'),
{}
)
),
new webpack.DefinePlugin({
'process.env': {
'ENV': JSON.stringify(ENV)
}
}),
new webpack.LoaderOptionsPlugin({
htmlLoader: {
minimize: false // workaround for ng2
}
})
],
devtool: 'cheap-module-source-map',
node: {
fs: 'empty',
module: false

View File

@@ -0,0 +1,22 @@
const webpack = require('webpack');
const webpackMerge = require('webpack-merge');
const testConfig = require('./webpack.test.js');
const helpers = require('./helpers');
module.exports = webpackMerge(testConfig, {
module: {
rules: [
{
enforce: 'post',
test: /^(?!(.*spec|index|.*mock|.*model|.*event)).*\.ts?$/,
include: [helpers.root('src')],
loader: 'istanbul-instrumenter-loader',
exclude: [
/node_modules/,
/test/
]
}
]
}
});

View File

@@ -1,85 +1,8 @@
const webpack = require('webpack');
const helpers = require('./helpers');
const webpackMerge = require('webpack-merge');
const commonConfig = require('./webpack.common.js');
const ENV = process.env.NODE_ENV = process.env.ENV = 'production';
module.exports = webpackMerge(commonConfig, {
module.exports = {
devtool: 'inline-source-map',
resolve: {
extensions: ['.ts', '.js'],
symlinks: false,
modules: [helpers.root('../ng2-components'), helpers.root('node_modules')]
},
module: {
rules: [
{
enforce: 'pre',
test: /\.js$/,
loader: 'source-map-loader',
exclude: [/node_modules/, /bundles/, /dist/, /demo/]
},
{
test: /\.ts$/,
loaders: ['ts-loader?' + JSON.stringify({ transpileOnly: true}), 'angular2-template-loader'],
exclude: [/node_modules/, /bundles/, /dist/, /demo/]
},
{
test: /\.html$/,
loader: 'html-loader',
exclude: [/node_modules/, /bundles/, /dist/, /demo/]
},
{
test: /\.css$/,
loader: ['to-string-loader', 'css-loader'],
exclude: [/node_modules/, /bundles/, /dist/, /demo/]
},
{
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico|pdf)$/,
loader: 'file-loader',
query: {
name: '[path][name].[ext]',
outputPath: (url)=> {
return url.replace('src', 'dist');
}
}
},
{
enforce: 'post',
test: /\.ts$/,
loader: 'istanbul-instrumenter-loader',
exclude: [
/node_modules/,
/test/
]
}
]
},
plugins: [
new webpack.NoEmitOnErrorsPlugin(),
new webpack.ContextReplacementPlugin(
/angular(\\|\/)core(\\|\/)@angular/,
helpers.root('./src'),
{}
),
new webpack.DefinePlugin({
'process.env': {
'ENV': JSON.stringify(ENV)
}
}),
new webpack.LoaderOptionsPlugin({
htmlLoader: {
minimize: false // workaround for ng2
}
})
],
node: {
fs: 'empty',
module: false
}
};
devtool: 'inline-source-map'
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View File

@@ -20,14 +20,17 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { HttpModule, Http } from '@angular/http';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { TranslateModule, TranslateLoader } from 'ng2-translate/ng2-translate';
import { MaterialModule } from '@angular/material';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { MaterialModule } from './src/material.module';
import { AppConfigModule } from './src/services/app-config.service';
import { AdfToolbarComponent } from './src/components/toolbar/toolbar.component';
import {
AlfrescoAuthenticationService,
AlfrescoContentService,
AlfrescoSettingsService,
StorageService,
CookieService,
AlfrescoApiService,
AlfrescoTranslateLoader,
AlfrescoTranslationService,
@@ -38,25 +41,31 @@ import {
LogService,
LogServiceMock,
NotificationService,
ContentService
ContentService,
AppConfigService, InitAppConfigServiceProvider
} from './src/services/index';
import { FileSizePipe } from './src/pipes/file-size.pipe';
import { UploadDirective } from './src/directives/upload.directive';
import { DataColumnComponent } from './src/components/data-column/data-column.component';
import { DataColumnListComponent } from './src/components/data-column/data-column-list.component';
import { MATERIAL_DESIGN_DIRECTIVES } from './src/components/material/index';
import { CONTEXT_MENU_PROVIDERS, CONTEXT_MENU_DIRECTIVES } from './src/components/context-menu/index';
import { COLLAPSABLE_DIRECTIVES } from './src/components/collapsable/index';
import { VIEW_DIRECTIVES } from './src/components/view/index';
export * from './src/services/index';
export * from './src/components/index';
export * from './src/components/data-column/data-column.component';
export * from './src/components/data-column/data-column-list.component';
export * from './src/components/collapsable/index';
export * from './src/components/view/index';
export * from './src/directives/upload.directive';
export * from './src/utils/index';
export * from './src/events/base.event';
export * from './src/events/base-ui.event';
export * from './src/events/folder-created.event';
export * from './src/models/index';
export const ALFRESCO_CORE_PROVIDERS: any[] = [
NotificationService,
@@ -66,6 +75,7 @@ export const ALFRESCO_CORE_PROVIDERS: any[] = [
AlfrescoContentService,
AlfrescoSettingsService,
StorageService,
CookieService,
AlfrescoApiService,
AlfrescoTranslateLoader,
AlfrescoTranslationService,
@@ -88,20 +98,26 @@ export function createTranslateLoader(http: Http, logService: LogService) {
ReactiveFormsModule,
HttpModule,
BrowserAnimationsModule,
MaterialModule.forRoot(),
TranslateModule.forRoot({
provide: TranslateLoader,
useFactory: (createTranslateLoader),
deps: [Http, LogService]
})
loader: {
provide: TranslateLoader,
useFactory: (createTranslateLoader),
deps: [Http, LogService]
}
}),
MaterialModule,
AppConfigModule
],
declarations: [
...MATERIAL_DESIGN_DIRECTIVES,
...CONTEXT_MENU_DIRECTIVES,
...COLLAPSABLE_DIRECTIVES,
...VIEW_DIRECTIVES,
UploadDirective,
DataColumnComponent,
DataColumnListComponent
DataColumnListComponent,
FileSizePipe,
AdfToolbarComponent
],
providers: [
...ALFRESCO_CORE_PROVIDERS
@@ -110,24 +126,32 @@ export function createTranslateLoader(http: Http, logService: LogService) {
BrowserAnimationsModule,
CommonModule,
FormsModule,
MaterialModule,
ReactiveFormsModule,
HttpModule,
TranslateModule,
MaterialModule,
...MATERIAL_DESIGN_DIRECTIVES,
...CONTEXT_MENU_DIRECTIVES,
...COLLAPSABLE_DIRECTIVES,
...VIEW_DIRECTIVES,
UploadDirective,
DataColumnComponent,
DataColumnListComponent
DataColumnListComponent,
FileSizePipe,
AdfToolbarComponent
]
})
export class CoreModule {
static forRoot(): ModuleWithProviders {
static forRoot(opts: any = {}): ModuleWithProviders {
const appConfigFile = opts.appConfigFile || 'app.config.json';
return {
ngModule: CoreModule,
providers: [
...ALFRESCO_CORE_PROVIDERS
...ALFRESCO_CORE_PROVIDERS,
AppConfigService,
InitAppConfigServiceProvider(appConfigFile)
]
};
}

View File

@@ -8,17 +8,13 @@ module.exports = function (config) {
files: [
'./node_modules/hammerjs/hammer.js',
{pattern: './node_modules/@angular/material/prebuilt-themes/indigo-pink.css', included: true, watched: false},
//diagrams
'./node_modules/chart.js/dist/Chart.js',
'./node_modules/alfresco-js-api/dist/alfresco-js-api.js',
'./node_modules/raphael/raphael.js',
'./node_modules/moment/min/moment.min.js',
'./node_modules/md-date-time-picker/dist/js/mdDateTimePicker.js',
{pattern: './node_modules/ng2-translate/**/*.js', included: false, watched: false},
{pattern: './node_modules/ng2-charts/**/*.js', included: false, served: true, watched: false},
{pattern: './node_modules/md-date-time-picker/**/*.js', included: false, served: true, watched: false},
{pattern: './node_modules/moment/**/*.js', included: false, served: true, watched: false},
{pattern: 'karma-test-shim.js', watched: false},
@@ -27,7 +23,7 @@ module.exports = function (config) {
{pattern: './src/**/*.ts', included: false, served: true, watched: false}
],
webpack: webpackConfig,
webpack: (config.mode === 'coverage') ? require('./webpack.coverage') : require('./webpack.test'),
webpackMiddleware: {
stats: 'errors-only'

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +1,17 @@
{
"name": "ng2-alfresco-core",
"description": "Alfresco Angular 2 Components core",
"version": "1.5.0",
"version": "1.6.0",
"author": "Alfresco Software, Ltd.",
"scripts": {
"clean": "rimraf dist node_modules typings bundles coverage .npmrc",
"clean-lock": "rimraf package-lock.json",
"rimraf": "rimraf",
"build": "webpack --config webpack.build.js --progress --profile --bail",
"test": "karma start karma.conf.js --reporters mocha,coverage --single-run --component",
"test": "karma start karma.conf.js --reporters mocha,coverage --single-run --mode coverage",
"test-browser": "karma start karma.conf.js --reporters kjhtml --component",
"coverage": "npm run test && wsrv -o -p 9875 ./coverage/report",
"prepublish" : "npm run build"
"prepublishOnly": "npm run build"
},
"main": "bundles/ng2-alfresco-core.js",
"repository": {
@@ -51,17 +52,13 @@
"@angular/platform-browser-dynamic": "~4.0.0",
"@angular/router": "~4.0.0",
"@angular/compiler-cli": "~4.0.0",
"@angular/material": "2.0.0-beta.1",
"alfresco-js-api": "~1.5.0",
"@angular/material": "2.0.0-beta.6",
"alfresco-js-api": "~1.6.0",
"core-js": "2.4.1",
"dialog-polyfill": "0.4.7",
"element.scrollintoviewifneeded-polyfill": "1.0.1",
"hammerjs": "2.0.8",
"intl": "1.2.4",
"material-design-icons": "2.2.3",
"material-design-lite": "1.2.1",
"ng2-translate": "5.0.0",
"@ngx-translate/core": "^7.0.0",
"reflect-metadata": "0.1.10",
"rxjs": "5.1.0",
"systemjs": "0.19.27",
@@ -79,6 +76,8 @@
"cssnano": "^3.8.1",
"extract-text-webpack-plugin": "^2.0.0-rc.3",
"file-loader": "0.11.1",
"fork-ts-checker-webpack-plugin": "^0.2.3",
"happypack": "3.0.0",
"html-loader": "^0.4.4",
"html-webpack-plugin": "^2.28.0",
"istanbul-instrumenter-loader": "0.2.0",
@@ -97,12 +96,14 @@
"karma-webpack": "^2.0.2",
"loader-utils": "^1.1.0",
"merge-stream": "^1.0.1",
"node-sass": "^4.5.3",
"null-loader": "^0.1.1",
"package-json-merge": "0.0.1",
"raw-loader": "^0.5.1",
"remap-istanbul": "^0.6.3",
"rimraf": "^2.5.4",
"rimraf": "^2.6.1",
"run-sequence": "^1.2.2",
"sass-loader": "^6.0.5",
"script-loader": "0.7.0",
"source-map-loader": "^0.1.6",
"style-loader": "^0.13.1",
@@ -113,7 +114,7 @@
"ts-node": "^1.7.0",
"tslint": "^4.4.2",
"tslint-loader": "^3.3.0",
"typescript": "^2.1.6",
"typescript": "^2.3.4",
"webpack": "^2.2.1",
"webpack-dev-server": "^2.3.0",
"webpack-merge": "2.6.1",

View File

@@ -0,0 +1,27 @@
/*!
* @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 class CookieServiceMock {
getItem(key: string): string | null {
return this[key] && this[key].data || null;
}
setItem(key: string, data: string, expiration: Date | null, path: string | null): void {
this[key] = {data, expiration, path};
}
}

View File

@@ -1,12 +1,10 @@
<div class="adf-panel adf-panel-default" [ngClass]="{'adf-panel-open': isOpen}">
<div class="adf-panel-heading" [ngClass]="{'adf-panel-heading-selected': isSelected}">
<div (click)="onHeadingClick()">
<div id="heading-icon" *ngIf="hasHeadingIcon()" class="adf-panel-heading-icon">
<i class="material-icons">{{headingIcon}}</i>
</div>
<div id="heading-text" class="adf-panel-heading-text">{{heading}}</div>
<div class="adf-panel-heading" [ngClass]="{'adf-panel-heading-selected': isSelected}" (click)="toggleOpen($event)">
<div id="heading-icon" *ngIf="hasHeadingIcon()" class="adf-panel-heading-icon">
<i class="material-icons">{{headingIcon}}</i>
</div>
<div id="accordion-button" *ngIf="!isGroupContentEmpty()" class="adf-panel-heading-toggle" (click)="toggleOpen($event)">
<div id="heading-text" class="adf-panel-heading-text">{{heading}}</div>
<div id="accordion-button" *ngIf="hasAccordionIcon" class="adf-panel-heading-toggle">
<i class="material-icons">{{getAccordionIcon()}}</i>
</div>
</div>

View File

@@ -75,17 +75,6 @@ describe('AccordionGroupComponent', () => {
});
});
it('should hide expand icon by default', () => {
component.heading = 'Fake Header';
component.headingIcon = 'fake-icon';
component.contentWrapper.nativeElement.innerHTML = '';
fixture.whenStable().then(() => {
fixture.detectChanges();
let headerIcon = element.querySelector('#accordion-button');
expect(headerIcon).toBeNull();
});
});
it('should show expand icon by default', () => {
component.heading = 'Fake Header';
component.headingIcon = 'fake-icon';
@@ -97,6 +86,18 @@ describe('AccordionGroupComponent', () => {
});
});
it('should hide expand icon', () => {
component.heading = 'Fake Header';
component.headingIcon = 'fake-icon';
component.hasAccordionIcon = false;
component.contentWrapper.nativeElement.innerHTML = '<a>Test</a>';
fixture.whenStable().then(() => {
fixture.detectChanges();
let headerIcon = element.querySelector('#accordion-button');
expect(headerIcon).toBeNull();
});
});
it('should emit an event when a heading clicked', (done) => {
component.heading = 'Fake Header';
fixture.detectChanges();
@@ -106,8 +107,8 @@ describe('AccordionGroupComponent', () => {
expect(headName).toEqual(heading);
done();
});
component.onHeadingClick();
let header = element.querySelector('.adf-panel-heading');
header.click();
});
});

View File

@@ -37,6 +37,9 @@ export class AccordionGroupComponent implements OnDestroy {
@Input()
headingIcon: string;
@Input()
hasAccordionIcon: boolean = true;
@Output()
headingClick: EventEmitter<any> = new EventEmitter<any>();
@@ -76,17 +79,11 @@ export class AccordionGroupComponent implements OnDestroy {
toggleOpen(event: MouseEvent): void {
event.preventDefault();
this.isOpen = !this.isOpen;
this.headingClick.emit(this.heading);
}
getAccordionIcon(): string {
return this.isOpen ? 'expand_less' : 'expand_more';
}
onHeadingClick() {
this.headingClick.emit(this.heading);
}
isGroupContentEmpty() {
return this.contentWrapper.nativeElement.innerHTML.trim().length === 0;
}
}

View File

@@ -18,3 +18,4 @@
export * from './context-menu/index';
export * from './material/index';
export * from './collapsable/index';
export * from './view/index';

View File

@@ -18,19 +18,16 @@
import { MDL } from './mdl-upgrade-element.directive';
import { AlfrescoMdlButtonDirective } from './mdl-button.directive';
import { AlfrescoMdlMenuDirective } from './mdl-menu.directive';
import { AlfrescoMdlTabsDirective } from './mdl-tabs.directive';
import { AlfrescoMdlTextFieldDirective } from './mdl-textfield.directive';
export * from './mdl-upgrade-element.directive';
export * from './mdl-button.directive';
export * from './mdl-menu.directive';
export * from './mdl-tabs.directive';
export * from './mdl-textfield.directive';
export const MATERIAL_DESIGN_DIRECTIVES: [any] = [
MDL,
AlfrescoMdlButtonDirective,
AlfrescoMdlMenuDirective,
AlfrescoMdlTabsDirective,
AlfrescoMdlTextFieldDirective
];

View File

@@ -1,79 +0,0 @@
/*!
* @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 {
Directive,
ElementRef,
AfterViewInit,
OnDestroy
} from '@angular/core';
declare var componentHandler;
@Directive({
selector: '[alfresco-mdl-tabs]'
})
export class AlfrescoMdlTabsDirective implements AfterViewInit, OnDestroy {
private observer: MutationObserver;
constructor(private element: ElementRef) {}
ngAfterViewInit() {
if (componentHandler) {
let el = this.element.nativeElement;
el.classList.add('mdl-tabs');
el.classList.add('mdl-js-tabs');
el.classList.add('mdl-js-ripple-effect');
componentHandler.upgradeElement(el, 'MaterialTabs');
// watch widget DOM changes and re-upgrade MDL content
let tabBar = el.querySelector('.mdl-tabs__tab-bar');
if (tabBar) {
this.observer = new MutationObserver((mutations: any[]) => {
let upgrade = false;
mutations.forEach((mutation: MutationRecord) => {
if (mutation.addedNodes && mutation.addedNodes.length > 0) {
upgrade = true;
}
if (mutation.removedNodes && mutation.removedNodes.length > 0) {
upgrade = true;
}
});
if (upgrade) {
componentHandler.downgradeElements([el]);
componentHandler.upgradeElement(el);
}
});
this.observer.observe(tabBar, {
childList: true,
subtree: false
});
}
}
}
ngOnDestroy() {
if (this.observer) {
this.observer.disconnect();
}
}
}

View File

@@ -0,0 +1,3 @@
.adf-toolbar--spacer {
flex: 1 1 auto;
}

View File

@@ -0,0 +1,5 @@
<md-toolbar [color]="color">
<span>{{ title }}</span>
<span class="adf-toolbar--spacer"></span>
<ng-content></ng-content>
</md-toolbar>

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 { Component, Input, ChangeDetectionStrategy, ViewEncapsulation } from '@angular/core';
@Component({
selector: 'adf-toolbar',
templateUrl: './toolbar.component.html',
styleUrls: ['./toolbar.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None
})
export class AdfToolbarComponent {
@Input()
title: string = '';
@Input()
color: string;
}

View File

@@ -0,0 +1,18 @@
:host {
width: 100%;
}
.adf-header__label {
font-weight: bold;
float: left;
width: 50%;
word-wrap: break-word;
}
.adf-header__value {
color: rgb(68, 138, 255);
}
.material-icons {
cursor: pointer;
}

View File

@@ -0,0 +1,6 @@
<div class="mdl-cell mdl-cell--12-col" *ngFor="let property of properties; let idx = index">
<div [attr.data-automation-id]="'header-' + property.key">
<div class="adf-header__label">{{ property.label }}</div>
<div class="adf-header__value">{{ getPropertyValue(property) }}</div>
</div>
</div>

View File

@@ -0,0 +1,99 @@
/*!
* @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 { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { CardView } from './adf-card-view.component';
import { CardViewModel } from '../../models/card-view.model';
import { By } from '@angular/platform-browser';
describe('AdfCardView', () => {
let fixture: ComponentFixture<CardView>;
let component: CardView;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
CardView
],
providers: [
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CardView);
component = fixture.componentInstance;
});
it('should render the label and value', async(() => {
component.properties = [new CardViewModel({label: 'My label', value: 'My value'})];
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
let labelValue = fixture.debugElement.query(By.css('.adf-header__label'));
expect(labelValue).not.toBeNull();
expect(labelValue.nativeElement.innerText).toBe('My label');
let value = fixture.debugElement.query(By.css('.adf-header__value'));
expect(value).not.toBeNull();
expect(value.nativeElement.innerText).toBe('My value');
});
}));
it('should render the date in the correct format', async(() => {
component.properties = [new CardViewModel({
label: 'My date label', value: '2017-06-14',
format: 'MMM DD YYYY'
})];
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
let labelValue = fixture.debugElement.query(By.css('.adf-header__label'));
expect(labelValue).not.toBeNull();
expect(labelValue.nativeElement.innerText).toBe('My date label');
let value = fixture.debugElement.query(By.css('.adf-header__value'));
expect(value).not.toBeNull();
expect(value.nativeElement.innerText).toBe('Jun 14 2017');
});
}));
it('should render the default value if the value is empty', async(() => {
component.properties = [new CardViewModel({
label: 'My default label',
default: 'default value'
})];
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
let labelValue = fixture.debugElement.query(By.css('.adf-header__label'));
expect(labelValue).not.toBeNull();
expect(labelValue.nativeElement.innerText).toBe('My default label');
let value = fixture.debugElement.query(By.css('.adf-header__value'));
expect(value).not.toBeNull();
expect(value.nativeElement.innerText).toBe('default value');
});
}));
});

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.
*/
import {
Component,
Input,
OnInit
} from '@angular/core';
import { CardViewModel } from '../../models/card-view.model';
import * as moment from 'moment';
@Component({
selector: 'adf-card-view',
templateUrl: './adf-card-view.component.html',
styleUrls: ['./adf-card-view.component.css']
})
export class CardView implements OnInit {
@Input()
properties: CardViewModel [];
constructor() {
}
ngOnInit() {
}
getPropertyValue(property: CardViewModel): string {
if (!property.value) {
return property.default;
} else if (property.format) {
return moment(property.value).format(property.format);
}
return property.value;
}
}

View File

@@ -0,0 +1,22 @@
/*!
* @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 { CardView } from './adf-card-view.component';
export const VIEW_DIRECTIVES: [any] = [
CardView
];

View File

@@ -17,6 +17,7 @@
import { ElementRef } from '@angular/core';
import { UploadDirective } from './upload.directive';
import { FileInfo } from './../utils/file-utils';
describe('UploadDirective', () => {
@@ -106,30 +107,35 @@ describe('UploadDirective', () => {
expect(event.preventDefault).not.toHaveBeenCalled();
});
it('should raise upload-files event on files drop', () => {
it('should raise upload-files event on files drop', (done) => {
directive.enabled = true;
let files = [<File> {}];
let event = jasmine.createSpyObj('event', ['preventDefault', 'stopPropagation']);
spyOn(directive, 'getDataTransfer').and.returnValue({});
spyOn(directive, 'getFilesDropped').and.returnValue(files);
spyOn(nativeElement, 'dispatchEvent').and.stub();
spyOn(directive, 'getFilesDropped').and.returnValue(Promise.resolve([
<FileInfo> {},
<FileInfo> {}
]));
spyOn(nativeElement, 'dispatchEvent').and.callFake(_ => {
done();
});
directive.onDrop(event);
expect(nativeElement.dispatchEvent).toHaveBeenCalled();
});
it('should provide dropped files in upload-files event', () => {
it('should provide dropped files in upload-files event', (done) => {
directive.enabled = true;
let files = [<File> {}];
let files = [
<FileInfo> {}
];
let event = jasmine.createSpyObj('event', ['preventDefault', 'stopPropagation']);
spyOn(directive, 'getDataTransfer').and.returnValue({});
spyOn(directive, 'getFilesDropped').and.returnValue(files);
spyOn(directive, 'getFilesDropped').and.returnValue(Promise.resolve(files));
spyOn(nativeElement, 'dispatchEvent').and.callFake(e => {
expect(e.detail.files.length).toBe(1);
expect(e.detail.files[0]).toBe(files[0]);
done();
});
directive.onDrop(event);
expect(nativeElement.dispatchEvent).toHaveBeenCalled();
});
});

View File

@@ -16,6 +16,7 @@
*/
import { Directive, Input, HostListener, ElementRef, Renderer, OnInit, NgZone, OnDestroy } from '@angular/core';
import { FileUtils, FileInfo } from '../utils/file-utils';
@Directive({
selector: '[adf-upload]'
@@ -129,14 +130,16 @@ export class UploadDirective implements OnInit, OnDestroy {
const dataTranfer = this.getDataTransfer(event);
if (dataTranfer) {
const files = this.getFilesDropped(dataTranfer);
this.onUploadFiles(files);
this.getFilesDropped(dataTranfer).then(files => {
this.onUploadFiles(files);
});
}
}
return false;
}
onUploadFiles(files: File[]) {
onUploadFiles(files: FileInfo[]) {
if (this.enabled && files.length > 0) {
let e = new CustomEvent('upload-files', {
detail: {
@@ -177,34 +180,55 @@ export class UploadDirective implements OnInit, OnDestroy {
* Extract files from the DataTransfer object used to hold the data that is being dragged during a drag and drop operation.
* @param dataTransfer DataTransfer object
*/
protected getFilesDropped(dataTransfer: DataTransfer): File[] {
const result: File[] = [];
protected getFilesDropped(dataTransfer: DataTransfer): Promise<FileInfo[]> {
return new Promise(resolve => {
const iterations = [];
if (dataTransfer) {
const items: FileList = dataTransfer.files;
if (dataTransfer) {
const items = dataTransfer.items;
if (items) {
for (let i = 0; i < items.length; i++) {
if (typeof items[i].webkitGetAsEntry !== 'undefined') {
let item = items[i].webkitGetAsEntry();
if (item) {
if (item.isFile) {
iterations.push(Promise.resolve(<FileInfo> {
entry: item,
file: items[i].getAsFile(),
relativeFolder: '/'
}));
} else if (item.isDirectory) {
iterations.push(new Promise(resolveFolder => {
FileUtils.flattern(item).then(files => resolveFolder(files));
}));
}
}
} else {
iterations.push(Promise.resolve(<FileInfo>{
entry: null,
file: items[i].getAsFile(),
relativeFolder: '/'
}));
}
}
} else {
// safari or FF
let files = FileUtils
.toFileArray(dataTransfer.files)
.map(file => <FileInfo> {
entry: null,
file: file,
relativeFolder: '/'
});
if (items && items.length > 0) {
for (let i = 0; i < items.length; i++) {
result.push(items[i]);
iterations.push(Promise.resolve(files));
}
}
}
return result;
}
/**
* Extract files from the FileList object used to hold files that user selected by means of File Dialog.
* @param fileList List of selected files
*/
protected getFilesSelected(fileList: FileList) {
let result: File[] = [];
if (fileList && fileList.length > 0) {
for (let i = 0; i < fileList.length; i++) {
result.push(fileList[i]);
}
}
return result;
Promise.all(iterations).then(result => {
resolve(result.reduce((a, b) => a.concat(b), []));
});
});
}
/**
@@ -214,8 +238,12 @@ export class UploadDirective implements OnInit, OnDestroy {
protected onSelectFiles(e: Event) {
if (this.isClickMode()) {
const input = (<HTMLInputElement>e.currentTarget);
const files = this.getFilesSelected(input.files);
this.onUploadFiles(files);
const files = FileUtils.toFileArray(input.files);
this.onUploadFiles(files.map(file => <FileInfo> {
entry: null,
file: file,
relativeFolder: '/'
}));
}
}
}

View File

@@ -0,0 +1,27 @@
/*!
* @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 { MinimalNodeEntity } from 'alfresco-js-api';
export interface FolderCreatedEvent {
name: string;
relativePath?: string;
parentId?: string;
node?: MinimalNodeEntity;
}

View File

@@ -0,0 +1,3 @@
{
}

View File

@@ -0,0 +1,31 @@
/*!
* @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 { MdSnackBarModule, MdToolbarModule, MdButtonModule } from '@angular/material';
const MATERIAL_MODULES = [
MdSnackBarModule,
MdToolbarModule,
MdButtonModule
];
@NgModule({
imports: MATERIAL_MODULES,
exports: MATERIAL_MODULES
})
export class MaterialModule {}

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.
*/
/**
*
* This object represent the basic structure of a card view.
*
*
* @returns {CardViewModel} .
*/
export class CardViewModel {
label: string;
value: any;
key: any;
format: string;
default: string;
constructor(obj?: any) {
this.label = obj.label || '';
this.value = obj.value;
this.key = obj.key;
this.format = obj.format;
this.default = obj.default;
}
}

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 './card-view.model';

View File

@@ -0,0 +1,36 @@
/*!
* @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 { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'adfFileSize'
})
export class FileSizePipe implements PipeTransform {
transform(bytes: number = 0, decimals: number = 2): string {
if (bytes === 0) {
return '0 Bytes';
}
const k = 1024,
dm = decimals || 2,
sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
}

View File

@@ -19,54 +19,29 @@ import { Injectable } from '@angular/core';
import { AlfrescoApi } from 'alfresco-js-api';
import * as alfrescoApi from 'alfresco-js-api';
import { AlfrescoSettingsService } from './alfresco-settings.service';
import { AppConfigService } from './app-config.service';
import { StorageService } from './storage.service';
@Injectable()
export class AlfrescoApiService {
private alfrescoApi: AlfrescoApi;
private provider: string;
private ticketEcm: string;
private ticketBpm: string;
private hostEcm: string;
private hostBpm: string;
private contextRoot: string;
private disableCsrf: boolean;
public getInstance(): AlfrescoApi {
return this.alfrescoApi;
}
constructor(private settingsService: AlfrescoSettingsService,
constructor(private appConfig: AppConfigService,
private settingsService: AlfrescoSettingsService,
private storage: StorageService) {
this.provider = this.settingsService.getProviders();
this.ticketEcm = this.getTicketEcm();
this.ticketBpm = this.getTicketBpm();
this.hostEcm = this.settingsService.ecmHost;
this.hostBpm = this.settingsService.bpmHost;
this.contextRoot = 'alfresco';
this.disableCsrf = false;
this.init();
settingsService.bpmHostSubject.subscribe((hostBpm) => {
this.hostBpm = hostBpm;
this.init();
});
settingsService.ecmHostSubject.subscribe((hostEcm) => {
this.hostEcm = hostEcm;
this.init();
});
settingsService.csrfSubject.subscribe((disableCsrf) => {
this.disableCsrf = disableCsrf;
this.init();
@@ -81,29 +56,12 @@ export class AlfrescoApiService {
private init() {
this.alfrescoApi = <AlfrescoApi>new alfrescoApi({
provider: this.provider,
ticketEcm: this.ticketEcm,
ticketBpm: this.ticketBpm,
hostEcm: this.hostEcm,
hostBpm: this.hostBpm,
contextRoot: this.contextRoot,
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.disableCsrf
});
}
/**
* The method return the ECM ticket stored in the Storage
* @returns ticket
*/
private getTicketEcm(): string {
return this.storage.getItem('ticket-ECM');
}
/**
* The method return the BPM ticket stored in the Storage
* @returns ticket
*/
private getTicketBpm(): string {
return this.storage.getItem('ticket-BPM');
}
}

View File

@@ -15,33 +15,47 @@
* limitations under the License.
*/
import { ReflectiveInjector } from '@angular/core';
import { TestBed, async } from '@angular/core/testing';
import { AlfrescoSettingsService } from './alfresco-settings.service';
import { AlfrescoAuthenticationService } from './alfresco-authentication.service';
import { AlfrescoApiService } from './alfresco-api.service';
import { StorageService } from './storage.service';
import { CookieService } from './cookie.service';
import { CookieServiceMock } from './../assets/cookie.service.mock';
import { LogService } from './log.service';
import { AppConfigModule } from './app-config.service';
declare let jasmine: any;
describe('AlfrescoAuthenticationService', () => {
let injector;
let apiService: AlfrescoApiService;
let authService: AlfrescoAuthenticationService;
let settingsService: AlfrescoSettingsService;
let storage: StorageService;
let cookie: CookieService;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
AppConfigModule
],
providers: [
AlfrescoSettingsService,
AlfrescoApiService,
AlfrescoAuthenticationService,
StorageService,
{ provide: CookieService, useClass: CookieServiceMock },
LogService
]
}).compileComponents();
}));
beforeEach(() => {
injector = ReflectiveInjector.resolveAndCreate([
AlfrescoSettingsService,
AlfrescoApiService,
AlfrescoAuthenticationService,
StorageService,
LogService
]);
authService = injector.get(AlfrescoAuthenticationService);
settingsService = injector.get(AlfrescoSettingsService);
storage = injector.get(StorageService);
apiService = TestBed.get(AlfrescoApiService);
authService = TestBed.get(AlfrescoAuthenticationService);
settingsService = TestBed.get(AlfrescoSettingsService);
cookie = TestBed.get(CookieService);
storage = TestBed.get(StorageService);
storage.clear();
jasmine.Ajax.install();
@@ -51,6 +65,64 @@ describe('AlfrescoAuthenticationService', () => {
jasmine.Ajax.uninstall();
});
describe('remembe me', () => {
beforeEach(() => {
settingsService.setProviders('ECM');
});
it('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('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('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(() => {
@@ -287,32 +359,6 @@ describe('AlfrescoAuthenticationService', () => {
});
});
describe('Setting service change should reflect in the api', () => {
beforeEach(() => {
settingsService.setProviders('ALL');
});
it('should host ecm url change be reflected in the api configuration', () => {
settingsService.ecmHost = '127.99.99.99';
expect(authService.alfrescoApi.getInstance().config.hostEcm).toBe('127.99.99.99');
});
it('should host bpm url change be reflected in the api configuration', () => {
settingsService.bpmHost = '127.99.99.99';
expect(authService.alfrescoApi.getInstance().config.hostBpm).toBe('127.99.99.99');
});
it('should host bpm provider change be reflected in the api configuration', () => {
settingsService.setProviders('ECM');
expect(authService.alfrescoApi.getInstance().config.provider).toBe('ECM');
});
});
describe('when the setting is both ECM and BPM ', () => {
beforeEach(() => {

View File

@@ -19,23 +19,29 @@ import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs/Rx';
import { AlfrescoSettingsService } from './alfresco-settings.service';
import { StorageService } from './storage.service';
import { CookieService } from './cookie.service';
import { LogService } from './log.service';
import { AlfrescoApiService } from './alfresco-api.service';
const REMEMBER_ME_COOKIE_KEY = 'ALFRESCO_REMEMBER_ME';
const REMEMBER_ME_UNTIL = 1000 * 60 * 60 * 24 * 30 ;
@Injectable()
export class AlfrescoAuthenticationService {
onLogin: Subject<any> = new Subject<any>();
onLogout: Subject<any> = new Subject<any>();
constructor(private settingsService: AlfrescoSettingsService,
public alfrescoApi: AlfrescoApiService,
private storage: StorageService,
private logService: LogService) {
constructor(
private settingsService: AlfrescoSettingsService,
public alfrescoApi: AlfrescoApiService,
private storage: StorageService,
private cookie: CookieService,
private logService: LogService) {
}
/**
* The method return tru if the user is logged in
* The method return true if the user is logged in
* @returns {boolean}
*/
isLoggedIn(): boolean {
@@ -48,17 +54,43 @@ export class AlfrescoAuthenticationService {
* @param password
* @returns {Observable<R>|Observable<T>}
*/
login(username: string, password: string): Observable<{ type: string, ticket: any }> {
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.settingsService.getProviders(), ticket: response};
return { type: this.settingsService.getProviders(), 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
@@ -130,7 +162,7 @@ export class AlfrescoAuthenticationService {
/**
* The method save the ECM and BPM ticket in the Storage
*/
saveTickets() {
saveTickets(): void {
this.saveTicketEcm();
this.saveTicketBpm();
}
@@ -155,16 +187,20 @@ export class AlfrescoAuthenticationService {
/**
* The method return true if user is logged in on ecm provider
*
* @returns {boolean}
*/
isEcmLoggedIn() {
return this.alfrescoApi.getInstance().ecmAuth && !!this.alfrescoApi.getInstance().ecmAuth.isLoggedIn();
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() {
return this.alfrescoApi.getInstance().bpmAuth && !!this.alfrescoApi.getInstance().bpmAuth.isLoggedIn();
isBpmLoggedIn(): boolean {
return this.isRememberMeSet() && this.alfrescoApi.getInstance().bpmAuth && !!this.alfrescoApi.getInstance().bpmAuth.isLoggedIn();
}
/**

View File

@@ -15,19 +15,22 @@
* limitations under the License.
*/
import { ReflectiveInjector } from '@angular/core';
import { TestBed, async } from '@angular/core/testing';
import { AlfrescoSettingsService } from './alfresco-settings.service';
import { AlfrescoAuthenticationService } from './alfresco-authentication.service';
import { AlfrescoContentService } from './alfresco-content.service';
import { AlfrescoApiService } from './alfresco-api.service';
import { StorageService } from './storage.service';
import { CookieService } from './cookie.service';
import { CookieServiceMock } from './../assets/cookie.service.mock';
import { LogService } from './log.service';
import { AppConfigModule } from './app-config.service';
declare let jasmine: any;
describe('AlfrescoContentService', () => {
let injector, contentService: AlfrescoContentService;
let contentService: AlfrescoContentService;
let authService: AlfrescoAuthenticationService;
let settingsService: AlfrescoSettingsService;
let storage: StorageService;
@@ -35,20 +38,30 @@ describe('AlfrescoContentService', () => {
const nodeId = 'fake-node-id';
beforeEach(() => {
injector = ReflectiveInjector.resolveAndCreate([
AlfrescoApiService,
AlfrescoContentService,
AlfrescoAuthenticationService,
AlfrescoSettingsService,
StorageService,
LogService
]);
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
AppConfigModule
],
declarations: [
],
providers: [
AlfrescoApiService,
AlfrescoContentService,
AlfrescoAuthenticationService,
AlfrescoSettingsService,
StorageService,
{ provide: CookieService, useClass: CookieServiceMock },
LogService
]
}).compileComponents();
}));
authService = injector.get(AlfrescoAuthenticationService);
settingsService = injector.get(AlfrescoSettingsService);
contentService = injector.get(AlfrescoContentService);
storage = injector.get(StorageService);
beforeEach(() => {
authService = TestBed.get(AlfrescoAuthenticationService);
settingsService = TestBed.get(AlfrescoSettingsService);
contentService = TestBed.get(AlfrescoContentService);
storage = TestBed.get(StorageService);
storage.clear();
node = {
@@ -70,7 +83,7 @@ describe('AlfrescoContentService', () => {
it('should return a valid content URL', (done) => {
authService.login('fake-username', 'fake-password').subscribe(() => {
expect(contentService.getContentUrl(node)).toBe('http://localhost:8080/alfresco/api/' +
expect(contentService.getContentUrl(node)).toBe('http://localhost:3000/ecm/alfresco/api/' +
'-default-/public/alfresco/versions/1/nodes/fake-node-id/content?attachment=false&alf_ticket=fake-post-ticket');
done();
});
@@ -85,7 +98,7 @@ describe('AlfrescoContentService', () => {
it('should return a valid thumbnail URL', (done) => {
authService.login('fake-username', 'fake-password').subscribe(() => {
expect(contentService.getDocumentThumbnailUrl(node))
.toBe('http://localhost:8080/alfresco/api/-default-/public/alfresco' +
.toBe('http://localhost:3000/ecm/alfresco/api/-default-/public/alfresco' +
'/versions/1/nodes/fake-node-id/renditions/doclib/content?attachment=false&alf_ticket=fake-post-ticket');
done();
});

View File

@@ -16,32 +16,80 @@
*/
import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs/Rx';
import { AlfrescoAuthenticationService } from './alfresco-authentication.service';
import { AlfrescoApiService } from './alfresco-api.service';
import { LogService } from './log.service';
import { FolderCreatedEvent } from '../events/folder-created.event';
@Injectable()
export class AlfrescoContentService {
folderCreated: Subject<FolderCreatedEvent> = new Subject<FolderCreatedEvent>();
constructor(public authService: AlfrescoAuthenticationService,
public apiService: AlfrescoApiService) {
public apiService: AlfrescoApiService,
private logService: LogService) {
}
/**
* Get thumbnail URL for the given document node.
* @param document Node to get URL for.
* @param nodeId {string} Node to get URL for.
* @returns {string} URL address.
*/
getDocumentThumbnailUrl(document: any): string {
return this.apiService.getInstance().content.getDocumentThumbnailUrl(document.entry.id);
getDocumentThumbnailUrl(nodeId: any): string {
if (nodeId && nodeId.entry) {
nodeId = nodeId.entry.id;
}
return this.apiService.getInstance().content.getDocumentThumbnailUrl(nodeId);
}
/**
* Get content URL for the given node.
* @param document Node to get URL for.
* @param nodeId {string} Node to get URL for.
* @returns {string} URL address.
*/
getContentUrl(document: any): string {
return this.apiService.getInstance().content.getContentUrl(document.entry.id);
getContentUrl(nodeId: any): string {
if (nodeId && nodeId.entry) {
nodeId = nodeId.entry.id;
}
return this.apiService.getInstance().content.getContentUrl(nodeId);
}
/**
* 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));
}
private handleError(error: any) {
this.logService.error(error);
return Observable.throw(error || 'Server error');
}
}

View File

@@ -15,35 +15,32 @@
* limitations under the License.
*/
import { TestBed, async } from '@angular/core/testing';
import { AppConfigModule } from './app-config.service';
import { AlfrescoSettingsService } from './alfresco-settings.service';
describe('AlfrescoSettingsService', () => {
let service: AlfrescoSettingsService;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
AppConfigModule
],
declarations: [
],
providers: [
AlfrescoSettingsService
]
}).compileComponents();
}));
beforeEach(() => {
service = new AlfrescoSettingsService();
service = TestBed.get(AlfrescoSettingsService);
});
it('should have default ECM host', () => {
expect(service.ecmHost).toBe(AlfrescoSettingsService.DEFAULT_ECM_ADDRESS);
});
it('should change host ECM', () => {
// this test ensures 'host' getter/setter working properly
let address = 'http://192.168.0.1';
service.ecmHost = address;
expect(service.ecmHost).toBe(address);
});
it('should have default BPM host', () => {
expect(service.bpmHost).toBe(AlfrescoSettingsService.DEFAULT_BPM_ADDRESS);
});
it('should change host BPM', () => {
// this test ensures 'host' getter/setter working properly
let address = 'http://192.168.0.1';
service.bpmHost = address;
expect(service.bpmHost).toBe(address);
it('should be exposed by the module', () => {
expect(service).toBeDefined();
});
});

View File

@@ -17,31 +17,23 @@
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { AppConfigService } from './app-config.service';
@Injectable()
export class AlfrescoSettingsService {
static DEFAULT_ECM_ADDRESS: string = 'http://' + window.location.hostname + ':8080';
static DEFAULT_BPM_ADDRESS: string = 'http://' + window.location.hostname + ':9999';
static DEFAULT_CSRF_CONFIG: boolean = false;
static DEFAULT_BPM_CONTEXT_PATH: string = '/activiti-app';
private _ecmHost: string = AlfrescoSettingsService.DEFAULT_ECM_ADDRESS;
private _bpmHost: string = AlfrescoSettingsService.DEFAULT_BPM_ADDRESS;
private _csrfDisabled: boolean = AlfrescoSettingsService.DEFAULT_CSRF_CONFIG;
private _bpmContextPath = AlfrescoSettingsService.DEFAULT_BPM_CONTEXT_PATH;
private providers: string = 'ALL'; // ECM, BPM , ALL
public bpmHostSubject: Subject<string> = new Subject<string>();
public ecmHostSubject: Subject<string> = new Subject<string>();
public csrfSubject: Subject<boolean> = new Subject<boolean>();
public providerSubject: Subject<string> = new Subject<string>();
constructor(private appConfig: AppConfigService) {}
public get ecmHost(): string {
return this._ecmHost;
return this.appConfig.get<string>('ecmHost');
}
public set csrfDisabled(csrfDisabled: boolean) {
@@ -49,22 +41,24 @@ export class AlfrescoSettingsService {
this._csrfDisabled = csrfDisabled;
}
/* @deprecated in 1.6.0 */
public set ecmHost(ecmHostUrl: string) {
this.ecmHostSubject.next(ecmHostUrl);
this._ecmHost = ecmHostUrl;
console.log('AlfrescoSettingsService.ecmHost is deprecated. Use AppConfigService instead.');
}
public get bpmHost(): string {
return this._bpmHost;
return this.appConfig.get<string>('bpmHost');
}
/* @deprecated in 1.6.0 */
public set bpmHost(bpmHostUrl: string) {
this.bpmHostSubject.next(bpmHostUrl);
this._bpmHost = bpmHostUrl;
console.log('AlfrescoSettingsService.bpmHost is deprecated. Use AppConfigService instead.');
}
/* @deprecated in 1.6.0 */
public getBPMApiBaseUrl(): string {
return this._bpmHost + this._bpmContextPath;
console.log('AlfrescoSettingsService.getBPMApiBaseUrl is deprecated.');
return this.bpmHost + '/activiti-app';
}
public getProviders(): string {

View File

@@ -18,7 +18,7 @@
import { Injectable } from '@angular/core';
import { Response, Http } from '@angular/http';
import { Observable } from 'rxjs/Rx';
import { TranslateLoader } from 'ng2-translate/ng2-translate';
import { TranslateLoader } from '@ngx-translate/core';
import { ComponentTranslationModel } from '../models/component.model';
import { LogService } from './log.service';

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { TranslateModule, TranslateLoader } from 'ng2-translate/ng2-translate';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { Injector } from '@angular/core';
import { ResponseOptions, Response, XHRBackend, HttpModule } from '@angular/http';
import { MockBackend, MockConnection } from '@angular/http/testing';
@@ -43,8 +43,10 @@ describe('TranslateLoader', () => {
imports: [
HttpModule,
TranslateModule.forRoot({
provide: TranslateLoader,
useClass: AlfrescoTranslateLoader
loader: {
provide: TranslateLoader,
useClass: AlfrescoTranslateLoader
}
})
],
providers: [

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { TranslateModule, TranslateLoader } from 'ng2-translate/ng2-translate';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { Injector } from '@angular/core';
import { ResponseOptions, Response, XHRBackend, HttpModule } from '@angular/http';
import { MockBackend, MockConnection } from '@angular/http/testing';
@@ -40,8 +40,10 @@ describe('AlfrescoTranslationService', () => {
imports: [
HttpModule,
TranslateModule.forRoot({
provide: TranslateLoader,
useClass: AlfrescoTranslateLoader
loader: {
provide: TranslateLoader,
useClass: AlfrescoTranslateLoader
}
})
],
providers: [

View File

@@ -17,7 +17,7 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import { TranslateService } from 'ng2-translate/ng2-translate';
import { TranslateService } from '@ngx-translate/core';
import { AlfrescoTranslateLoader } from './alfresco-translate-loader.service';
@Injectable()

View File

@@ -0,0 +1,91 @@
/*!
* @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, inject } from '@angular/core/testing';
import { HttpModule, XHRBackend, Response, ResponseOptions } from '@angular/http';
import { MockBackend, MockConnection } from '@angular/http/testing';
import { AppConfigModule, AppConfigService } from './app-config.service';
describe('AppConfigService', () => {
let appConfigService: AppConfigService;
const mockResponse = {
'ecmHost': 'http://localhost:4000/ecm',
'bpmHost': 'http://localhost:4000/ecm',
'application': {
'name': 'Custom Name'
}
};
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
HttpModule,
AppConfigModule
],
providers: [
{ provide: XHRBackend, useClass: MockBackend }
]
});
});
beforeEach(
inject([AppConfigService, XHRBackend], (appConfig: AppConfigService, mockBackend) => {
appConfigService = appConfig;
mockBackend.connections.subscribe((connection: MockConnection) => {
connection.mockRespond(new Response(new ResponseOptions({
body: JSON.stringify(mockResponse)
})));
});
})
);
it('should export service in the module', () => {
const service = TestBed.get(AppConfigService);
expect(service).toBeDefined();
});
it('should load external settings', () => {
appConfigService.load().then(config => {
expect(config).toEqual(mockResponse);
});
});
it('should retrieve settings', () => {
appConfigService.load().then(() => {
expect(appConfigService.get('ecmHost')).toBe(mockResponse.ecmHost);
expect(appConfigService.get('bpmHost')).toBe(mockResponse.bpmHost);
expect(appConfigService.get('application.name')).toBe(mockResponse.application.name);
});
});
it('should use default config file', () => {
expect(appConfigService.configFile).toBeNull();
appConfigService.load().then(() => {
expect(appConfigService.configFile).toBe('app.config.json');
});
});
it('should take custom config file', () => {
expect(appConfigService.configFile).toBeNull();
const name = 'custom.config.json';
appConfigService.load(name).then(() => {
expect(appConfigService.configFile).toBe(name);
});
});
});

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, APP_INITIALIZER, NgModule, ModuleWithProviders } from '@angular/core';
import { HttpModule, Http } from '@angular/http';
import { ObjectUtils } from '../utils/object-utils';
@Injectable()
export class AppConfigService {
private config: any = {
'ecmHost': 'http://localhost:3000/ecm',
'bpmHost': 'http://localhost:3000/bpm',
'application': {
'name': 'Alfresco'
}
};
configFile: string = null;
constructor(private http: Http) {}
get<T>(key: string): T {
return <T> ObjectUtils.getValue(this.config, key);
}
load(resource: string = 'app.config.json'): Promise<any> {
this.configFile = resource;
return new Promise((resolve, reject) => {
this.http.get(resource).subscribe(
data => {
this.config = Object.assign({}, this.config, data.json() || {});
resolve(this.config);
},
(err) => {
const errorMessage = `Error loading ${resource}`;
console.log(errorMessage);
resolve(this.config);
}
);
});
}
}
export function InitAppConfigServiceProvider(resource: string): any {
return {
provide: APP_INITIALIZER,
useFactory: (configService: AppConfigService) => {
return () => configService.load(resource);
},
deps: [
AppConfigService
],
multi: true
};
}
@NgModule({
imports: [
HttpModule
],
providers: [
AppConfigService
]
})
export class AppConfigModule {
static forRoot(resource: string): ModuleWithProviders {
return {
ngModule: AppConfigModule,
providers: [
AppConfigService,
InitAppConfigServiceProvider(resource)
]
};
}
}

View File

@@ -15,29 +15,41 @@
* limitations under the License.
*/
import { TestBed, async, inject } from '@angular/core/testing';
import { Router} from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { AlfrescoSettingsService } from './alfresco-settings.service';
import { AlfrescoAuthenticationService } from './alfresco-authentication.service';
import { AlfrescoApiService } from './alfresco-api.service';
import { StorageService } from './storage.service';
import { LogService } from './log.service';
import { CookieService } from './cookie.service';
import { CookieServiceMock } from './../assets/cookie.service.mock';
import { AuthGuardBpm } from './auth-guard-bpm.service';
import { Router} from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { TestBed, async, inject } from '@angular/core/testing';
import { AppConfigModule } from './app-config.service';
describe('AuthGuardService BPM', () => {
beforeEach(() => {
beforeEach(async(() => {
TestBed.configureTestingModule({
providers: [AuthGuardBpm,
imports: [
AppConfigModule,
RouterTestingModule
],
declarations: [
],
providers: [
AuthGuardBpm,
AlfrescoSettingsService,
AlfrescoApiService,
AlfrescoAuthenticationService,
StorageService,
LogService],
imports: [RouterTestingModule]
});
});
{ provide: CookieService, useClass: CookieServiceMock },
LogService
]
}).compileComponents();
}));
it('if the alfresco js api is logged in should canActivate be true',
async(inject([AuthGuardBpm, Router, AlfrescoSettingsService, StorageService, AlfrescoAuthenticationService], (auth, router, settingsService, storage, authService) => {

View File

@@ -15,29 +15,41 @@
* limitations under the License.
*/
import { TestBed, async, inject } from '@angular/core/testing';
import { Router} from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { AlfrescoSettingsService } from './alfresco-settings.service';
import { AlfrescoAuthenticationService } from './alfresco-authentication.service';
import { AlfrescoApiService } from './alfresco-api.service';
import { StorageService } from './storage.service';
import { CookieService } from './cookie.service';
import { CookieServiceMock } from './../assets/cookie.service.mock';
import { LogService } from './log.service';
import { AuthGuardEcm } from './auth-guard-ecm.service';
import { Router} from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { TestBed, async, inject } from '@angular/core/testing';
import { AppConfigModule } from './app-config.service';
describe('AuthGuardService ECM', () => {
beforeEach(() => {
beforeEach(async(() => {
TestBed.configureTestingModule({
providers: [AuthGuardEcm,
imports: [
AppConfigModule,
RouterTestingModule
],
declarations: [
],
providers: [
AuthGuardEcm,
AlfrescoSettingsService,
AlfrescoApiService,
AlfrescoAuthenticationService,
StorageService,
LogService],
imports: [RouterTestingModule]
});
});
{ provide: CookieService, useClass: CookieServiceMock },
LogService
]
}).compileComponents();
}));
it('if the alfresco js api is logged in should canActivate be true',
async(inject([AuthGuardEcm, Router, AlfrescoSettingsService, StorageService, AlfrescoAuthenticationService], (auth, router, settingsService, storage, authService) => {

View File

@@ -15,29 +15,39 @@
* limitations under the License.
*/
import { TestBed, async, inject } from '@angular/core/testing';
import { Router} from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { AlfrescoSettingsService } from './alfresco-settings.service';
import { AlfrescoAuthenticationService } from './alfresco-authentication.service';
import { AlfrescoApiService } from './alfresco-api.service';
import { StorageService } from './storage.service';
import { CookieService } from './cookie.service';
import { CookieServiceMock } from './../assets/cookie.service.mock';
import { LogService } from './log.service';
import { AuthGuard } from './auth-guard.service';
import { Router} from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { TestBed, async, inject } from '@angular/core/testing';
import { AppConfigModule } from './app-config.service';
describe('AuthGuardService', () => {
beforeEach(() => {
beforeEach(async(() => {
TestBed.configureTestingModule({
providers: [AuthGuard,
imports: [
AppConfigModule,
RouterTestingModule
],
providers: [
AuthGuard,
AlfrescoSettingsService,
AlfrescoApiService,
AlfrescoAuthenticationService,
StorageService,
LogService],
imports: [RouterTestingModule]
});
});
{ provide: CookieService, useClass: CookieServiceMock },
LogService
]
}).compileComponents();
}));
it('if the alfresco js api is logged in should canActivate be true',
async(inject([AuthGuard, Router, AlfrescoSettingsService, StorageService, AlfrescoAuthenticationService], (auth, router, settingsService, storage, authService) => {

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

@@ -17,6 +17,7 @@
export * from './content.service';
export * from './storage.service';
export * from './cookie.service';
export * from './alfresco-api.service';
export * from './alfresco-settings.service';
export * from './alfresco-content.service';
@@ -29,3 +30,4 @@ export * from './log.service';
export * from './alfresco-authentication.service';
export * from './alfresco-translation.service';
export * from './alfresco-translate-loader.service';
export * from './app-config.service';

View File

@@ -14,11 +14,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { Component } from '@angular/core';
import { NotificationService } from './notification.service';
import { MdSnackBarModule } from '@angular/material';
import { MdSnackBarModule, MdSnackBar, OverlayModule, OVERLAY_PROVIDERS, LiveAnnouncer } from '@angular/material';
describe('NotificationService', () => {
let fixture: ComponentFixture<ComponentThatProvidesNotificationService>;
@@ -27,11 +28,15 @@ describe('NotificationService', () => {
TestBed.configureTestingModule({
imports: [
BrowserAnimationsModule,
MdSnackBarModule.forRoot()
OverlayModule,
MdSnackBarModule
],
declarations: [ComponentThatProvidesNotificationService],
providers: [
NotificationService
NotificationService,
MdSnackBar,
OVERLAY_PROVIDERS,
LiveAnnouncer
]
});
@@ -43,33 +48,26 @@ describe('NotificationService', () => {
fixture.detectChanges();
});
describe('openSnackMessage', () => {
it('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', (done) => {
let promise = fixture.componentInstance.sendMessage();
promise.afterDismissed().subscribe(() => {
done();
});
fixture.detectChanges();
expect(document.querySelector('snack-bar-container')).not.toBeNull();
});
describe('openSnackMessageAction', () => {
it('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();
expect(document.querySelector('.md-simple-snackbar-action')).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();
});
});

View File

@@ -15,33 +15,40 @@
* limitations under the License.
*/
import { ReflectiveInjector } from '@angular/core';
import { TestBed, async } from '@angular/core/testing';
import { AlfrescoApiService } from './alfresco-api.service';
import { RenditionsService } from './renditions.service';
import { AlfrescoSettingsService } from './alfresco-settings.service';
import { StorageService } from './storage.service';
import { LogService } from './log.service';
import { fakeRedition, fakeReditionCreated, fakeReditionsList } from '../assets/renditionsService.mock';
import { AppConfigModule } from './app-config.service';
declare let jasmine: any;
declare let AlfrescoApi: any;
describe('RenditionsService', () => {
let service, injector;
let service: RenditionsService;
beforeEach(() => {
injector = ReflectiveInjector.resolveAndCreate([
AlfrescoApiService,
RenditionsService,
AlfrescoSettingsService,
StorageService,
LogService
]);
});
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
AppConfigModule
],
declarations: [
],
providers: [
AlfrescoApiService,
RenditionsService,
AlfrescoSettingsService,
StorageService,
LogService
]
}).compileComponents();
}));
beforeEach(() => {
jasmine.Ajax.install();
service = injector.get(RenditionsService);
service = TestBed.get(RenditionsService);
});
afterEach(() => {
@@ -63,7 +70,8 @@ describe('RenditionsService', () => {
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().url).toBe('http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/fake-node-id/renditions');
expect(jasmine.Ajax.requests.mostRecent().method).toBe('POST');
expect(jasmine.Ajax.requests.mostRecent().url).toBe('http://localhost:3000/ecm/alfresco/api/-default-/public/alfresco/versions/1/nodes/fake-node-id/renditions');
done();
});
@@ -74,6 +82,17 @@ describe('RenditionsService', () => {
});
});
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).toBe('http://localhost:3000/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) => {

View File

@@ -76,6 +76,19 @@ export class RenditionsService {
.catch(err => this.handleError(err));
}
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 !== 'CREATED');
});
}
private handleError(error: any): Observable<any> {
this.logService.error(error);
return Observable.throw(error || 'Server error');

View File

@@ -0,0 +1,73 @@
/*!
* @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 FileInfo {
entry?: WebKitFileEntry;
file?: File;
relativeFolder?: string;
}
export class FileUtils {
static flattern(folder: any): Promise<FileInfo[]> {
let reader = folder.createReader();
let files: FileInfo[] = [];
return new Promise(resolve => {
let iterations = [];
(function traverse() {
reader.readEntries((entries) => {
if (!entries.length) {
Promise.all(iterations).then(result => resolve(files));
} else {
iterations.push(Promise.all(entries.map(entry => {
if (entry.isFile) {
return new Promise(resolveFile => {
entry.file(function (f: File) {
files.push({
entry: entry,
file: f,
relativeFolder: entry.fullPath.replace(/\/[^\/]*$/, '')
});
resolveFile();
});
});
} else {
return FileUtils.flattern(entry).then(result => {
files.push(...result);
});
}
})));
// Try calling traverse() again for the same dir, according to spec
traverse();
}
});
})();
});
}
static toFileArray(fileList: FileList): File[] {
let result = [];
if (fileList && fileList.length > 0) {
for (let i = 0; i < fileList.length; i++) {
result.push(fileList[i]);
}
}
return result;
}
}

View File

@@ -16,3 +16,4 @@
*/
export * from './object-utils';
export * from './file-utils';

View File

@@ -0,0 +1,2 @@
@import './_variables-color.scss';
@import './_variables-mdl-overrides.scss';

View File

@@ -0,0 +1,37 @@
// Accent color palette
$alfresco-primary-accent--default: #ff9100;
$alfresco-primary-accent--hue-1: #ffd180;
$alfresco-primary-accent--hue-2: #ffab40;
$alfresco-primary-accent--hue-3: #ff6d00;
$alfresco-secondary-accent--default: #3d5afe;
$alfresco-secondary-accent--hue-1: #8c9eff;
$alfresco-secondary-accent--hue-2: #536dfe;
$alfresco-secondary-accent--hue-3: #304ffe;
// Warn color palette
$alfresco-warn-color--default: #ff1744;
$alfresco-warn-color--hue-1: #ff8a80;
$alfresco-warn-color--hue-2: #ff5252;
$alfresco-warn-color--hue-3: #d50000;
// Grayscale
$alfresco-white: #fff;
$alfresco-black: #000;
// Dark
$alfresco-dark-color--default: #78909c;
$alfresco-dark-color--hue-1: #eceff1;
$alfresco-dark-color--hue-3: #546e7a;
$alfresco-drop-shadow: #888888;
$alfresco-primary-text-color: rgba($alfresco-black, .87);
$alfresco-secondary-text-color: rgba($alfresco-black, .54);
$alfresco-hint-text-color: rgba($alfresco-black, .38);
$alfresco-disabled-text-color: rgba($alfresco-black, .26);
$alfresco-divider-color: rgba($alfresco-black, .07);
$alfresco-gray-background: #fafafa;

View File

@@ -0,0 +1,14 @@
@import './_variables-color.scss';
$button-fab-color-alt: $alfresco-primary-accent--default;
$layout-header-mobile-row-height: 65px;
$layout-header-desktop-row-height: 65px;
$layout-header-desktop-baseline: 25px;
$layout-header-mobile-baseline: 25px;
/* snackBar */
$md-snack-bar-container-background: $alfresco-white;
$md-simple-snackbar-message: $alfresco-secondary-text-color;
$md-simple-snackbar-action: $alfresco-primary-accent--hue-2;

View File

@@ -35,7 +35,9 @@
"ng2-alfresco-viewer": ["../ng2-alfresco-viewer/"],
"ng2-alfresco-webscript": ["../ng2-alfresco-webscript/"],
"ng2-alfresco-userinfo": ["../ng2-alfresco-userinfo"],
"alfresco-js-api": ["../node_modules/alfresco-js-api/"]
"alfresco-js-api": ["./node_modules/alfresco-js-api/"],
"@angular/*": ["./node_modules/@angular/*"],
"rxjs/*": ["./node_modules/rxjs/*"]
},
"lib": [
"es2015",

View File

@@ -1,17 +1 @@
const webpack = require('webpack');
const webpackMerge = require('webpack-merge');
const commonConfig = require('./config/webpack.common.js');
module.exports = webpackMerge(commonConfig, {
output: {
filename: './bundles/[name].js',
library: '[name]',
libraryTarget: 'umd',
chunkFilename: '[id].chunk.js'
},
entry: {
"ng2-alfresco-core": "./index.ts"
}
});
module.exports = require('./config/webpack.build.js');

View File

@@ -0,0 +1 @@
module.exports = require('./config/webpack.coverage.js');

View File

@@ -1,8 +1 @@
const webpack = require('webpack');
const webpackMerge = require('webpack-merge');
const testConfig = require('./config/webpack.test.js');
module.exports = webpackMerge(testConfig, {
});
module.exports = require('./config/webpack.test.js');