mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
1.6.0 (#2029)
* 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:
@@ -8,13 +8,13 @@ dist
|
||||
src/**/*.js
|
||||
src/**/*.js.map
|
||||
src/**/*.d.ts
|
||||
demo/**/*.js
|
||||
demo/**/*.js.map
|
||||
demo/**/*.d.ts
|
||||
index.js
|
||||
index.js.map
|
||||
index.js.map.gitignore
|
||||
!systemjs.config.js
|
||||
*.tgz
|
||||
/package/
|
||||
/bundles/
|
||||
index.d.ts
|
||||
/.happypack
|
||||
|
@@ -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
|
||||
|
@@ -1,188 +1,105 @@
|
||||
# Alfresco Upload Component for Angular
|
||||
# Alfresco Upload Component
|
||||
|
||||
<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-upload'>
|
||||
<img src='https://img.shields.io/npm/dt/ng2-alfresco-upload.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 -->
|
||||
|
||||
- [Content](#content)
|
||||
* [Components](#components)
|
||||
* [Services](#services)
|
||||
* [Directives](#directives)
|
||||
- [Prerequisites](#prerequisites)
|
||||
- [Install](#install)
|
||||
- [UploadButtonComponent](#uploadbuttoncomponent)
|
||||
* [Properties](#properties)
|
||||
* [Events](#events)
|
||||
* [Advanced usage](#advanced-usage)
|
||||
+ [How to show notification message with no permission](#how-to-show-notification-message-with-no-permission)
|
||||
+ [How to disable the button when the delete permission is missing](#how-to-disable-the-button-when-the-delete-permission-is-missing)
|
||||
- [UploadDragAreaComponent](#uploaddragareacomponent)
|
||||
* [Properties](#properties-1)
|
||||
* [Events](#events-1)
|
||||
- [FileUploadingDialogComponent](#fileuploadingdialogcomponent)
|
||||
- [UploadService](#uploadservice)
|
||||
* [Events](#events-2)
|
||||
- [Build from sources](#build-from-sources)
|
||||
- [NPM scripts](#npm-scripts)
|
||||
- [Demo](#demo)
|
||||
- [License](#license)
|
||||
|
||||
<!-- tocstop -->
|
||||
|
||||
<!-- markdown-toc end -->
|
||||
|
||||
## Content
|
||||
|
||||
### Components
|
||||
|
||||
- [FileUploadingDialogComponent](#fileuploadingdialogcomponent)
|
||||
- FileUploadingListComponent
|
||||
- [UploadButtonComponent](#uploadbuttoncomponent)
|
||||
- [UploadDragAreaComponent](#uploaddragareacomponent)
|
||||
|
||||
### Services
|
||||
|
||||
- [UploadService](#uploadservice)
|
||||
|
||||
### Directives
|
||||
|
||||
- FileDraggableDirective
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you start using this development framework, make sure you have installed all required software and done all the
|
||||
necessary configuration [prerequisites](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
|
||||
|
||||
Follow the 3 steps below:
|
||||
|
||||
1. Npm
|
||||
|
||||
```sh
|
||||
npm install ng2-alfresco-upload --save
|
||||
```
|
||||
|
||||
2. Html
|
||||
|
||||
Include these dependencies in your index.html page:
|
||||
|
||||
```html
|
||||
|
||||
<!-- Google Material Design Lite -->
|
||||
<link rel="stylesheet" href="node_modules/material-design-lite/material.min.css">
|
||||
<script src="node_modules/material-design-lite/material.min.js"></script>
|
||||
<link rel="stylesheet" href="node_modules/material-design-icons/iconfont/material-icons.css">
|
||||
|
||||
<!-- Load the Angular Material 2 stylesheet -->
|
||||
<link href="node_modules/@angular/material/core/theming/prebuilt/deeppurple-amber.css" rel="stylesheet">
|
||||
|
||||
<!-- Polyfill(s) for Safari (pre-10.x) -->
|
||||
<script src="node_modules/intl/dist/Intl.min.js"></script>
|
||||
<script src="node_modules/intl/locale-data/jsonp/en.js"></script>
|
||||
|
||||
<!-- Polyfill(s) for older browsers -->
|
||||
<script src="node_modules/core-js/client/shim.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/dom4/1.8.3/dom4.js"></script>
|
||||
<script src="node_modules/element.scrollintoviewifneeded-polyfill/index.js"></script>
|
||||
|
||||
<!-- Polyfill(s) for dialogs -->
|
||||
<script src="node_modules/dialog-polyfill/dialog-polyfill.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="node_modules/dialog-polyfill/dialog-polyfill.css" />
|
||||
<style>._dialog_overlay { position: static !important; } </style>
|
||||
|
||||
<!-- Modules -->
|
||||
<script src="node_modules/zone.js/dist/zone.js"></script>
|
||||
<script src="node_modules/reflect-metadata/Reflect.js"></script>
|
||||
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||
|
||||
```
|
||||
|
||||
3. SystemJs
|
||||
|
||||
Add the following components to your systemjs.config.js file:
|
||||
|
||||
- ng2-translate
|
||||
- alfresco-js-api
|
||||
- ng2-alfresco-core
|
||||
- ng2-alfresco-upload
|
||||
|
||||
Please refer to the following example file: [systemjs.config.js](demo/systemjs.config.js) .
|
||||
|
||||
|
||||
|
||||
#### Basic usage
|
||||
```sh
|
||||
npm install ng2-alfresco-upload
|
||||
```
|
||||
|
||||
## UploadButtonComponent
|
||||
|
||||
```html
|
||||
<alfresco-upload-button [showNotificationBar]="true"
|
||||
[uploadFolders]="true"
|
||||
[multipleFiles]="false"
|
||||
[acceptedFilesType]=".jpg,.gif,.png,.svg"
|
||||
[currentFolderPath]="/Sites/swsdp/documentLibrary"
|
||||
[versioning]="false"
|
||||
(onSuccess)="customMethod($event)">
|
||||
<alfresco-upload-button
|
||||
[rootFolderId]="-my-"
|
||||
[uploadFolders]="true"
|
||||
[multipleFiles]="false"
|
||||
[acceptedFilesType]=".jpg,.gif,.png,.svg"
|
||||
[versioning]="false"
|
||||
(onSuccess)="customMethod($event)">
|
||||
</alfresco-upload-button>
|
||||
<file-uploading-dialog></file-uploading-dialog>
|
||||
```
|
||||
|
||||
Example of an App that declares upload button component :
|
||||
|
||||
```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 { UploadModule } from 'ng2-alfresco-upload';
|
||||
|
||||
@Component({
|
||||
selector: 'alfresco-app-demo',
|
||||
template: `<alfresco-upload-button [showNotificationBar]="true"
|
||||
[uploadFolders]="false"
|
||||
[multipleFiles]="false"
|
||||
[acceptedFilesType]="'.jpg,.gif,.png,.svg'"
|
||||
(onSuccess)="onSuccess($event)">
|
||||
</alfresco-upload-button>
|
||||
<file-uploading-dialog></file-uploading-dialog>`
|
||||
})
|
||||
export class MyDemoApp {
|
||||
|
||||
constructor(private authService: AlfrescoAuthenticationService, private settingsService: AlfrescoSettingsService) {
|
||||
settingsService.ecmHost = 'http://localhost:8080';
|
||||
|
||||
this.authService.login('admin', 'admin').subscribe(
|
||||
ticket => {
|
||||
console.log(ticket);
|
||||
},
|
||||
error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
public onSuccess(event: Object): void {
|
||||
console.log('File uploaded');
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule,
|
||||
CoreModule.forRoot(),
|
||||
UploadModule.forRoot()
|
||||
],
|
||||
declarations: [ MyDemoApp ],
|
||||
bootstrap: [ MyDemoApp ]
|
||||
})
|
||||
export class AppModule { }
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
|
||||
```
|
||||
#### Events
|
||||
|
||||
| Name | Description |
|
||||
| --- | --- |
|
||||
| `onSuccess` | The event is emitted when the file is uploaded |
|
||||
|
||||
#### Properties
|
||||
### Properties
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `disabled` | *boolean* | false | Toggle component disabled state |
|
||||
| `showNotificationBar` | *boolean* | true | Hide/show notification bar |
|
||||
| `uploadFolders` | *boolean* | false | Allow/disallow upload folders (only for chrome) |
|
||||
| `multipleFiles` | *boolean* | false | Allow/disallow multiple files |
|
||||
| `acceptedFilesType` | *string* | * | array of allowed file extensions , example: ".jpg,.gif,.png,.svg" |
|
||||
| `currentFolderPath` | *string* | '/Sites/swsdp/documentLibrary' | define the path where the files are uploaded |
|
||||
| `versioning` | *boolean* | false | Versioning false is the default uploader behaviour and it rename using an integer suffix if there is a name clash. Versioning true to indicate that a major version should be created |
|
||||
| `staticTitle` | *string* | 'FILE_UPLOAD.BUTTON.UPLOAD_FILE' or 'FILE_UPLOAD.BUTTON.UPLOAD_FOLDER' string in the JSON text file | define the text of the upload button |
|
||||
| `disableWithNoPermission` | *boolean* | false | If the value is true and the user doesn't have the permission to delete the node the button will be disabled |
|
||||
| disabled | boolean | false | Toggle component disabled state |
|
||||
| **(deprecated)** showNotificationBar | boolean | true | Hide/show notification bar. **Deprecated in 1.6.0: use UploadService events and NotificationService api instead.** |
|
||||
| uploadFolders | boolean | false | Allow/disallow upload folders (only for chrome) |
|
||||
| multipleFiles | boolean | false | Allow/disallow multiple files |
|
||||
| acceptedFilesType | string | * | array of allowed file extensions , example: ".jpg,.gif,.png,.svg" |
|
||||
| **(deprecated)** currentFolderPath | string | '/Sites/swsdp/documentLibrary' | define the path where the files are uploaded. **Deprecated in 1.6.0: use rootFolderId instead.** |
|
||||
| rootFolderId | string | '-root-' | The ID of the root folder node. |
|
||||
| versioning | boolean | false | Versioning false is the default uploader behaviour and it rename using an integer suffix if there is a name clash. Versioning true to indicate that a major version should be created |
|
||||
| staticTitle | string | (predefined) | define the text of the upload button |
|
||||
| disableWithNoPermission | boolean | false | If the value is true and the user doesn't have the permission to delete the node the button will be disabled |
|
||||
|
||||
### Events
|
||||
|
||||
| Name | Description |
|
||||
| --- | --- |
|
||||
| onSuccess | Raised when the file is uploaded |
|
||||
|
||||
### Advanced usage
|
||||
|
||||
#### How to show notification message with no permission
|
||||
|
||||
### How to show notification message with no permission
|
||||
You can show a notification error when the user doesn't have the right permission to perform the action.
|
||||
The UploadButtonComponent provides the event permissionEvent that is raised when the delete permission is missing
|
||||
You can subscribe to this event from your component and use the NotificationService to show a message.
|
||||
@@ -192,12 +109,16 @@ You can subscribe to this event from your component and use the NotificationServ
|
||||
[rootFolderId]="currentFolderId"
|
||||
(permissionEvent)="onUploadPermissionFailed($event)">
|
||||
</alfresco-upload-button>
|
||||
```
|
||||
|
||||
```ts
|
||||
export class MyComponent {
|
||||
|
||||
onUploadPermissionFailed(event: any) {
|
||||
this.notificationService.openSnackMessage(`you don't have the ${event.permission} permission to ${event.action} the ${event.type} `, 4000);
|
||||
}
|
||||
onUploadPermissionFailed(event: any) {
|
||||
this.notificationService.openSnackMessage(
|
||||
`you don't have the ${event.permission} permission to ${event.action} the ${event.type} `, 4000
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
@@ -205,6 +126,7 @@ onUploadPermissionFailed(event: any) {
|
||||

|
||||
|
||||
#### How to disable the button when the delete permission is missing
|
||||
|
||||
You can easily disable the button when the user doesn't own the permission to perform the action.
|
||||
The UploadButtonComponent provides the property disableWithNoPermission that can be true. In this way the button should be disabled if the delete permission is missing for the node.
|
||||
|
||||
@@ -217,141 +139,82 @@ The UploadButtonComponent provides the property disableWithNoPermission that can
|
||||
|
||||

|
||||
|
||||
## UploadDragAreaComponent
|
||||
|
||||
|
||||
### Drag and drop
|
||||
This component, provide a drag and drop are to upload files to alfresco.
|
||||
|
||||
#### Basic usage
|
||||
|
||||
```html
|
||||
<alfresco-upload-drag-area (onSuccess)="customMethod($event)"></alfresco-upload-drag-area>
|
||||
<alfresco-upload-drag-area (onSuccess)="customMethod($event)">
|
||||
<div style="width: 200px; height: 100px; border: 1px solid #888888">
|
||||
DRAG HERE
|
||||
</div>
|
||||
</alfresco-upload-drag-area>
|
||||
<file-uploading-dialog></file-uploading-dialog>
|
||||
```
|
||||
|
||||
Example of an App that declares upload drag and drop component :
|
||||
|
||||
```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 { UploadModule } from 'ng2-alfresco-upload';
|
||||
|
||||
@Component({
|
||||
selector: 'alfresco-app-demo',
|
||||
template: `<alfresco-upload-drag-area (onSuccess)="customMethod($event)" >
|
||||
<div style="width: 200px; height: 100px; border: 1px solid #888888">
|
||||
DRAG HERE
|
||||
</div>
|
||||
</alfresco-upload-drag-area>
|
||||
<file-uploading-dialog></file-uploading-dialog>`
|
||||
})
|
||||
export class MyDemoApp {
|
||||
|
||||
constructor(private authService: AlfrescoAuthenticationService, private settingsService: AlfrescoSettingsService) {
|
||||
settingsService.ecmHost = 'http://localhost:8080';
|
||||
|
||||
this.authService.login('admin', 'admin').subscribe(
|
||||
ticket => {
|
||||
console.log(ticket);
|
||||
},
|
||||
error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
export class AppComponent {
|
||||
|
||||
public onSuccess(event: Object): void {
|
||||
console.log('File uploaded');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule,
|
||||
CoreModule.forRoot(),
|
||||
UploadModule.forRoot()
|
||||
],
|
||||
declarations: [ MyDemoApp ],
|
||||
bootstrap: [ MyDemoApp ]
|
||||
})
|
||||
export class AppModule { }
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
|
||||
```
|
||||
|
||||
#### Events
|
||||
|
||||
| Name | Description |
|
||||
| --- | --- |
|
||||
| `onSuccess` | The event is emitted when the file is uploaded |
|
||||
|
||||
#### Propertoes
|
||||
### Properties
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `showNotificationBar` | *boolean* | true | Hide/show notification bar |
|
||||
| `currentFolderPath` | *string* | '/Sites/swsdp/documentLibrary' | define the path where the files are uploaded |
|
||||
| `versioning` | *boolean* | false | Versioning false is the default uploader behaviour and it rename using an integer suffix if there is a name clash. Versioning true to indicate that a major version should be created |
|
||||
| enabled | boolean | true | Toggle component enabled state |
|
||||
| **(deprecated)** showNotificationBar | boolean | true | Hide/show notification bar. **Deprecated in 1.6.0: use UploadService events and NotificationService api instead.** |
|
||||
| rootFolderId | string | '-root-' | The ID of the root folder node. |
|
||||
| **(deprecated)** currentFolderPath | string | '/' | define the path where the files are uploaded. **Deprecated in 1.6.0: use rootFolderId instead.** |
|
||||
| versioning | boolean | false | Versioning false is the default uploader behaviour and it rename using an integer suffix if there is a name clash. Versioning true to indicate that a major version should be created |
|
||||
|
||||
### Events
|
||||
|
||||
### Files Dialog
|
||||
| Name | Description |
|
||||
| --- | --- |
|
||||
| onSuccess | Raised when the file is uploaded |
|
||||
|
||||
## FileUploadingDialogComponent
|
||||
|
||||
This component provides a dialog that shows all the files uploaded with upload button or drag & drop area components.
|
||||
This component should be used in combination with upload button or drag & drop area.
|
||||
|
||||
#### Basic usage
|
||||
|
||||
```html
|
||||
<file-uploading-dialog></file-uploading-dialog>
|
||||
```
|
||||
|
||||
## UploadService
|
||||
|
||||
Provides access to various APIs related to file upload features.
|
||||
|
||||
### Events
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| queueChanged | FileModel[] | Raised every time the file queue changes. |
|
||||
| fileUpload | FileUploadEvent | Raised every time a File model changes its state. |
|
||||
| fileUploadStarting | FileUploadEvent | Raised when upload starts. |
|
||||
| fileUploadCancelled | FileUploadEvent | Raised when upload gets cancelled by user. |
|
||||
| fileUploadProgress | FileUploadEvent | Raised during file upload process and contains the current progress for the particular File model. |
|
||||
| fileUploadAborted | FileUploadEvent | Raised when file upload gets aborted by the server. |
|
||||
| fileUploadError | FileUploadEvent | Raised when an error occurs to file upload. |
|
||||
| fileUploadComplete | FileUploadCompleteEvent | Raised when file upload is complete. |
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
## Demo
|
||||
|
||||
If you want have a demo of how the component works, please check the demo folder :
|
||||
|
||||
```sh
|
||||
cd demo
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
> The `build` task rebuilds all the code, runs tslint, license checks
|
||||
> and other quality check tools before performing unit testing.
|
||||
|
||||
## NPM scripts
|
||||
|
||||
@@ -362,6 +225,16 @@ npm start
|
||||
| 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)
|
||||
|
35
ng2-components/ng2-alfresco-upload/config/webpack.build.js
Normal file
35
ng2-components/ng2-alfresco-upload/config/webpack.build.js
Normal 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-upload": "./index.ts"
|
||||
}
|
||||
});
|
@@ -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,14 @@ 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: {
|
||||
alias: {
|
||||
"ng2-alfresco-core": helpers.root('../ng2-alfresco-core/index.ts')
|
||||
},
|
||||
extensions: ['.ts', '.js'],
|
||||
symlinks: false,
|
||||
modules: [helpers.root('../../ng2-components'), helpers.root('node_modules')]
|
||||
},
|
||||
|
||||
module: {
|
||||
rules: [
|
||||
@@ -37,25 +34,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 +58,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 +92,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 +123,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
|
||||
|
@@ -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/
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
@@ -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'
|
||||
});
|
||||
|
10
ng2-components/ng2-alfresco-upload/demo/config/helpers.js
Normal file
10
ng2-components/ng2-alfresco-upload/demo/config/helpers.js
Normal file
@@ -0,0 +1,10 @@
|
||||
var path = require('path');
|
||||
|
||||
var _root = path.resolve(__dirname, '..');
|
||||
|
||||
function root(args) {
|
||||
args = Array.prototype.slice.call(arguments, 0);
|
||||
return path.join.apply(path, [_root].concat(args));
|
||||
}
|
||||
|
||||
exports.root = root;
|
133
ng2-components/ng2-alfresco-upload/demo/config/webpack.common.js
Normal file
133
ng2-components/ng2-alfresco-upload/demo/config/webpack.common.js
Normal file
@@ -0,0 +1,133 @@
|
||||
const webpack = require('webpack');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
const ExtractTextPlugin = require("extract-text-webpack-plugin");
|
||||
const helpers = require('./helpers');
|
||||
const path = require('path');
|
||||
|
||||
const alfrescoLibs = [
|
||||
'ng2-alfresco-upload'
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
'polyfills': './src/polyfills.ts',
|
||||
'vendor': './src/vendor.ts',
|
||||
'dist': './src/main.ts'
|
||||
},
|
||||
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
enforce: 'pre',
|
||||
test: /\.js$/,
|
||||
include: [helpers.root('src'), helpers.root('../ng2-components')],
|
||||
loader: 'source-map-loader',
|
||||
exclude: [ /node_modules/, /public/, /resources/, /dist/]
|
||||
},
|
||||
{
|
||||
test: /\.ts$/,
|
||||
include: [helpers.root('src'), helpers.root('..')],
|
||||
loader: [
|
||||
'ts-loader',
|
||||
'angular2-template-loader'
|
||||
],
|
||||
exclude: [ /node_modules/, /public/, /resources/, /dist/]
|
||||
},
|
||||
{
|
||||
enforce: 'pre',
|
||||
test: /\.ts$/,
|
||||
loader: 'tslint-loader',
|
||||
include: [helpers.root('src')],
|
||||
options: {
|
||||
emitErrors: true
|
||||
},
|
||||
exclude: [ /node_modules/, /public/, /resources/, /dist/]
|
||||
},
|
||||
{
|
||||
enforce: 'pre',
|
||||
test: /\.ts$/,
|
||||
use: 'source-map-loader',
|
||||
exclude: [ /public/, /resources/, /dist/]
|
||||
},
|
||||
{
|
||||
test: /\.html$/,
|
||||
loader: 'html-loader',
|
||||
exclude: [ /node_modules/, /public/, /resources/, /dist/]
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
exclude: [helpers.root('src'), helpers.root('../ng2-components')],
|
||||
loader: ExtractTextPlugin.extract({
|
||||
fallback: 'style-loader',
|
||||
use: 'css-loader?sourceMap'
|
||||
})
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
include: [helpers.root('src'), helpers.root('../ng2-components')],
|
||||
loader: 'raw-loader'
|
||||
},
|
||||
{
|
||||
test: /\.component.scss$/,
|
||||
use: ['to-string-loader', 'raw-loader', 'sass-loader']
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
|
||||
loader: 'file-loader?name=assets/[name].[hash].[ext]'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
plugins: [
|
||||
// Workaround for angular/angular#11580
|
||||
new webpack.ContextReplacementPlugin(
|
||||
// The (\\|\/) piece accounts for path separators in *nix and Windows
|
||||
/angular(\\|\/)core(\\|\/)@angular/,
|
||||
helpers.root('./src'), // location of your src
|
||||
{} // a map of your routes
|
||||
),
|
||||
new HtmlWebpackPlugin({
|
||||
template: './index.html'
|
||||
}),
|
||||
|
||||
new CopyWebpackPlugin([
|
||||
... alfrescoLibs.map(lib => {
|
||||
return {
|
||||
context: `../ng2-components/${lib}/bundles/assets/` ,
|
||||
from: '**/*',
|
||||
to: `assets/`
|
||||
}
|
||||
}),
|
||||
{
|
||||
context: 'resources/i18n',
|
||||
from: '**/*.json',
|
||||
to: 'resources/i18n'
|
||||
},
|
||||
... alfrescoLibs.map(lib => {
|
||||
return {
|
||||
context: 'node_modules',
|
||||
from: `${lib}/src/i18n/*.json`,
|
||||
to: 'node_modules'
|
||||
}
|
||||
})
|
||||
]),
|
||||
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: ['src', 'vendor', 'polyfills']
|
||||
})
|
||||
],
|
||||
|
||||
devServer: {
|
||||
contentBase: helpers.root('dist'),
|
||||
compress: true,
|
||||
port: 3000,
|
||||
historyApiFallback: true,
|
||||
host: '0.0.0.0',
|
||||
inline: true
|
||||
},
|
||||
|
||||
node: {
|
||||
fs: 'empty'
|
||||
}
|
||||
};
|
@@ -0,0 +1,36 @@
|
||||
const webpack = require('webpack');
|
||||
const webpackMerge = require('webpack-merge');
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||
const commonConfig = require('./webpack.common.js');
|
||||
const helpers = require('./helpers');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = webpackMerge(commonConfig, {
|
||||
|
||||
devtool: 'cheap-module-eval-source-map',
|
||||
|
||||
output: {
|
||||
path: helpers.root('dist'),
|
||||
filename: '[name].js',
|
||||
chunkFilename: '[id].chunk.js'
|
||||
},
|
||||
|
||||
resolve: {
|
||||
alias: {
|
||||
"ng2-alfresco-core$": path.resolve(__dirname, '../../ng2-alfresco-core/index.ts'),
|
||||
"ng2-alfresco-upload$": path.resolve(__dirname, '../../ng2-alfresco-upload/index.ts')
|
||||
},
|
||||
extensions: ['.ts', '.js'],
|
||||
modules: [path.resolve(__dirname, '../node_modules')]
|
||||
},
|
||||
|
||||
plugins: [
|
||||
new webpack.NoEmitOnErrorsPlugin(),
|
||||
new ExtractTextPlugin('[name].[hash].css'),
|
||||
new webpack.LoaderOptionsPlugin({
|
||||
htmlLoader: {
|
||||
minimize: false // workaround for ng2
|
||||
}
|
||||
})
|
||||
]
|
||||
});
|
@@ -0,0 +1,65 @@
|
||||
const webpack = require('webpack');
|
||||
const webpackMerge = require('webpack-merge');
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||
const commonConfig = require('./webpack.common.js');
|
||||
const helpers = require('./helpers');
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
|
||||
const ENV = process.env.NODE_ENV = process.env.ENV = 'production';
|
||||
|
||||
const alfrescoLibs = [
|
||||
'ng2-alfresco-upload'
|
||||
];
|
||||
|
||||
module.exports = webpackMerge(commonConfig, {
|
||||
|
||||
devtool: 'source-map',
|
||||
|
||||
output: {
|
||||
path: helpers.root('dist'),
|
||||
publicPath: '/',
|
||||
filename: '[name].[hash].js',
|
||||
chunkFilename: '[id].[hash].chunk.js'
|
||||
},
|
||||
|
||||
resolve: {
|
||||
extensions: ['.ts', '.js'],
|
||||
modules: [helpers.root('node_modules')]
|
||||
},
|
||||
|
||||
plugins: [
|
||||
new CopyWebpackPlugin([
|
||||
... alfrescoLibs.map(lib => {
|
||||
return {
|
||||
context: `node_modules/${lib}/bundles/assets/` ,
|
||||
from: '**/*',
|
||||
to: `assets/`
|
||||
}
|
||||
})
|
||||
]),
|
||||
new webpack.NoEmitOnErrorsPlugin(),
|
||||
new webpack.optimize.UglifyJsPlugin({ // https://github.com/angular/angular/issues/10618
|
||||
mangle: {
|
||||
keep_fnames: true
|
||||
},
|
||||
compress: {
|
||||
warnings: false
|
||||
},
|
||||
output: {
|
||||
comments: false
|
||||
},
|
||||
sourceMap: true
|
||||
}),
|
||||
new ExtractTextPlugin('[name].[hash].css'),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
'ENV': JSON.stringify(ENV)
|
||||
}
|
||||
}),
|
||||
new webpack.LoaderOptionsPlugin({
|
||||
htmlLoader: {
|
||||
minimize: false // workaround for ng2
|
||||
}
|
||||
})
|
||||
]
|
||||
});
|
@@ -7,38 +7,7 @@
|
||||
<title>Alfresco Angular 2 Upload - Demo</title>
|
||||
<base href="./">
|
||||
|
||||
<!-- Google Material Design Lite -->
|
||||
<link rel="stylesheet" href="node_modules/material-design-lite/material.min.css">
|
||||
<script src="node_modules/material-design-lite/material.min.js"></script>
|
||||
<link rel="stylesheet" href="node_modules/material-design-icons/iconfont/material-icons.css">
|
||||
|
||||
<!-- Polyfill(s) for Safari (pre-10.x) -->
|
||||
<script src="node_modules/intl/dist/Intl.min.js"></script>
|
||||
<script src="node_modules/intl/locale-data/jsonp/en.js"></script>
|
||||
|
||||
<!-- Polyfill(s) for older browsers -->
|
||||
<script src="node_modules/core-js/client/shim.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/dom4/1.8.3/dom4.js"></script>
|
||||
<script src="node_modules/element.scrollintoviewifneeded-polyfill/index.js"></script>
|
||||
|
||||
<!-- Polyfill(s) for dialogs -->
|
||||
<script src="node_modules/dialog-polyfill/dialog-polyfill.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="node_modules/dialog-polyfill/dialog-polyfill.css" />
|
||||
<style>._dialog_overlay { position: static !important; } </style>
|
||||
|
||||
<!-- Modules -->
|
||||
<script src="node_modules/zone.js/dist/zone.js"></script>
|
||||
<script src="node_modules/reflect-metadata/Reflect.js"></script>
|
||||
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||
|
||||
<!-- Load the Angular Material 2 stylesheet -->
|
||||
<link href="node_modules/@angular/material/core/theming/prebuilt/deeppurple-amber.css" rel="stylesheet">
|
||||
|
||||
<script src="systemjs.config.js"></script>
|
||||
<script>
|
||||
System.import('app').catch(function(err){ console.error(err); });
|
||||
</script>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
79
ng2-components/ng2-alfresco-upload/demo/package-lock.json
generated
Normal file
79
ng2-components/ng2-alfresco-upload/demo/package-lock.json
generated
Normal file
@@ -0,0 +1,79 @@
|
||||
{
|
||||
"name": "ng2-alfresco-upload-demo",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
|
||||
"integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=",
|
||||
"dev": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||
"dev": true
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
|
||||
"dev": true
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
|
||||
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
|
||||
"dev": true
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||
"dev": true
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
|
||||
"dev": true
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"dev": true
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"dev": true
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
|
||||
"dev": true
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz",
|
||||
"integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=",
|
||||
"dev": true
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,19 +3,16 @@
|
||||
"description": "Alfresco Angular2 Upload Component - Demo",
|
||||
"version": "0.1.0",
|
||||
"author": "Alfresco Software, Ltd.",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"clean": "npm run clean-build && rimraf dist node_modules typings dist",
|
||||
"clean-build" : "rimraf 'src/{,**/}**.js' 'src/{,**/}**.js.map' 'src/{,**/}**.d.ts'",
|
||||
"postinstall": "npm run build",
|
||||
"start": "npm run build && concurrently \"npm run tsc:w\" \"npm run server\" ",
|
||||
"server": "wsrv -o -s -l",
|
||||
"build": "npm run tslint && npm run clean-build && npm run tsc",
|
||||
"build:w": "npm run tslint && rimraf dist && npm run tsc:w",
|
||||
"travis": "npm link ng2-alfresco-core ng2-alfresco-upload",
|
||||
"tsc": "tsc",
|
||||
"tsc:w": "tsc -w",
|
||||
"tslint": "tslint -c tslint.json *.ts && tslint -c tslint.json src/{,**/}**.ts -e '{,**/}**.d.ts'"
|
||||
"build": "rimraf dist && npm run webpack -- --config config/webpack.prod.js --progress --profile --bail",
|
||||
"build:dev": "rimraf dist && npm run webpack -- --config config/webpack.dev.js --progress --profile --bail",
|
||||
"start:dist": "wsrv -s dist/ -p 3000 -a 0.0.0.0",
|
||||
"start": "npm run webpack-dev-server -- --config config/webpack.prod.js --progress --content-base app/",
|
||||
"start:dev": "npm run webpack-dev-server -- --config config/webpack.dev.js --progress --content-base app/",
|
||||
"clean": "npm run clean-build && rimraf dist node_modules typings dist",
|
||||
"clean-build": "rimraf 'app/{,**/}**.js' 'app/{,**/}**.js.map' 'app/{,**/}**.d.ts'",
|
||||
"webpack-dev-server": "node --max_old_space_size=4096 node_modules/webpack-dev-server/bin/webpack-dev-server.js",
|
||||
"webpack": "webpack"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"contributors": [
|
||||
@@ -56,32 +53,75 @@
|
||||
"@angular/platform-browser": "~4.0.0",
|
||||
"@angular/platform-browser-dynamic": "~4.0.0",
|
||||
"@angular/router": "~4.0.0",
|
||||
|
||||
"@angular/material": "2.0.0-beta.1",
|
||||
"alfresco-js-api": "~1.5.0",
|
||||
"alfresco-js-api": "~1.6.0",
|
||||
"core-js": "2.4.1",
|
||||
"hammerjs": "2.0.8",
|
||||
"ng2-alfresco-core": "1.5.0",
|
||||
"ng2-translate": "5.0.0",
|
||||
"ng2-alfresco-core": "1.6.0",
|
||||
"@ngx-translate/core": "^7.0.0",
|
||||
"reflect-metadata": "0.1.10",
|
||||
"rxjs": "5.1.0",
|
||||
"systemjs": "0.19.27",
|
||||
"zone.js": "0.7.6",
|
||||
|
||||
"intl": "1.2.4",
|
||||
"dialog-polyfill": "0.4.7",
|
||||
"element.scrollintoviewifneeded-polyfill": "1.0.1",
|
||||
"material-design-icons": "2.2.3",
|
||||
"material-design-lite": "1.2.1",
|
||||
"ng2-alfresco-upload": "1.5.0"
|
||||
"ng2-alfresco-upload": "1.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jasmine": "^2.2.33",
|
||||
"@types/node": "^6.0.42",
|
||||
"concurrently": "^2.2.0",
|
||||
"rimraf": "2.5.2",
|
||||
"tslint": "3.15.1",
|
||||
"typescript": "^2.0.3",
|
||||
"wsrv": "^0.1.5"
|
||||
"@types/hammerjs": "^2.0.34",
|
||||
"@types/jasmine": "2.5.35",
|
||||
"@types/node": "6.0.45",
|
||||
"angular2-template-loader": "^0.6.2",
|
||||
"autoprefixer": "^6.5.4",
|
||||
"copy-webpack-plugin": "^4.0.1",
|
||||
"css-loader": "^0.23.1",
|
||||
"css-to-string-loader": "^0.1.2",
|
||||
"cssnano": "^3.8.1",
|
||||
"extract-text-webpack-plugin": "^2.0.0-rc.3",
|
||||
"file-loader": "0.11.1",
|
||||
"html-loader": "^0.4.4",
|
||||
"html-webpack-plugin": "^2.28.0",
|
||||
"istanbul-instrumenter-loader": "0.2.0",
|
||||
"jasmine-ajax": "^3.2.0",
|
||||
"jasmine-core": "2.4.1",
|
||||
"karma": "^0.13.22",
|
||||
"karma-chrome-launcher": "~1.0.1",
|
||||
"karma-coverage": "^1.1.1",
|
||||
"karma-jasmine": "~1.0.2",
|
||||
"karma-jasmine-ajax": "^0.1.13",
|
||||
"karma-jasmine-html-reporter": "0.2.0",
|
||||
"karma-mocha-reporter": "^2.2.2",
|
||||
"karma-remap-istanbul": "^0.6.0",
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"karma-systemjs": "^0.16.0",
|
||||
"karma-webpack": "^2.0.2",
|
||||
"loader-utils": "^1.1.0",
|
||||
"merge-stream": "^1.0.1",
|
||||
"node-sass": "^3.13.1",
|
||||
"null-loader": "^0.1.1",
|
||||
"package-json-merge": "0.0.1",
|
||||
"raw-loader": "^0.5.1",
|
||||
"remap-istanbul": "^0.6.3",
|
||||
"rimraf": "^2.6.1",
|
||||
"run-sequence": "^1.2.2",
|
||||
"sass-loader": "6.0.2",
|
||||
"script-loader": "0.7.0",
|
||||
"source-map-loader": "^0.1.6",
|
||||
"style-loader": "^0.13.1",
|
||||
"systemjs-builder": "^0.15.34",
|
||||
"to-string-loader": "^1.1.4",
|
||||
"traceur": "^0.0.91",
|
||||
"ts-loader": "^2.0.0",
|
||||
"ts-node": "^1.7.0",
|
||||
"tslint": "^4.4.2",
|
||||
"tslint-loader": "^3.3.0",
|
||||
"typescript": "^2.1.6",
|
||||
"webpack": "^2.2.1",
|
||||
"webpack-dev-server": "^2.3.0",
|
||||
"webpack-merge": "2.6.1",
|
||||
"wsrv": "^0.1.7"
|
||||
}
|
||||
}
|
||||
|
17
ng2-components/ng2-alfresco-upload/demo/src/polyfills.ts
Normal file
17
ng2-components/ng2-alfresco-upload/demo/src/polyfills.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import 'core-js/es6';
|
||||
import 'core-js/es7/reflect';
|
||||
import 'intl';
|
||||
|
||||
require('zone.js/dist/zone'); // IE 8-11
|
||||
require('element.scrollintoviewifneeded-polyfill'); // IE/FF
|
||||
|
||||
if (process.env.ENV === 'production') {
|
||||
// Production
|
||||
|
||||
} else {
|
||||
// Development
|
||||
|
||||
Error['stackTraceLimit'] = Infinity;
|
||||
|
||||
require('zone.js/dist/long-stack-trace-zone');
|
||||
}
|
26
ng2-components/ng2-alfresco-upload/demo/src/vendor.ts
Normal file
26
ng2-components/ng2-alfresco-upload/demo/src/vendor.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
// Angular
|
||||
import '@angular/platform-browser';
|
||||
import '@angular/platform-browser-dynamic';
|
||||
import '@angular/core';
|
||||
import '@angular/common';
|
||||
import '@angular/http';
|
||||
import '@angular/router';
|
||||
|
||||
// RxJS
|
||||
import 'rxjs';
|
||||
|
||||
// hammerjs
|
||||
import 'hammerjs';
|
||||
|
||||
// Alfresco
|
||||
import 'alfresco-js-api';
|
||||
import 'ng2-alfresco-upload';
|
||||
|
||||
// Google Material Design Lite
|
||||
import 'material-design-lite/material.js';
|
||||
import 'material-design-lite/dist/material.orange-blue.min.css';
|
||||
import 'material-design-icons/iconfont/material-icons.css';
|
||||
|
||||
// Polyfill(s) for dialogs
|
||||
require('script-loader!dialog-polyfill/dialog-polyfill');
|
||||
import 'dialog-polyfill/dialog-polyfill.css';
|
@@ -1,51 +0,0 @@
|
||||
/**
|
||||
* System configuration for Angular 2 samples
|
||||
* Adjust as necessary for your application needs.
|
||||
*/
|
||||
(function (global) {
|
||||
System.config({
|
||||
paths: {
|
||||
// paths serve as alias
|
||||
'npm:': 'node_modules/'
|
||||
},
|
||||
// map tells the System loader where to look for things
|
||||
map: {
|
||||
// our app is within the app folder
|
||||
app: 'src',
|
||||
// angular bundles
|
||||
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
|
||||
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
|
||||
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
|
||||
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
|
||||
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
|
||||
'@angular/http': 'npm:@angular/http/bundles/http.umd.js',
|
||||
'@angular/router': 'npm:@angular/router/bundles/router.umd.js',
|
||||
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
|
||||
'@angular/material': 'npm:@angular/material/bundles/material.umd.js',
|
||||
'@angular/animations': 'npm:@angular/animations/bundles/animations.umd.min.js',
|
||||
'@angular/animations/browser':'npm:@angular/animations/bundles/animations-browser.umd.js',
|
||||
'@angular/platform-browser/animations': 'npm:@angular/platform-browser/bundles/platform-browser-animations.umd.js',
|
||||
|
||||
// other libraries
|
||||
'rxjs': 'npm:rxjs',
|
||||
'ng2-translate': 'npm:ng2-translate',
|
||||
'alfresco-js-api': 'npm:alfresco-js-api/dist',
|
||||
'ng2-alfresco-core': 'npm:ng2-alfresco-core',
|
||||
'ng2-alfresco-upload': 'npm:ng2-alfresco-upload'
|
||||
},
|
||||
// packages tells the System loader how to load when no filename and/or no extension
|
||||
packages: {
|
||||
app: {
|
||||
main: './main.js',
|
||||
defaultExtension: 'js'
|
||||
},
|
||||
rxjs: {
|
||||
defaultExtension: 'js'
|
||||
},
|
||||
'ng2-translate': { defaultExtension: 'js' },
|
||||
'alfresco-js-api': { main: './alfresco-js-api.js', defaultExtension: 'js'},
|
||||
'ng2-alfresco-core': {main: './bundles/ng2-alfresco-core.js', defaultExtension: 'js'},
|
||||
'ng2-alfresco-upload': {main: './bundles/ng2-alfresco-upload.js', defaultExtension: 'js'}
|
||||
}
|
||||
});
|
||||
})(this);
|
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
@@ -16,6 +17,7 @@
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"removeComments": true,
|
||||
"declaration": true,
|
||||
"outDir": "./dist",
|
||||
"lib": [
|
||||
"es2015",
|
||||
"dom"
|
||||
@@ -23,7 +25,9 @@
|
||||
"suppressImplicitAnyIndexErrors": true
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
"demo",
|
||||
"node_modules",
|
||||
"dist"
|
||||
],
|
||||
"angularCompilerOptions": {
|
||||
"strictMetadataEmit": false,
|
||||
|
@@ -1,124 +1,118 @@
|
||||
{
|
||||
"rules": {
|
||||
"align": [
|
||||
true,
|
||||
"parameters",
|
||||
"arguments",
|
||||
"statements"
|
||||
],
|
||||
"ban": false,
|
||||
"class-name": true,
|
||||
"comment-format": [
|
||||
true,
|
||||
"check-space",
|
||||
"check-lowercase"
|
||||
],
|
||||
"curly": true,
|
||||
"eofline": true,
|
||||
"forin": true,
|
||||
"indent": [
|
||||
true,
|
||||
"spaces"
|
||||
],
|
||||
"interface-name": false,
|
||||
"jsdoc-format": true,
|
||||
"label-position": true,
|
||||
"label-undefined": true,
|
||||
"max-line-length": [
|
||||
true,
|
||||
180
|
||||
],
|
||||
"member-ordering": [
|
||||
true,
|
||||
"public-before-private",
|
||||
"static-before-instance",
|
||||
"variables-before-functions"
|
||||
],
|
||||
"no-any": false,
|
||||
"no-arg": true,
|
||||
"no-bitwise": false,
|
||||
"no-conditional-assignment": true,
|
||||
"no-consecutive-blank-lines": true,
|
||||
"no-console": [
|
||||
true,
|
||||
"debug",
|
||||
"info",
|
||||
"time",
|
||||
"timeEnd",
|
||||
"trace"
|
||||
],
|
||||
"no-construct": true,
|
||||
"no-constructor-vars": false,
|
||||
"no-debugger": true,
|
||||
"no-duplicate-key": true,
|
||||
"no-duplicate-variable": true,
|
||||
"no-empty": false,
|
||||
"no-eval": true,
|
||||
"no-inferrable-types": false,
|
||||
"no-internal-module": true,
|
||||
"no-require-imports": false,
|
||||
"no-shadowed-variable": true,
|
||||
"no-switch-case-fall-through": true,
|
||||
"no-trailing-whitespace": true,
|
||||
"no-unreachable": true,
|
||||
"no-unused-expression": true,
|
||||
"no-unused-variable": true,
|
||||
"no-use-before-declare": true,
|
||||
"no-var-keyword": true,
|
||||
"no-var-requires": true,
|
||||
"object-literal-sort-keys": false,
|
||||
"one-line": [
|
||||
true,
|
||||
"check-open-brace",
|
||||
"check-catch",
|
||||
"check-else",
|
||||
"check-whitespace"
|
||||
],
|
||||
"quotemark": [
|
||||
true,
|
||||
"single",
|
||||
"avoid-escape"
|
||||
],
|
||||
"radix": true,
|
||||
"semicolon": true,
|
||||
"switch-default": true,
|
||||
"trailing-comma": [
|
||||
true,
|
||||
{
|
||||
"multiline": "never",
|
||||
"singleline": "never"
|
||||
}
|
||||
],
|
||||
"triple-equals": [
|
||||
true,
|
||||
"allow-null-check"
|
||||
],
|
||||
"typedef": false,
|
||||
"typedef-whitespace": [
|
||||
true,
|
||||
{
|
||||
"call-signature": "nospace",
|
||||
"index-signature": "nospace",
|
||||
"parameter": "nospace",
|
||||
"property-declaration": "nospace",
|
||||
"variable-declaration": "nospace"
|
||||
}
|
||||
],
|
||||
"use-strict": false,
|
||||
"variable-name": [
|
||||
true,
|
||||
"check-format",
|
||||
"allow-leading-underscore",
|
||||
"ban-keywords"
|
||||
],
|
||||
"whitespace": [
|
||||
true,
|
||||
"check-branch",
|
||||
"check-operator",
|
||||
"check-separator",
|
||||
"check-type",
|
||||
"check-module",
|
||||
"check-decl"
|
||||
]
|
||||
}
|
||||
"rules": {
|
||||
"align": [
|
||||
true,
|
||||
"parameters",
|
||||
"statements"
|
||||
],
|
||||
"ban": false,
|
||||
"class-name": true,
|
||||
"comment-format": [
|
||||
true,
|
||||
"check-space"
|
||||
],
|
||||
"curly": true,
|
||||
"eofline": true,
|
||||
"forin": true,
|
||||
"indent": [
|
||||
true,
|
||||
"spaces"
|
||||
],
|
||||
"interface-name": false,
|
||||
"jsdoc-format": true,
|
||||
"label-position": true,
|
||||
"max-line-length": [
|
||||
true,
|
||||
180
|
||||
],
|
||||
"member-ordering": [
|
||||
true,
|
||||
"static-before-instance",
|
||||
"variables-before-functions"
|
||||
],
|
||||
"no-any": false,
|
||||
"no-arg": true,
|
||||
"no-bitwise": false,
|
||||
"no-conditional-assignment": true,
|
||||
"no-consecutive-blank-lines": true,
|
||||
"no-console": [
|
||||
true,
|
||||
"debug",
|
||||
"info",
|
||||
"time",
|
||||
"timeEnd",
|
||||
"trace"
|
||||
],
|
||||
"no-construct": true,
|
||||
"no-constructor-vars": false,
|
||||
"no-debugger": true,
|
||||
"no-duplicate-variable": true,
|
||||
"no-empty": false,
|
||||
"no-eval": true,
|
||||
"no-inferrable-types": false,
|
||||
"no-internal-module": true,
|
||||
"no-require-imports": false,
|
||||
"no-shadowed-variable": true,
|
||||
"no-switch-case-fall-through": true,
|
||||
"no-trailing-whitespace": true,
|
||||
"no-unused-expression": true,
|
||||
"no-unused-variable": true,
|
||||
"no-use-before-declare": true,
|
||||
"no-var-keyword": true,
|
||||
"no-var-requires": false,
|
||||
"object-literal-sort-keys": false,
|
||||
"one-line": [
|
||||
true,
|
||||
"check-open-brace",
|
||||
"check-catch",
|
||||
"check-else",
|
||||
"check-whitespace"
|
||||
],
|
||||
"quotemark": [
|
||||
true,
|
||||
"single",
|
||||
"avoid-escape"
|
||||
],
|
||||
"radix": true,
|
||||
"semicolon": true,
|
||||
"switch-default": true,
|
||||
"trailing-comma": [
|
||||
true,
|
||||
{
|
||||
"multiline": "never",
|
||||
"singleline": "never"
|
||||
}
|
||||
],
|
||||
"triple-equals": [
|
||||
true,
|
||||
"allow-null-check"
|
||||
],
|
||||
"typedef": false,
|
||||
"typedef-whitespace": [
|
||||
true,
|
||||
{
|
||||
"call-signature": "nospace",
|
||||
"index-signature": "nospace",
|
||||
"parameter": "nospace",
|
||||
"property-declaration": "nospace",
|
||||
"variable-declaration": "nospace"
|
||||
}
|
||||
],
|
||||
"use-strict": false,
|
||||
"variable-name": [
|
||||
true,
|
||||
"check-format",
|
||||
"allow-leading-underscore",
|
||||
"ban-keywords"
|
||||
],
|
||||
"whitespace": [
|
||||
true,
|
||||
"check-branch",
|
||||
"check-operator",
|
||||
"check-separator",
|
||||
"check-type",
|
||||
"check-module",
|
||||
"check-decl"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1 @@
|
||||
module.exports = require('./config/webpack.dev.js');
|
1
ng2-components/ng2-alfresco-upload/index.js.map
Normal file
1
ng2-components/ng2-alfresco-upload/index.js.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;;;;;;;;;;;;AAEH,sCAA8D;AAC9D,uDAA+C;AAE/C,0FAAsF;AACtF,sFAAmF;AACnF,oFAAiF;AACjF,oGAAgG;AAChG,gGAA4F;AAC5F,gEAA8D;AAkB9D,8DAAyD;AACzD,sEAAiE;AACjE,iEAA4D;AAC5D,mDAA8C;AAC9C,+DAA0D;AAC1D,oEAA+D;AAElD,QAAA,iBAAiB,GAAU;IACpC,iDAAsB;IACtB,oDAAuB;IACvB,+CAAqB;IACrB,8DAA4B;IAC5B,0DAA0B;CAC7B,CAAC;AAEW,QAAA,gBAAgB,GAAU;IACnC,8BAAa;CAChB,CAAC;AAgBF,IAAa,YAAY;IAAzB;IASA,CAAC;IARU,oBAAO,GAAd;QACI,MAAM,CAAC;YACH,QAAQ,EAAE,cAAY;YACtB,SAAS,EACF,wBAAgB,QACtB;SACJ,CAAC;IACN,CAAC;IACL,mBAAC;AAAD,CAAC,AATD,IASC;AATY,YAAY;IAdxB,eAAQ,CAAC;QACN,OAAO,EAAE;YACL,8BAAU;SACb;QACD,YAAY,EACL,yBAAiB,QACvB;QACD,SAAS,EACF,wBAAgB,QACtB;QACD,OAAO,EACA,yBAAiB,QACvB;KACJ,CAAC;GACW,YAAY,CASxB;AATY,oCAAY"}
|
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
import { NgModule, ModuleWithProviders } from '@angular/core';
|
||||
import { MdIconModule, MdProgressSpinnerModule, MdButtonModule } from '@angular/material';
|
||||
import { CoreModule } from 'ng2-alfresco-core';
|
||||
|
||||
import { UploadDragAreaComponent } from './src/components/upload-drag-area.component';
|
||||
@@ -25,22 +26,6 @@ import { FileUploadingDialogComponent } from './src/components/file-uploading-di
|
||||
import { FileUploadingListComponent } from './src/components/file-uploading-list.component';
|
||||
import { UploadService } from './src/services/upload.service';
|
||||
|
||||
/**
|
||||
* ng2-alfresco-upload, provide components to upload files to alfresco repository.
|
||||
*
|
||||
* Components provided:
|
||||
* - A button to upload files
|
||||
* <alfresco-upload-button [showDialogUpload]="boolean"
|
||||
* [showNotificationBar]="boolean"
|
||||
* [uploadFolders]="boolean"
|
||||
* [multipleFiles]="boolean"
|
||||
* [acceptedFilesType]="string">
|
||||
* </alfresco-upload-button>
|
||||
*
|
||||
* - Drag and drop area to upload files:
|
||||
* <alfresco-upload-drag-area [showDialogUpload]="boolean" ></alfresco-upload-drag-area>
|
||||
*/
|
||||
|
||||
export * from './src/components/upload-button.component';
|
||||
export * from './src/components/file-uploading-dialog.component';
|
||||
export * from './src/components/upload-drag-area.component';
|
||||
@@ -49,6 +34,7 @@ export * from './src/directives/file-draggable.directive';
|
||||
export * from './src/components/file-uploading-list.component';
|
||||
export * from './src/models/file.model';
|
||||
export * from './src/models/permissions.model';
|
||||
export * from './src/events/file.event';
|
||||
|
||||
export const UPLOAD_DIRECTIVES: any[] = [
|
||||
FileDraggableDirective,
|
||||
@@ -64,7 +50,10 @@ export const UPLOAD_PROVIDERS: any[] = [
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CoreModule
|
||||
CoreModule,
|
||||
MdIconModule,
|
||||
MdProgressSpinnerModule,
|
||||
MdButtonModule
|
||||
],
|
||||
declarations: [
|
||||
...UPLOAD_DIRECTIVES
|
||||
|
@@ -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'
|
||||
|
7132
ng2-components/ng2-alfresco-upload/package-lock.json
generated
Normal file
7132
ng2-components/ng2-alfresco-upload/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,16 +1,17 @@
|
||||
{
|
||||
"name": "ng2-alfresco-upload",
|
||||
"description": "Alfresco Angular2 Upload Component",
|
||||
"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-upload.js",
|
||||
"repository": {
|
||||
@@ -51,13 +52,12 @@
|
||||
"@angular/platform-browser": "~4.0.0",
|
||||
"@angular/platform-browser-dynamic": "~4.0.0",
|
||||
"@angular/router": "~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",
|
||||
"hammerjs": "2.0.8",
|
||||
"ng2-alfresco-core": "1.5.0",
|
||||
"ng2-translate": "5.0.0",
|
||||
"ng2-alfresco-core": "1.6.0",
|
||||
"@ngx-translate/core": "^7.0.0",
|
||||
"reflect-metadata": "0.1.10",
|
||||
"rxjs": "5.1.0",
|
||||
"systemjs": "0.19.27",
|
||||
@@ -75,6 +75,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",
|
||||
@@ -93,12 +95,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",
|
||||
@@ -109,7 +113,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",
|
||||
|
@@ -14,7 +14,7 @@
|
||||
}
|
||||
|
||||
:host .file-dialog {
|
||||
width: 700px;
|
||||
width: 550px;
|
||||
display: none;
|
||||
-webkit-box-shadow: 0 2px 8px 0 rgba(0, 0, 0, .2);
|
||||
box-shadow: -2px -1px 8px 3px rgba(0, 0, 0, .2);
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<i class="material-icons up" title="expand upload list">keyboard_arrow_up</i>
|
||||
</div>
|
||||
|
||||
<div class="close-button" (click)="toggleVisible()" (keyup.enter)="toggleVisible()" tabindex="0" title="close upload list">
|
||||
<div *ngIf="showCloseButton" id="button-close-upload-list" class="close-button" (click)="toggleVisible()" (keyup.enter)="toggleVisible()" tabindex="0" title="close upload list">
|
||||
<i class="material-icons">clear</i>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -16,12 +16,14 @@
|
||||
*/
|
||||
|
||||
import { DebugElement } from '@angular/core';
|
||||
import { MdProgressSpinnerModule } from '@angular/material';
|
||||
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
||||
import { CoreModule } from 'ng2-alfresco-core';
|
||||
import { FileUploadingDialogComponent } from './file-uploading-dialog.component';
|
||||
import { FileUploadingListComponent } from './file-uploading-list.component';
|
||||
import { UploadService } from '../services/upload.service';
|
||||
import { FileModel } from '../models/file.model';
|
||||
import { FileModel, FileUploadStatus } from '../models/file.model';
|
||||
import { FileUploadCompleteEvent, FileUploadEvent } from '../events/file.event';
|
||||
|
||||
describe('FileUploadingDialogComponent', () => {
|
||||
|
||||
@@ -35,7 +37,8 @@ describe('FileUploadingDialogComponent', () => {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
CoreModule.forRoot()
|
||||
CoreModule.forRoot(),
|
||||
MdProgressSpinnerModule
|
||||
],
|
||||
declarations: [
|
||||
FileUploadingDialogComponent,
|
||||
@@ -48,12 +51,7 @@ describe('FileUploadingDialogComponent', () => {
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
window['componentHandler'] = null;
|
||||
|
||||
let fileFake = {
|
||||
id: 'fake-id',
|
||||
name: 'fake-name'
|
||||
};
|
||||
const fileFake = new File([''], 'fake-name');
|
||||
file = new FileModel(fileFake);
|
||||
|
||||
fixture = TestBed.createComponent(FileUploadingDialogComponent);
|
||||
@@ -73,14 +71,14 @@ describe('FileUploadingDialogComponent', () => {
|
||||
});
|
||||
|
||||
it('should render completed upload 1 when an element is added to Observer', () => {
|
||||
uploadService.updateFileCounterStream(1);
|
||||
uploadService.fileUploadComplete.next(new FileUploadCompleteEvent(null, 1));
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(element.querySelector('#total-upload-completed').innerText).toEqual('1');
|
||||
});
|
||||
|
||||
it('should render dialog box with css class show when an element is added to Observer', () => {
|
||||
uploadService.addToQueue([<File> { name: 'file' }]);
|
||||
uploadService.addToQueue(new FileModel(<File> { name: 'file' }));
|
||||
component.filesUploadingList = [file];
|
||||
|
||||
fixture.detectChanges();
|
||||
@@ -112,4 +110,38 @@ describe('FileUploadingDialogComponent', () => {
|
||||
|
||||
expect(element.querySelector('.minimize-button').getAttribute('class')).toEqual('minimize-button active');
|
||||
});
|
||||
|
||||
it('should show the close button when the file upload is completed', async(() => {
|
||||
component.isDialogActive = true;
|
||||
uploadService.addToQueue(new FileModel(<File> { name: 'file' }));
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
let closeButton = element.querySelector('#button-close-upload-list');
|
||||
expect(closeButton).not.toBeNull();
|
||||
});
|
||||
|
||||
uploadService.fileUpload.next(new FileUploadCompleteEvent(file, 1, { status: FileUploadStatus.Complete }, 0));
|
||||
}));
|
||||
|
||||
it('should show the close button when the file upload is in error', async(() => {
|
||||
component.isDialogActive = true;
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
let closeButton = element.querySelector('#button-close-upload-list');
|
||||
expect(closeButton).not.toBeNull();
|
||||
});
|
||||
|
||||
uploadService.fileUpload.next(new FileUploadEvent(file, FileUploadStatus.Error));
|
||||
}));
|
||||
|
||||
it('should show the close button when the file upload is cancelled', async(() => {
|
||||
component.isDialogActive = true;
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
let closeButton = element.querySelector('#button-close-upload-list');
|
||||
expect(closeButton).not.toBeNull();
|
||||
});
|
||||
|
||||
uploadService.fileUpload.next(new FileUploadEvent(file, FileUploadStatus.Cancelled));
|
||||
}));
|
||||
});
|
||||
|
@@ -15,24 +15,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, Input, ChangeDetectorRef, OnInit, OnDestroy } from '@angular/core';
|
||||
import { FileModel } from '../models/file.model';
|
||||
import { Component, Input, ChangeDetectorRef, OnInit, OnDestroy, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { FileModel, FileUploadStatus } from '../models/file.model';
|
||||
import { AlfrescoTranslationService } from 'ng2-alfresco-core';
|
||||
import { UploadService } from '../services/upload.service';
|
||||
import { FileUploadCompleteEvent } from '../events/file.event';
|
||||
|
||||
/**
|
||||
* <file-uploading-dialog [filesUploadingList]="FileModel[]"></file-uploading-dialog>
|
||||
*
|
||||
* This component is a hideable and minimizable wich contains the list of the uploading
|
||||
* files contained in the filesUploadingList.
|
||||
*
|
||||
* @InputParam {FileModel[]} filesUploadingList - list of the uploading files .
|
||||
*
|
||||
*
|
||||
* @returns {FileUploadingDialogComponent} .
|
||||
*/
|
||||
@Component({
|
||||
selector: 'file-uploading-dialog',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
templateUrl: './file-uploading-dialog.component.html',
|
||||
styleUrls: ['./file-uploading-dialog.component.css']
|
||||
})
|
||||
@@ -48,34 +39,41 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy {
|
||||
|
||||
private listSubscription: any;
|
||||
private counterSubscription: any;
|
||||
private showCloseButton: boolean = false;
|
||||
|
||||
constructor(private cd: ChangeDetectorRef,
|
||||
translateService: AlfrescoTranslationService,
|
||||
private uploadService: UploadService) {
|
||||
if (translateService) {
|
||||
translateService.addTranslationFolder('ng2-alfresco-upload', 'node_modules/ng2-alfresco-upload/src');
|
||||
translateService.addTranslationFolder('ng2-alfresco-upload', 'assets/ng2-alfresco-upload');
|
||||
}
|
||||
cd.detach();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
if (this.uploadService.filesUpload$) {
|
||||
this.listSubscription = this.uploadService.filesUpload$.subscribe((fileList: FileModel[]) => {
|
||||
this.filesUploadingList = fileList;
|
||||
if (this.filesUploadingList.length > 0) {
|
||||
this.isDialogActive = true;
|
||||
this.cd.detectChanges();
|
||||
}
|
||||
});
|
||||
}
|
||||
if (this.uploadService.totalCompleted$) {
|
||||
this.counterSubscription = this.uploadService.totalCompleted$.subscribe((total: number) => {
|
||||
this.totalCompleted = total;
|
||||
if (this.totalCompleted > 1) {
|
||||
this.totalCompletedMsg = 'FILE_UPLOAD.MESSAGES.COMPLETED';
|
||||
}
|
||||
this.listSubscription = this.uploadService.queueChanged.subscribe((fileList: FileModel[]) => {
|
||||
this.filesUploadingList = fileList;
|
||||
if (this.filesUploadingList.length > 0) {
|
||||
this.isDialogActive = true;
|
||||
this.cd.detectChanges();
|
||||
});
|
||||
}
|
||||
}
|
||||
this.showCloseButton = false;
|
||||
});
|
||||
|
||||
this.counterSubscription = this.uploadService.fileUploadComplete.subscribe((event: FileUploadCompleteEvent) => {
|
||||
this.totalCompleted = event.totalComplete;
|
||||
if (this.totalCompleted > 1) {
|
||||
this.totalCompletedMsg = 'FILE_UPLOAD.MESSAGES.COMPLETED';
|
||||
}
|
||||
this.cd.detectChanges();
|
||||
});
|
||||
|
||||
this.uploadService.fileUpload.subscribe((event: FileUploadCompleteEvent) => {
|
||||
if (event.status !== FileUploadStatus.Progress) {
|
||||
this.isUploadProcessCompleted(event);
|
||||
}
|
||||
this.cd.detectChanges();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,6 +81,8 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy {
|
||||
*/
|
||||
toggleVisible(): void {
|
||||
this.isDialogActive = !this.isDialogActive;
|
||||
this.uploadService.clearQueue();
|
||||
this.cd.detectChanges();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,11 +90,31 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy {
|
||||
*/
|
||||
toggleMinimized(): void {
|
||||
this.isDialogMinimized = !this.isDialogMinimized;
|
||||
this.cd.detectChanges();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.listSubscription.unsubscribe();
|
||||
this.counterSubscription.unsubscribe();
|
||||
this.cd.detach();
|
||||
}
|
||||
|
||||
private isUploadProcessCompleted(event: FileUploadCompleteEvent) {
|
||||
if (this.isAllFileUploadEnded(event) && this.isUploadStateCompleted(event.status)) {
|
||||
this.showCloseDialogButton();
|
||||
} else if (event.status === FileUploadStatus.Error || event.status === FileUploadStatus.Cancelled) {
|
||||
this.showCloseDialogButton();
|
||||
}
|
||||
}
|
||||
|
||||
private showCloseDialogButton() {
|
||||
this.showCloseButton = true;
|
||||
}
|
||||
|
||||
private isAllFileUploadEnded(event: FileUploadCompleteEvent) {
|
||||
return event.totalComplete === this.uploadService.getQueue().length - event.totalAborted;
|
||||
}
|
||||
|
||||
private isUploadStateCompleted(state): boolean {
|
||||
return FileUploadStatus.Complete === state;
|
||||
}
|
||||
}
|
||||
|
@@ -3,8 +3,8 @@
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
.cursor {
|
||||
cursor: pointer;
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.body-dialog-header {
|
||||
@@ -42,62 +42,40 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:host .truncate {
|
||||
margin-left: 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
.cancel-upload-button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
:host .mdl-progress {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
@media (max-device-width: 360px) {
|
||||
.truncate {
|
||||
max-width: 50px;
|
||||
margin-left: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-device-width: 568px) {
|
||||
.truncate {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.mdl-progress {
|
||||
width: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 740px) {
|
||||
.truncate {
|
||||
max-width: 80px;
|
||||
}
|
||||
|
||||
.mdl-progress {
|
||||
max-width: 70px;
|
||||
}
|
||||
|
||||
.size-column {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 740px) {
|
||||
.truncate {
|
||||
width: 249px;
|
||||
}
|
||||
|
||||
.size-column {
|
||||
display: table-cell;
|
||||
}
|
||||
.file-progress-spinner {
|
||||
height: 24px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.no-width {
|
||||
width: 0%;
|
||||
.ellipsis-cell .cell-container {
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
/* visible content */
|
||||
.ellipsis-cell .cell-value {
|
||||
display: block;
|
||||
position: absolute;
|
||||
max-width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
line-height: 1em; /* for vertical align of text */
|
||||
}
|
||||
|
||||
|
||||
/* cell stretching content */
|
||||
.ellipsis-cell > div:after {
|
||||
content: attr(title);
|
||||
overflow: hidden;
|
||||
height: 0;
|
||||
display: block;
|
||||
}
|
||||
|
@@ -6,32 +6,37 @@
|
||||
</div>
|
||||
<table class="mdl-data-table mdl-js-data-table mdl-shadow--2dp">
|
||||
<tr>
|
||||
<th class="mdl-data-table__cell--non-numeric">{{'FILE_UPLOAD.FILE_INFO.NAME' | translate}}</th>
|
||||
<th class="mdl-data-table__cell--non-numeric">{{'FILE_UPLOAD.FILE_INFO.PROGRESS' | translate}}</th>
|
||||
<th class="mdl-data-table__cell--non-numeric mdl-cell--hide-phone size-column">{{'FILE_UPLOAD.FILE_INFO.SIZE' | translate}}</th>
|
||||
<th class="mdl-data-table__cell--non-numeric">{{'FILE_UPLOAD.FILE_INFO.ACTION' | translate}}</th>
|
||||
<th class="mdl-data-table__cell--non-numeric full-width">{{'ADF_FILE_UPLOAD.FILE_LIST.NAME' | translate}}</th>
|
||||
<th class="mdl-data-table__cell center">{{'ADF_FILE_UPLOAD.FILE_LIST.PROGRESS' | translate}}</th>
|
||||
<th class="mdl-data-table__cell mdl-cell--hide-phone size-column center">{{'ADF_FILE_UPLOAD.FILE_LIST.SIZE' | translate}}</th>
|
||||
<th class="mdl-data-table__cell center">{{'ADF_FILE_UPLOAD.FILE_LIST.ACTION' | translate}}</th>
|
||||
</tr>
|
||||
<tr *ngFor="let file of files" tabindex="0">
|
||||
<td class="mdl-data-table__cell--non-numeric" attr.data-automation-id="dialog_{{file.name}}">
|
||||
<div class="truncate">{{file.name}}</div>
|
||||
</td>
|
||||
<td class="mdl-data-table__cell--non-numeric">
|
||||
<div class="mdl-progress mdl-js-progress is-upgraded" id="{{file.id}}">
|
||||
<div class="progressbar bar bar1" attr.data-automation-id="dialog_progress_{{file.name}}" [style.width.%]="file.progress.percent"></div>
|
||||
<div class="bufferbar bar bar2" class="full-width"></div>
|
||||
<div class="auxbar bar bar3" class="no-width"></div>
|
||||
<td class="mdl-data-table__cell--non-numeric full-width ellipsis-cell" attr.data-automation-id="dialog_{{file.name}}">
|
||||
<div class="cell-container">
|
||||
<div class="cell-value" [title]="file.name">{{file.name}}</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="mdl-data-table__cell--non-numeric mdl-cell--hide-phone size-column" attr.data-automation-id="{{file.name}}_filesize">{{file.size}}</td>
|
||||
<td class="mdl-data-table__cell--non-numeric">
|
||||
<span *ngIf="file.done && !file.abort">
|
||||
<i data-automation-id="done_icon" class="material-icons action-icons">done</i>
|
||||
<td class="mdl-data-table__cell center">
|
||||
<md-icon *ngIf="file.status === FileUploadStatus.Error || file.status === FileUploadStatus.Aborted">error_outline</md-icon>
|
||||
<md-icon *ngIf="file.status === FileUploadStatus.Cancelled">block</md-icon>
|
||||
<ng-container *ngIf="file.status === FileUploadStatus.Progress">
|
||||
<md-progress-spinner
|
||||
class="file-progress-spinner"
|
||||
[mode]="'determinate'"
|
||||
[value]="file.progress.percent">
|
||||
</md-progress-spinner>
|
||||
</ng-container>
|
||||
</td>
|
||||
<td class="mdl-data-table__cell mdl-cell--hide-phone size-column center" attr.data-automation-id="{{file.name}}_filesize">
|
||||
{{ file.size | adfFileSize }}
|
||||
</td>
|
||||
<td class="mdl-data-table__cell center">
|
||||
<span *ngIf="file.status === FileUploadStatus.Complete">
|
||||
<md-icon>done</md-icon>
|
||||
</span>
|
||||
<span *ngIf="file.uploading" (click)="cancelFileUpload(file)" class="cursor" tabindex="0">
|
||||
<i data-automation-id="abort_cancel_upload" class="material-icons action-icons">remove_circle_outline</i>
|
||||
</span>
|
||||
<span *ngIf="file.abort">
|
||||
<i class="material-icons action-icons" data-automation-id="upload_stopped" tabindex="0">remove_circle</i>
|
||||
<span *ngIf="file.status === FileUploadStatus.Progress" (click)="cancelFileUpload(file)" tabindex="0" class="cancel-upload-button">
|
||||
<md-icon>remove_circle_outline</md-icon>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
@@ -16,18 +16,9 @@
|
||||
*/
|
||||
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { FileModel } from '../models/file.model';
|
||||
import { FileModel, FileUploadStatus } from '../models/file.model';
|
||||
import { UploadService } from '../services/upload.service';
|
||||
|
||||
/**
|
||||
* <alfresco-file-uploading-list [files]="files"></alfresco-file-uploading-list>
|
||||
*
|
||||
* This component show a list of the uploading files contained in the filesUploadingList.
|
||||
*
|
||||
* @InputParam {FileModel[]} filesUploadingList - list of the uploading files .
|
||||
*
|
||||
*
|
||||
* @returns {FileUploadingListComponent} .
|
||||
*/
|
||||
@Component({
|
||||
selector: 'alfresco-file-uploading-list',
|
||||
templateUrl: './file-uploading-list.component.html',
|
||||
@@ -35,9 +26,14 @@ import { FileModel } from '../models/file.model';
|
||||
})
|
||||
export class FileUploadingListComponent {
|
||||
|
||||
FileUploadStatus = FileUploadStatus;
|
||||
|
||||
@Input()
|
||||
files: FileModel[];
|
||||
|
||||
constructor(private uploadService: UploadService) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel file upload
|
||||
*
|
||||
@@ -46,9 +42,7 @@ export class FileUploadingListComponent {
|
||||
* @memberOf FileUploadingListComponent
|
||||
*/
|
||||
cancelFileUpload(file: FileModel): void {
|
||||
if (file) {
|
||||
file.emitAbort();
|
||||
}
|
||||
this.uploadService.cancelUpload(file);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,21 +52,20 @@ export class FileUploadingListComponent {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
this.files.forEach((uploadingFileModel: FileModel) => {
|
||||
uploadingFileModel.emitAbort();
|
||||
});
|
||||
this.uploadService.cancelUpload(...this.files);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if all the files are in state done or abort
|
||||
* @returns {boolean} - false if there is a file in progress
|
||||
* Check if all the files are not in the Progress state.
|
||||
* @returns {boolean} - false if there is at least one file in Progress
|
||||
*/
|
||||
isUploadCompleted(): boolean {
|
||||
let isPending = false;
|
||||
let isAllCompleted = true;
|
||||
|
||||
for (let i = 0; i < this.files.length && !isPending; i++) {
|
||||
let file = this.files[i];
|
||||
if (!file.done && !file.abort) {
|
||||
if (file.status === FileUploadStatus.Progress) {
|
||||
isPending = true;
|
||||
isAllCompleted = false;
|
||||
}
|
||||
|
@@ -15,10 +15,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DebugElement, SimpleChange } from '@angular/core';
|
||||
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
||||
import { UploadButtonComponent } from './upload-button.component';
|
||||
import { DebugElement, SimpleChange } from '@angular/core';
|
||||
import { CoreModule, AlfrescoTranslationService, NotificationService } from 'ng2-alfresco-core';
|
||||
import { CoreModule, AlfrescoTranslationService, AlfrescoContentService} from 'ng2-alfresco-core';
|
||||
import { TranslationMock } from '../assets/translation.service.mock';
|
||||
import { UploadService } from '../services/upload.service';
|
||||
import { Observable } from 'rxjs/Rx';
|
||||
@@ -33,27 +33,6 @@ describe('UploadButtonComponent', () => {
|
||||
target: {value: 'fake-name-1'}
|
||||
};
|
||||
|
||||
let fakeResolveRest = {
|
||||
entry: {
|
||||
isFile: false,
|
||||
isFolder: true,
|
||||
name: 'fake-folder1'
|
||||
}
|
||||
};
|
||||
let fakeResolvePromise = new Promise(function (resolve, reject) {
|
||||
resolve(fakeResolveRest);
|
||||
});
|
||||
|
||||
let fakeRejectRest = {
|
||||
response: {
|
||||
body: {
|
||||
error: {
|
||||
statusCode: 409
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let fakeFolderNodeWithoutPermission = {
|
||||
allowableOperations: [
|
||||
'update'
|
||||
@@ -73,15 +52,12 @@ describe('UploadButtonComponent', () => {
|
||||
nodeType: 'cm:folder'
|
||||
};
|
||||
|
||||
let fakeRejectPromise = new Promise(function (resolve, reject) {
|
||||
reject(fakeRejectRest);
|
||||
});
|
||||
|
||||
let component: UploadButtonComponent;
|
||||
let fixture: ComponentFixture<UploadButtonComponent>;
|
||||
let debug: DebugElement;
|
||||
let element: HTMLElement;
|
||||
let uploadService: UploadService;
|
||||
let contentService: AlfrescoContentService;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
@@ -93,7 +69,6 @@ describe('UploadButtonComponent', () => {
|
||||
],
|
||||
providers: [
|
||||
UploadService,
|
||||
NotificationService,
|
||||
{provide: AlfrescoTranslationService, useClass: TranslationMock}
|
||||
]
|
||||
}).compileComponents();
|
||||
@@ -104,6 +79,7 @@ describe('UploadButtonComponent', () => {
|
||||
|
||||
fixture = TestBed.createComponent(UploadButtonComponent);
|
||||
uploadService = TestBed.get(UploadService);
|
||||
contentService = TestBed.get(AlfrescoContentService);
|
||||
|
||||
debug = fixture.debugElement;
|
||||
element = fixture.nativeElement;
|
||||
@@ -141,7 +117,7 @@ describe('UploadButtonComponent', () => {
|
||||
component.rootFolderId = '-my-';
|
||||
component.disableWithNoPermission = false;
|
||||
|
||||
spyOn(uploadService, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithoutPermission));
|
||||
spyOn(component, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithoutPermission));
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
@@ -160,7 +136,7 @@ describe('UploadButtonComponent', () => {
|
||||
component.rootFolderId = '-my-';
|
||||
component.disableWithNoPermission = true;
|
||||
|
||||
spyOn(uploadService, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithoutPermission));
|
||||
spyOn(component, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithoutPermission));
|
||||
|
||||
component.onFilesAdded(fakeEvent);
|
||||
let compiled = fixture.debugElement.nativeElement;
|
||||
@@ -173,7 +149,7 @@ describe('UploadButtonComponent', () => {
|
||||
component.rootFolderId = '-my-';
|
||||
component.disableWithNoPermission = true;
|
||||
|
||||
spyOn(uploadService, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
||||
spyOn(component, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
||||
|
||||
component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) });
|
||||
component.onFilesAdded(fakeEvent);
|
||||
@@ -187,7 +163,7 @@ describe('UploadButtonComponent', () => {
|
||||
component.rootFolderId = '-my-';
|
||||
component.disableWithNoPermission = false;
|
||||
|
||||
spyOn(uploadService, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
||||
spyOn(component, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
||||
|
||||
component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) });
|
||||
component.onFilesAdded(fakeEvent);
|
||||
@@ -202,7 +178,7 @@ describe('UploadButtonComponent', () => {
|
||||
component.currentFolderPath = '/root-fake-/sites-fake/folder-fake';
|
||||
component.onSuccess = null;
|
||||
|
||||
spyOn(uploadService, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
||||
spyOn(component, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
||||
|
||||
component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) });
|
||||
uploadService.uploadFilesInTheQueue = jasmine.createSpy('uploadFilesInTheQueue');
|
||||
@@ -210,7 +186,7 @@ describe('UploadButtonComponent', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
component.onFilesAdded(fakeEvent);
|
||||
expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith('-root-', '/root-fake-/sites-fake/folder-fake', null);
|
||||
expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith(null);
|
||||
});
|
||||
|
||||
it('should call uploadFile with a custom root folder', () => {
|
||||
@@ -218,7 +194,7 @@ describe('UploadButtonComponent', () => {
|
||||
component.rootFolderId = '-my-';
|
||||
component.onSuccess = null;
|
||||
|
||||
spyOn(uploadService, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
||||
spyOn(component, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
||||
component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) });
|
||||
|
||||
uploadService.uploadFilesInTheQueue = jasmine.createSpy('uploadFilesInTheQueue');
|
||||
@@ -226,18 +202,19 @@ describe('UploadButtonComponent', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
component.onFilesAdded(fakeEvent);
|
||||
expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith('-my-', '/root-fake-/sites-fake/folder-fake', null);
|
||||
expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith(null);
|
||||
});
|
||||
|
||||
it('should create a folder and emit an File uploaded event', (done) => {
|
||||
component.rootFolderId = '-my-';
|
||||
component.currentFolderPath = '/fake-root-path';
|
||||
|
||||
spyOn(uploadService, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
||||
spyOn(uploadService, 'callApiCreateFolder').and.returnValue(fakeResolvePromise);
|
||||
spyOn(contentService, 'createFolder').and.returnValue(Observable.of(true));
|
||||
spyOn(component, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
||||
|
||||
component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) });
|
||||
fixture.detectChanges();
|
||||
|
||||
component.onSuccess.subscribe(e => {
|
||||
expect(e.value).toEqual('File uploaded');
|
||||
done();
|
||||
@@ -245,28 +222,12 @@ describe('UploadButtonComponent', () => {
|
||||
|
||||
spyOn(component, 'uploadFiles').and.callFake(() => {
|
||||
component.onSuccess.emit({
|
||||
value: 'File uploaded'
|
||||
}
|
||||
);
|
||||
value: 'File uploaded'
|
||||
});
|
||||
});
|
||||
component.onDirectoryAdded(fakeEvent);
|
||||
});
|
||||
|
||||
it('should emit an onError event when the folder already exist', (done) => {
|
||||
component.rootFolderId = '-my-';
|
||||
spyOn(uploadService, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
||||
spyOn(uploadService, 'callApiCreateFolder').and.returnValue(fakeRejectPromise);
|
||||
|
||||
component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) });
|
||||
|
||||
component.onError.subscribe(e => {
|
||||
expect(e.value).toEqual('FILE_UPLOAD.MESSAGES.FOLDER_ALREADY_EXIST');
|
||||
done();
|
||||
});
|
||||
|
||||
component.onDirectoryAdded(fakeEvent);
|
||||
});
|
||||
|
||||
it('should by default the title of the button get from the JSON file', () => {
|
||||
let compiled = fixture.debugElement.nativeElement;
|
||||
fixture.detectChanges();
|
||||
|
@@ -16,36 +16,13 @@
|
||||
*/
|
||||
|
||||
import { Component, ElementRef, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { Subject } from 'rxjs/Rx';
|
||||
import { AlfrescoTranslationService, LogService, NotificationService, AlfrescoSettingsService } from 'ng2-alfresco-core';
|
||||
import { Observable, Subject } from 'rxjs/Rx';
|
||||
import { AlfrescoApiService, AlfrescoContentService, AlfrescoTranslationService, LogService, NotificationService, FileUtils } from 'ng2-alfresco-core';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { UploadService } from '../services/upload.service';
|
||||
import { FileModel } from '../models/file.model';
|
||||
import { PermissionModel } from '../models/permissions.model';
|
||||
|
||||
declare let componentHandler: any;
|
||||
|
||||
const ERROR_FOLDER_ALREADY_EXIST = 409;
|
||||
|
||||
/**
|
||||
* <alfresco-upload-button [showNotificationBar]="boolean"
|
||||
* [uploadFolders]="boolean"
|
||||
* [multipleFiles]="boolean"
|
||||
* [acceptedFilesType]="string"
|
||||
* (onSuccess)="customMethod($event)">
|
||||
* </alfresco-upload-button>
|
||||
*
|
||||
* This component, provide a set of buttons to upload files to alfresco.
|
||||
*
|
||||
* @InputParam {boolean} [true] showNotificationBar - hide/show notification bar.
|
||||
* @InputParam {boolean} [false] versioning - true to indicate that a major version should be created
|
||||
* @InputParam {boolean} [false] uploadFolders - allow/disallow upload folders (only for chrome).
|
||||
* @InputParam {boolean} [false] multipleFiles - allow/disallow multiple files.
|
||||
* @InputParam {string} [*] acceptedFilesType - array of allowed file extensions.
|
||||
* @InputParam {boolean} [false] versioning - true to indicate that a major version should be created
|
||||
* @Output - onSuccess - The event is emitted when the file is uploaded
|
||||
*
|
||||
* @returns {UploadButtonComponent} .
|
||||
*/
|
||||
@Component({
|
||||
selector: 'alfresco-upload-button',
|
||||
templateUrl: './upload-button.component.html',
|
||||
@@ -53,11 +30,15 @@ const ERROR_FOLDER_ALREADY_EXIST = 409;
|
||||
})
|
||||
export class UploadButtonComponent implements OnInit, OnChanges {
|
||||
|
||||
private static DEFAULT_ROOT_ID: string = '-root-';
|
||||
|
||||
@Input()
|
||||
disabled: boolean = false;
|
||||
|
||||
/**
|
||||
* @deprecated Deprecated in 1.6.0, you can use UploadService events and NotificationService api instead.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof UploadButtonComponent
|
||||
*/
|
||||
@Input()
|
||||
showNotificationBar: boolean = true;
|
||||
|
||||
@@ -76,11 +57,17 @@ export class UploadButtonComponent implements OnInit, OnChanges {
|
||||
@Input()
|
||||
staticTitle: string;
|
||||
|
||||
/**
|
||||
* @deprecated Deprecated in 1.6.0, this property is not used for couple of releases already.
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof UploadDragAreaComponent
|
||||
*/
|
||||
@Input()
|
||||
currentFolderPath: string = '/';
|
||||
|
||||
@Input()
|
||||
rootFolderId: string = UploadButtonComponent.DEFAULT_ROOT_ID;
|
||||
rootFolderId: string = '-root-';
|
||||
|
||||
@Input()
|
||||
disableWithNoPermission: boolean = false;
|
||||
@@ -106,17 +93,14 @@ export class UploadButtonComponent implements OnInit, OnChanges {
|
||||
private translateService: AlfrescoTranslationService,
|
||||
private logService: LogService,
|
||||
private notificationService: NotificationService,
|
||||
private settingsService: AlfrescoSettingsService) {
|
||||
private apiService: AlfrescoApiService,
|
||||
private contentService: AlfrescoContentService) {
|
||||
if (translateService) {
|
||||
translateService.addTranslationFolder('ng2-alfresco-upload', 'node_modules/ng2-alfresco-upload/src');
|
||||
translateService.addTranslationFolder('ng2-alfresco-upload', 'assets/ng2-alfresco-upload');
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.settingsService.ecmHostSubject.subscribe((hostEcm: string) => {
|
||||
this.checkPermission();
|
||||
});
|
||||
|
||||
this.permissionValue.subscribe((permission: boolean) => {
|
||||
this.hasPermission = permission;
|
||||
});
|
||||
@@ -127,8 +111,6 @@ export class UploadButtonComponent implements OnInit, OnChanges {
|
||||
if (rootFolderId && rootFolderId.currentValue) {
|
||||
this.checkPermission();
|
||||
}
|
||||
let formFields = this.createFormFields();
|
||||
this.uploadService.setOptions(formFields, this.versioning);
|
||||
}
|
||||
|
||||
isButtonDisabled(): boolean {
|
||||
@@ -143,16 +125,11 @@ export class UploadButtonComponent implements OnInit, OnChanges {
|
||||
return !this.hasPermission && this.disableWithNoPermission ? true : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called when files are dropped in the drag area.
|
||||
*
|
||||
* @param {File[]} files - files dropped in the drag area.
|
||||
*/
|
||||
onFilesAdded($event: any): void {
|
||||
let files = $event.currentTarget.files;
|
||||
let files: File[] = FileUtils.toFileArray($event.currentTarget.files);
|
||||
|
||||
if (this.hasPermission) {
|
||||
this.uploadFiles(this.currentFolderPath, files);
|
||||
this.uploadFiles(files);
|
||||
} else {
|
||||
this.permissionEvent.emit(new PermissionModel({type: 'content', action: 'upload', permission: 'create'}));
|
||||
}
|
||||
@@ -160,38 +137,10 @@ export class UploadButtonComponent implements OnInit, OnChanges {
|
||||
$event.target.value = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called when a folder is dropped in the drag area.
|
||||
*
|
||||
* @param {File[]} files - files of a folder dropped in the drag area.
|
||||
*/
|
||||
onDirectoryAdded($event: any): void {
|
||||
let files = $event.currentTarget.files;
|
||||
if (this.hasPermission) {
|
||||
let hashMapDir = this.convertIntoHashMap(files);
|
||||
|
||||
hashMapDir.forEach((filesDir, directoryPath) => {
|
||||
let directoryName = this.getDirectoryName(directoryPath);
|
||||
let absolutePath = this.currentFolderPath + this.getDirectoryPath(directoryPath);
|
||||
|
||||
this.uploadService.createFolder(absolutePath, directoryName, this.rootFolderId)
|
||||
.subscribe(
|
||||
res => {
|
||||
let relativeDir = this.currentFolderPath + '/' + directoryPath;
|
||||
this.uploadFiles(relativeDir, filesDir);
|
||||
},
|
||||
error => {
|
||||
let errorMessagePlaceholder = this.getErrorMessage(error.response);
|
||||
if (errorMessagePlaceholder) {
|
||||
this.onError.emit({value: errorMessagePlaceholder});
|
||||
let errorMessage = this.formatString(errorMessagePlaceholder, [directoryName]);
|
||||
if (errorMessage) {
|
||||
this._showErrorNotificationBar(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
let files: File[] = FileUtils.toFileArray($event.currentTarget.files);
|
||||
this.uploadFiles(files);
|
||||
} else {
|
||||
this.permissionEvent.emit(new PermissionModel({type: 'content', action: 'upload', permission: 'create'}));
|
||||
}
|
||||
@@ -201,147 +150,70 @@ export class UploadButtonComponent implements OnInit, OnChanges {
|
||||
|
||||
/**
|
||||
* Upload a list of file in the specified path
|
||||
* @param path
|
||||
* @param files
|
||||
* @param path
|
||||
*/
|
||||
uploadFiles(path: string, files: any[]) {
|
||||
if (files.length) {
|
||||
let latestFilesAdded = this.uploadService.addToQueue(files);
|
||||
this.uploadService.uploadFilesInTheQueue(this.rootFolderId, path, this.onSuccess);
|
||||
uploadFiles(files: File[]): void {
|
||||
if (files.length > 0) {
|
||||
const latestFilesAdded = files.map(file => new FileModel(file, {
|
||||
newVersion: this.versioning,
|
||||
parentId: this.rootFolderId,
|
||||
path: (file.webkitRelativePath || '').replace(/\/[^\/]*$/, '')
|
||||
}));
|
||||
this.uploadService.addToQueue(...latestFilesAdded);
|
||||
this.uploadService.uploadFilesInTheQueue(this.onSuccess);
|
||||
if (this.showNotificationBar) {
|
||||
this._showUndoNotificationBar(latestFilesAdded);
|
||||
this.showUndoNotificationBar(latestFilesAdded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* It converts the array given as input into a map. The map is a key values pairs, where the key is the directory name and the value are
|
||||
* all the files that the directory contains.
|
||||
* @param files - array of files
|
||||
* @returns {Map}
|
||||
*/
|
||||
private convertIntoHashMap(files: any[]) {
|
||||
let directoryMap = new Map<string, Object[]>();
|
||||
for (let file of files) {
|
||||
let directory = this.getDirectoryPath(file.webkitRelativePath);
|
||||
let filesSomeDir = directoryMap.get(directory) || [];
|
||||
filesSomeDir.push(file);
|
||||
directoryMap.set(directory, filesSomeDir);
|
||||
}
|
||||
return directoryMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the directory path given as input and cut the last directory name
|
||||
* @param directory
|
||||
* @returns {string}
|
||||
*/
|
||||
private getDirectoryPath(directory: string) {
|
||||
let relativeDirPath = '';
|
||||
let dirPath = directory.split('/');
|
||||
if (dirPath.length > 1) {
|
||||
dirPath.pop();
|
||||
relativeDirPath = '/' + dirPath.join('/');
|
||||
}
|
||||
return relativeDirPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a directory path passed in input and return the first directory name
|
||||
* @param directory
|
||||
* @returns {string}
|
||||
*/
|
||||
private getDirectoryName(directory: string) {
|
||||
let dirPath = directory.split('/');
|
||||
if (dirPath.length > 1) {
|
||||
return dirPath.pop();
|
||||
} else {
|
||||
return dirPath[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show undo notification bar.
|
||||
*
|
||||
* @param {FileModel[]} latestFilesAdded - files in the upload queue enriched with status flag and xhr object.
|
||||
*/
|
||||
private _showUndoNotificationBar(latestFilesAdded: FileModel[]) {
|
||||
private showUndoNotificationBar(latestFilesAdded: FileModel[]): void {
|
||||
let messageTranslate: any, actionTranslate: any;
|
||||
messageTranslate = this.translateService.get('FILE_UPLOAD.MESSAGES.PROGRESS');
|
||||
actionTranslate = this.translateService.get('FILE_UPLOAD.ACTION.UNDO');
|
||||
|
||||
this.notificationService.openSnackMessageAction(messageTranslate.value, actionTranslate.value, 3000).afterDismissed().subscribe(() => {
|
||||
latestFilesAdded.forEach((uploadingFileModel: FileModel) => {
|
||||
uploadingFileModel.emitAbort();
|
||||
});
|
||||
this.notificationService.openSnackMessageAction(messageTranslate.value, actionTranslate.value, 3000).onAction().subscribe(() => {
|
||||
this.uploadService.cancelUpload(...latestFilesAdded);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrive the error message using the error status code
|
||||
* @param response - object that contain the HTTP response
|
||||
* @returns {string}
|
||||
*/
|
||||
private getErrorMessage(response: any): string {
|
||||
if (response.body && response.body.error.statusCode === ERROR_FOLDER_ALREADY_EXIST) {
|
||||
let errorMessage: any;
|
||||
errorMessage = this.translateService.get('FILE_UPLOAD.MESSAGES.FOLDER_ALREADY_EXIST');
|
||||
return errorMessage.value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the error inside Notification bar
|
||||
* @param Error message
|
||||
* @private
|
||||
*/
|
||||
private _showErrorNotificationBar(errorMessage: string) {
|
||||
this.notificationService.openSnackMessage(errorMessage, 3000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace a placeholder {0} in a message with the input keys
|
||||
* @param message - the message that conains the placeholder
|
||||
* @param keys - array of value
|
||||
* @returns {string} - The message without placeholder
|
||||
*/
|
||||
private formatString(message: string, keys: any []) {
|
||||
let i = keys.length;
|
||||
while (i--) {
|
||||
message = message.replace(new RegExp('\\{' + i + '\\}', 'gm'), keys[i]);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
private createFormFields(): any {
|
||||
return {
|
||||
formFields: {
|
||||
overwrite: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
checkPermission() {
|
||||
if (this.rootFolderId) {
|
||||
this.uploadService.getFolderNode(this.rootFolderId).subscribe(
|
||||
(res) => {
|
||||
this.permissionValue.next(this.hasCreatePermission(res));
|
||||
},
|
||||
(error) => {
|
||||
this.onError.emit(error);
|
||||
}
|
||||
this.getFolderNode(this.rootFolderId).subscribe(
|
||||
res => this.permissionValue.next(this.hasCreatePermission(res)),
|
||||
error => this.onError.emit(error)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: move to AlfrescoContentService
|
||||
getFolderNode(nodeId: string): Observable<MinimalNodeEntryEntity> {
|
||||
let opts: any = {
|
||||
includeSource: true,
|
||||
include: ['allowableOperations']
|
||||
};
|
||||
|
||||
return Observable.fromPromise(this.apiService.getInstance().nodes.getNodeInfo(nodeId, opts))
|
||||
.catch(err => this.handleError(err));
|
||||
}
|
||||
|
||||
private handleError(error: Response) {
|
||||
// in a real world app, we may send the error to some remote logging infrastructure
|
||||
// instead of just logging it to the console
|
||||
this.logService.error(error);
|
||||
return Observable.throw(error || 'Server error');
|
||||
}
|
||||
|
||||
private hasCreatePermission(node: any): boolean {
|
||||
if (this.hasPermissions(node)) {
|
||||
if (node && node.allowableOperations) {
|
||||
return node.allowableOperations.find(permision => permision === 'create') ? true : false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private hasPermissions(node: any): boolean {
|
||||
return node && node.allowableOperations ? true : false;
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
<div file-draggable id="UploadBorder" class="upload-border"
|
||||
<div [file-draggable]="enabled" id="UploadBorder" class="upload-border"
|
||||
(onFilesDropped)="onFilesDropped($event)"
|
||||
(onFilesEntityDropped)="onFilesEntityDropped($event)"
|
||||
(onFolderEntityDropped)="onFolderEntityDropped($event)"
|
||||
|
@@ -16,12 +16,49 @@
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
||||
import { EventEmitter, DebugElement } from '@angular/core';
|
||||
import { AlfrescoTranslationService, CoreModule, LogService, LogServiceMock, NotificationService } from 'ng2-alfresco-core';
|
||||
import { DebugElement } from '@angular/core';
|
||||
import { AlfrescoTranslationService, CoreModule, LogService, LogServiceMock } from 'ng2-alfresco-core';
|
||||
|
||||
import { UploadDragAreaComponent } from './upload-drag-area.component';
|
||||
import { FileDraggableDirective } from '../directives/file-draggable.directive';
|
||||
import { TranslationMock } from '../assets/translation.service.mock';
|
||||
import { UploadService } from '../services/upload.service';
|
||||
import { FileModel } from '../models/file.model';
|
||||
|
||||
let fakeShareDataRow = {
|
||||
obj: {
|
||||
entry: {
|
||||
createdAt: '2017-06-04T04:32:15.597Z',
|
||||
path: {
|
||||
name: '/Company Home/User Homes/Test',
|
||||
isComplete: true,
|
||||
elements: [
|
||||
{
|
||||
id: '94acfc73-7014-4475-9bd9-93a2162f0f8c',
|
||||
name: 'Company Home'
|
||||
},
|
||||
{
|
||||
id: '55052317-7e59-4058-8e07-769f41e615e1',
|
||||
name: 'User Homes'
|
||||
},
|
||||
{
|
||||
id: '70e1cc6a-6918-468a-b84a-1048093b06fd',
|
||||
name: 'Test'
|
||||
}
|
||||
]
|
||||
},
|
||||
isFolder: true,
|
||||
name: 'pippo',
|
||||
id: '7462d28e-bd43-4b91-9e7b-0d71598680ac',
|
||||
nodeType: 'cm:folder',
|
||||
allowableOperations: [
|
||||
'delete',
|
||||
'update',
|
||||
'create'
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
describe('UploadDragAreaComponent', () => {
|
||||
|
||||
@@ -38,11 +75,11 @@ describe('UploadDragAreaComponent', () => {
|
||||
CoreModule.forRoot()
|
||||
],
|
||||
declarations: [
|
||||
FileDraggableDirective,
|
||||
UploadDragAreaComponent
|
||||
],
|
||||
providers: [
|
||||
UploadService,
|
||||
NotificationService,
|
||||
{ provide: AlfrescoTranslationService, useClass: TranslationMock },
|
||||
{ provide: LogService, useClass: LogServiceMock }
|
||||
]
|
||||
@@ -65,20 +102,22 @@ describe('UploadDragAreaComponent', () => {
|
||||
TestBed.resetTestingModule();
|
||||
});
|
||||
|
||||
it('should upload the list of files dropped', () => {
|
||||
it('should upload the list of files dropped', (done) => {
|
||||
component.currentFolderPath = '/root-fake-/sites-fake/folder-fake';
|
||||
component.onSuccess = null;
|
||||
component.showNotificationBar = false;
|
||||
uploadService.addToQueue = jasmine.createSpy('addToQueue');
|
||||
uploadService.uploadFilesInTheQueue = jasmine.createSpy('uploadFilesInTheQueue');
|
||||
|
||||
fixture.detectChanges();
|
||||
let fileFake = <File> {name: 'fake-name-1', size: 10, webkitRelativePath: 'fake-folder1/fake-name-1.json'};
|
||||
let filesList = [fileFake];
|
||||
const file = <File> {name: 'fake-name-1', size: 10, webkitRelativePath: 'fake-folder1/fake-name-1.json'};
|
||||
let filesList = [file];
|
||||
|
||||
spyOn(uploadService, 'addToQueue').and.callFake((f: FileModel) => {
|
||||
expect(f.file).toBe(file);
|
||||
done();
|
||||
});
|
||||
|
||||
component.onFilesDropped(filesList);
|
||||
expect(uploadService.addToQueue).toHaveBeenCalledWith(filesList);
|
||||
expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith('-root-', '/root-fake-/sites-fake/folder-fake', null);
|
||||
});
|
||||
|
||||
it('should show the loading messages in the notification bar when the files are dropped', () => {
|
||||
@@ -93,7 +132,7 @@ describe('UploadDragAreaComponent', () => {
|
||||
let filesList = [fileFake];
|
||||
|
||||
component.onFilesDropped(filesList);
|
||||
expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith('-root-', '/root-fake-/sites-fake/folder-fake', null);
|
||||
expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith(null);
|
||||
expect(component.showUndoNotificationBar).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -116,8 +155,7 @@ describe('UploadDragAreaComponent', () => {
|
||||
};
|
||||
|
||||
component.onFilesEntityDropped(itemEntity);
|
||||
expect(uploadService.uploadFilesInTheQueue)
|
||||
.toHaveBeenCalledWith('-root-', '/root-fake-/sites-fake/document-library-fake/folder-fake/', null);
|
||||
expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith(null);
|
||||
});
|
||||
|
||||
it('should upload a file with a custom root folder ID when dropped', () => {
|
||||
@@ -140,50 +178,15 @@ describe('UploadDragAreaComponent', () => {
|
||||
};
|
||||
|
||||
component.onFilesEntityDropped(itemEntity);
|
||||
expect(uploadService.uploadFilesInTheQueue)
|
||||
.toHaveBeenCalledWith('-my-', '/root-fake-/sites-fake/document-library-fake/folder-fake/', null);
|
||||
expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith(null);
|
||||
});
|
||||
|
||||
xit('should throws an exception and show it in the notification bar when the folder already exist', done => {
|
||||
component.currentFolderPath = '/root-fake-/sites-fake/folder-fake';
|
||||
component.showNotificationBar = true;
|
||||
|
||||
fixture.detectChanges();
|
||||
let fakeRest = {
|
||||
response: {
|
||||
body: {
|
||||
error: {
|
||||
statusCode: 409
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
let fakePromise = new Promise(function (resolve, reject) {
|
||||
reject(fakeRest);
|
||||
});
|
||||
spyOn(uploadService, 'callApiCreateFolder').and.returnValue(fakePromise);
|
||||
spyOn(component, 'showErrorNotificationBar').and.callFake( () => {
|
||||
expect(component.showErrorNotificationBar).toHaveBeenCalledWith('FILE_UPLOAD.MESSAGES.FOLDER_ALREADY_EXIST');
|
||||
done();
|
||||
});
|
||||
|
||||
let folderEntry = {
|
||||
fullPath: '/folder-duplicate-fake',
|
||||
isDirectory: true,
|
||||
isFile: false,
|
||||
name: 'folder-duplicate-fake'
|
||||
};
|
||||
|
||||
component.onFolderEntityDropped(folderEntry);
|
||||
});
|
||||
|
||||
it('should create a folder and call onFilesEntityDropped with the file inside the folder', done => {
|
||||
it('should upload a file when user has create permission on target folder', async(() => {
|
||||
component.currentFolderPath = '/root-fake-/sites-fake/document-library-fake';
|
||||
component.onSuccess = new EventEmitter();
|
||||
component.rootFolderId = '-my-';
|
||||
component.enabled = false;
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
let itemEntity = {
|
||||
let fakeItem = {
|
||||
fullPath: '/folder-fake/file-fake.png',
|
||||
isDirectory: false,
|
||||
isFile: true,
|
||||
@@ -194,41 +197,20 @@ describe('UploadDragAreaComponent', () => {
|
||||
}
|
||||
};
|
||||
|
||||
let fakeRest = {
|
||||
entry: {
|
||||
isFile: false,
|
||||
isFolder: true,
|
||||
name: 'folder-fake'
|
||||
fixture.detectChanges();
|
||||
spyOn(uploadService, 'uploadFilesInTheQueue').and.returnValue(Promise.resolve(fakeItem));
|
||||
component.onSuccess.subscribe((val) => {
|
||||
expect(val).not.toBeNull();
|
||||
});
|
||||
|
||||
let fakeCustomEvent: CustomEvent = new CustomEvent('CustomEvent', {
|
||||
detail: {
|
||||
data: fakeShareDataRow,
|
||||
files: [fakeItem]
|
||||
}
|
||||
};
|
||||
let fakePromise = new Promise(function (resolve, reject) {
|
||||
resolve(fakeRest);
|
||||
});
|
||||
spyOn(uploadService, 'callApiCreateFolder').and.returnValue(fakePromise);
|
||||
spyOn(component, 'onFilesEntityDropped').and.callFake( () => {
|
||||
expect(component.onFilesEntityDropped).toHaveBeenCalledWith(itemEntity);
|
||||
});
|
||||
|
||||
spyOn(component, 'showUndoNotificationBar').and.callFake( () => {
|
||||
expect(component.showUndoNotificationBar).toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
component.onUploadFiles(fakeCustomEvent);
|
||||
}));
|
||||
|
||||
let folderEntry = {
|
||||
fullPath: '/folder-fake',
|
||||
isDirectory: true,
|
||||
isFile: false,
|
||||
name: 'folder-fake',
|
||||
createReader: () => {
|
||||
return {
|
||||
readEntries: (callback) => {
|
||||
let entries = [itemEntity, itemEntity];
|
||||
callback(entries);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
component.onFolderEntityDropped(folderEntry);
|
||||
});
|
||||
});
|
||||
|
@@ -16,23 +16,10 @@
|
||||
*/
|
||||
|
||||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { AlfrescoTranslationService, LogService, NotificationService } from 'ng2-alfresco-core';
|
||||
import { AlfrescoTranslationService, NotificationService, FileUtils, FileInfo } from 'ng2-alfresco-core';
|
||||
import { UploadService } from '../services/upload.service';
|
||||
import { FileModel } from '../models/file.model';
|
||||
|
||||
declare let componentHandler: any;
|
||||
|
||||
const ERROR_FOLDER_ALREADY_EXIST = 409;
|
||||
|
||||
/**
|
||||
* <alfresco-upload-drag-area (onSuccess)="customMethod($event)></alfresco-upload-drag-area>
|
||||
*
|
||||
* This component, provide a drag and drop are to upload files to alfresco.
|
||||
*
|
||||
* @Output - onSuccess - The event is emitted when the file is uploaded
|
||||
*
|
||||
* @returns {UploadDragAreaComponent} .
|
||||
*/
|
||||
@Component({
|
||||
selector: 'alfresco-upload-drag-area',
|
||||
templateUrl: './upload-drag-area.component.html',
|
||||
@@ -40,52 +27,65 @@ const ERROR_FOLDER_ALREADY_EXIST = 409;
|
||||
})
|
||||
export class UploadDragAreaComponent {
|
||||
|
||||
private static DEFAULT_ROOT_ID: string = '-root-';
|
||||
@Input()
|
||||
enabled: boolean = true;
|
||||
|
||||
/**
|
||||
* @deprecated Deprecated in 1.6.0, you can use UploadService events and NotificationService api instead.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof UploadButtonComponent
|
||||
*/
|
||||
@Input()
|
||||
showNotificationBar: boolean = true;
|
||||
|
||||
@Input()
|
||||
versioning: boolean = false;
|
||||
|
||||
/**
|
||||
* @deprecated Deprecated in 1.6.0, this property is not used for couple of releases already. Use rootFolderId instead.
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof UploadDragAreaComponent
|
||||
*/
|
||||
@Input()
|
||||
currentFolderPath: string = '/';
|
||||
|
||||
@Input()
|
||||
rootFolderId: string = UploadDragAreaComponent.DEFAULT_ROOT_ID;
|
||||
rootFolderId: string = '-root-';
|
||||
|
||||
@Output()
|
||||
onSuccess = new EventEmitter();
|
||||
|
||||
constructor(private uploadService: UploadService,
|
||||
private translateService: AlfrescoTranslationService,
|
||||
private logService: LogService,
|
||||
private notificationService: NotificationService) {
|
||||
if (translateService) {
|
||||
translateService.addTranslationFolder('ng2-alfresco-upload', 'node_modules/ng2-alfresco-upload/src');
|
||||
translateService.addTranslationFolder('ng2-alfresco-upload', 'assets/ng2-alfresco-upload');
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes) {
|
||||
let formFields = this.createFormFields();
|
||||
this.uploadService.setOptions(formFields, this.versioning);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles 'upload-files' events raised by child components.
|
||||
* @param e DOM event
|
||||
* @param event DOM event
|
||||
*/
|
||||
onUploadFiles(e: CustomEvent) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
let files = e.detail.files;
|
||||
if (files && files.length > 0) {
|
||||
if (e.detail.data.obj.entry.isFolder) {
|
||||
let id = e.detail.data.obj.entry.id;
|
||||
this.onFilesDropped(files, id, '/');
|
||||
} else {
|
||||
this.onFilesDropped(files);
|
||||
onUploadFiles(event: CustomEvent) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
let isAllowed: boolean = this.isAllowed(event.detail.data.obj.entry);
|
||||
if (isAllowed) {
|
||||
let files: FileInfo[] = event.detail.files;
|
||||
if (files && files.length > 0) {
|
||||
let parentId = this.rootFolderId;
|
||||
if (event.detail.data && event.detail.data.obj.entry.isFolder) {
|
||||
parentId = event.detail.data.obj.entry.id || this.rootFolderId;
|
||||
}
|
||||
const fileModels = files.map(fileInfo => new FileModel(fileInfo.file, {
|
||||
newVersion: this.versioning,
|
||||
path: fileInfo.relativeFolder,
|
||||
parentId: parentId
|
||||
}));
|
||||
this.uploadFiles(fileModels, isAllowed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,10 +95,15 @@ export class UploadDragAreaComponent {
|
||||
*
|
||||
* @param {File[]} files - files dropped in the drag area.
|
||||
*/
|
||||
onFilesDropped(files: File[], rootId?: string, directory?: string): void {
|
||||
if (files.length) {
|
||||
this.uploadService.addToQueue(files);
|
||||
this.uploadService.uploadFilesInTheQueue(rootId || this.rootFolderId, directory || this.currentFolderPath, this.onSuccess);
|
||||
onFilesDropped(files: File[]): void {
|
||||
if (this.enabled && files.length) {
|
||||
const fileModels = files.map(file => new FileModel(file, {
|
||||
newVersion: this.versioning,
|
||||
path: '/',
|
||||
parentId: this.rootFolderId
|
||||
}));
|
||||
this.uploadService.addToQueue(...fileModels);
|
||||
this.uploadService.uploadFilesInTheQueue(this.onSuccess);
|
||||
let latestFilesAdded = this.uploadService.getQueue();
|
||||
if (this.showNotificationBar) {
|
||||
this.showUndoNotificationBar(latestFilesAdded);
|
||||
@@ -111,12 +116,17 @@ export class UploadDragAreaComponent {
|
||||
* @param item - FileEntity
|
||||
*/
|
||||
onFilesEntityDropped(item: any): void {
|
||||
item.file((file: any) => {
|
||||
this.uploadService.addToQueue([file]);
|
||||
let path = item.fullPath.replace(item.name, '');
|
||||
let filePath = this.currentFolderPath + path;
|
||||
this.uploadService.uploadFilesInTheQueue(this.rootFolderId, filePath, this.onSuccess);
|
||||
});
|
||||
if (this.enabled) {
|
||||
item.file((file: File) => {
|
||||
const fileModel = new FileModel(file, {
|
||||
newVersion: this.versioning,
|
||||
parentId: this.rootFolderId,
|
||||
path: item.fullPath.replace(item.name, '')
|
||||
});
|
||||
this.uploadService.addToQueue(fileModel);
|
||||
this.uploadService.uploadFilesInTheQueue(this.onSuccess);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,53 +134,23 @@ export class UploadDragAreaComponent {
|
||||
* @param folder - name of the dropped folder
|
||||
*/
|
||||
onFolderEntityDropped(folder: any): void {
|
||||
if (folder.isDirectory) {
|
||||
let relativePath = folder.fullPath.replace(folder.name, '');
|
||||
relativePath = this.currentFolderPath + relativePath;
|
||||
|
||||
this.uploadService.createFolder(relativePath, folder.name, this.rootFolderId)
|
||||
.subscribe(
|
||||
message => {
|
||||
this.onSuccess.emit({
|
||||
value: 'Created folder'
|
||||
});
|
||||
let dirReader = folder.createReader();
|
||||
dirReader.readEntries((entries: any) => {
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
this._traverseFileTree(entries[i]);
|
||||
}
|
||||
if (this.showNotificationBar) {
|
||||
let latestFilesAdded = this.uploadService.getQueue();
|
||||
this.showUndoNotificationBar(latestFilesAdded);
|
||||
}
|
||||
});
|
||||
},
|
||||
error => {
|
||||
let errorMessagePlaceholder = this.getErrorMessage(error.response);
|
||||
let errorMessage = this.formatString(errorMessagePlaceholder, [folder.name]);
|
||||
if (this.showNotificationBar) {
|
||||
this.showErrorNotificationBar(errorMessage);
|
||||
} else {
|
||||
this.logService.error(errorMessage);
|
||||
}
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Travers all the files and folders, and create it on the alfresco.
|
||||
*
|
||||
* @param {Object} item - can contains files or folders.
|
||||
*/
|
||||
private _traverseFileTree(item: any): void {
|
||||
if (item.isFile) {
|
||||
this.onFilesEntityDropped(item);
|
||||
} else {
|
||||
if (item.isDirectory) {
|
||||
this.onFolderEntityDropped(item);
|
||||
}
|
||||
if (this.enabled && folder.isDirectory) {
|
||||
FileUtils.flattern(folder).then(entries => {
|
||||
let files = entries.map(entry => {
|
||||
return new FileModel(entry.file, {
|
||||
newVersion: this.versioning,
|
||||
parentId: this.rootFolderId,
|
||||
path: entry.relativeFolder
|
||||
});
|
||||
});
|
||||
this.uploadService.addToQueue(...files);
|
||||
/* @deprecated in 1.6.0 */
|
||||
if (this.showNotificationBar) {
|
||||
let latestFilesAdded = this.uploadService.getQueue();
|
||||
this.showUndoNotificationBar(latestFilesAdded);
|
||||
}
|
||||
this.uploadService.uploadFilesInTheQueue(this.onSuccess);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,10 +164,8 @@ export class UploadDragAreaComponent {
|
||||
messageTranslate = this.translateService.get('FILE_UPLOAD.MESSAGES.PROGRESS');
|
||||
actionTranslate = this.translateService.get('FILE_UPLOAD.ACTION.UNDO');
|
||||
|
||||
this.notificationService.openSnackMessageAction(messageTranslate.value, actionTranslate.value, 3000).afterDismissed().subscribe(() => {
|
||||
latestFilesAdded.forEach((uploadingFileModel: FileModel) => {
|
||||
uploadingFileModel.emitAbort();
|
||||
});
|
||||
this.notificationService.openSnackMessageAction(messageTranslate.value, actionTranslate.value, 3000).onAction().subscribe(() => {
|
||||
this.uploadService.cancelUpload(...latestFilesAdded);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -200,40 +178,27 @@ export class UploadDragAreaComponent {
|
||||
this.notificationService.openSnackMessage(errorMessage, 3000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrive the error message using the error status code
|
||||
* @param response - object that contain the HTTP response
|
||||
* @returns {string}
|
||||
*/
|
||||
private getErrorMessage(response: any): string {
|
||||
if (response.body.error.statusCode === ERROR_FOLDER_ALREADY_EXIST) {
|
||||
let errorMessage: any;
|
||||
errorMessage = this.translateService.get('FILE_UPLOAD.MESSAGES.FOLDER_ALREADY_EXIST');
|
||||
return errorMessage.value;
|
||||
private uploadFiles(files: FileModel[], isAllowed: boolean): void {
|
||||
if (isAllowed && files.length) {
|
||||
this.uploadService.addToQueue(...files);
|
||||
this.uploadService.uploadFilesInTheQueue(this.onSuccess);
|
||||
let latestFilesAdded = this.uploadService.getQueue();
|
||||
if (this.showNotificationBar) {
|
||||
this.showUndoNotificationBar(latestFilesAdded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace a placeholder {0} in a message with the input keys
|
||||
* @param message - the message that conains the placeholder
|
||||
* @param keys - array of value
|
||||
* @returns {string} - The message without placeholder
|
||||
*/
|
||||
private formatString(message: string, keys: any []) {
|
||||
if (message) {
|
||||
let i = keys.length;
|
||||
while (i--) {
|
||||
message = message.replace(new RegExp('\\{' + i + '\\}', 'gm'), keys[i]);
|
||||
}
|
||||
private hasCreatePermission(node: any): boolean {
|
||||
let isPermitted = false;
|
||||
if (node && node['allowableOperations']) {
|
||||
let permFound = node['allowableOperations'].find(element => element === 'create');
|
||||
isPermitted = permFound ? true : false;
|
||||
}
|
||||
return message;
|
||||
return isPermitted;
|
||||
}
|
||||
|
||||
private createFormFields(): any {
|
||||
return {
|
||||
formFields: {
|
||||
overwrite: true
|
||||
}
|
||||
};
|
||||
private isAllowed(node: any) {
|
||||
return this.enabled || this.hasCreatePermission(node);
|
||||
}
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ElementRef } from '@angular/core';
|
||||
import { FileDraggableDirective } from '../directives/file-draggable.directive';
|
||||
|
||||
describe('FileDraggableDirective', () => {
|
||||
@@ -22,7 +23,22 @@ describe('FileDraggableDirective', () => {
|
||||
let component: FileDraggableDirective;
|
||||
|
||||
beforeEach( () => {
|
||||
component = new FileDraggableDirective(null, null);
|
||||
let el = new ElementRef(null);
|
||||
component = new FileDraggableDirective(el, null);
|
||||
});
|
||||
|
||||
it('should always be enabled by default', () => {
|
||||
expect(component.enabled).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not allow drad and drop when disabled', () => {
|
||||
component.enabled = false;
|
||||
let event = new CustomEvent('custom-event');
|
||||
spyOn(event, 'preventDefault').and.stub();
|
||||
component.onDropFiles(event);
|
||||
component.onDragEnter(event);
|
||||
component.onDragLeave(event);
|
||||
expect(event.preventDefault).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
/*
|
||||
|
@@ -15,19 +15,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Directive, EventEmitter, Output, OnInit, OnDestroy, ElementRef, NgZone } from '@angular/core';
|
||||
import { Directive, EventEmitter, Input, Output, OnInit, OnDestroy, ElementRef, NgZone } from '@angular/core';
|
||||
import { FileUtils } from 'ng2-alfresco-core';
|
||||
|
||||
/**
|
||||
* [file-draggable]
|
||||
*
|
||||
* This directive, provide a drag and drop area for files and folders.
|
||||
*
|
||||
* @OutputEvent {EventEmitter} onFilesDropped(File)- event fired fot each file dropped
|
||||
* in the drag and drop area.
|
||||
*
|
||||
*
|
||||
* @returns {FileDraggableDirective} .
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[file-draggable]'
|
||||
})
|
||||
@@ -35,8 +25,11 @@ export class FileDraggableDirective implements OnInit, OnDestroy {
|
||||
|
||||
files: File [];
|
||||
|
||||
@Input('file-draggable')
|
||||
enabled: boolean = true;
|
||||
|
||||
@Output()
|
||||
onFilesDropped: EventEmitter<any> = new EventEmitter();
|
||||
onFilesDropped: EventEmitter<File[]> = new EventEmitter<File[]>();
|
||||
|
||||
@Output()
|
||||
onFilesEntityDropped: EventEmitter<any> = new EventEmitter();
|
||||
@@ -72,7 +65,7 @@ export class FileDraggableDirective implements OnInit, OnDestroy {
|
||||
* @param event DOM event.
|
||||
*/
|
||||
onDropFiles(event: any): void {
|
||||
if (!event.defaultPrevented) {
|
||||
if (this.enabled && !event.defaultPrevented) {
|
||||
this.preventDefault(event);
|
||||
|
||||
let items = event.dataTransfer.items;
|
||||
@@ -81,16 +74,20 @@ export class FileDraggableDirective implements OnInit, OnDestroy {
|
||||
if (typeof items[i].webkitGetAsEntry !== 'undefined') {
|
||||
let item = items[i].webkitGetAsEntry();
|
||||
if (item) {
|
||||
this.traverseFileTree(item);
|
||||
if (item.isFile) {
|
||||
this.onFilesEntityDropped.emit(item);
|
||||
} else if (item.isDirectory) {
|
||||
this.onFolderEntityDropped.emit(item);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let files = event.dataTransfer.files;
|
||||
let files = FileUtils.toFileArray(event.dataTransfer.files);
|
||||
this.onFilesDropped.emit(files);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// safari or FF
|
||||
let files = event.dataTransfer.files;
|
||||
let files = FileUtils.toFileArray(event.dataTransfer.files);
|
||||
this.onFilesDropped.emit(files);
|
||||
}
|
||||
|
||||
@@ -98,29 +95,13 @@ export class FileDraggableDirective implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Travers all the files and folders, and emit an event for each file or directory.
|
||||
*
|
||||
* @param {Object} item - can contains files or folders.
|
||||
*/
|
||||
private traverseFileTree(item: any): void {
|
||||
if (item.isFile) {
|
||||
let self = this;
|
||||
self.onFilesEntityDropped.emit(item);
|
||||
} else {
|
||||
if (item.isDirectory) {
|
||||
this.onFolderEntityDropped.emit(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the style of the drag area when a file drag in.
|
||||
*
|
||||
* @param {event} event - DOM event.
|
||||
*/
|
||||
onDragEnter(event: Event): void {
|
||||
if (!event.defaultPrevented) {
|
||||
if (this.enabled && !event.defaultPrevented) {
|
||||
this.preventDefault(event);
|
||||
this.element.classList.add(this.cssClassName);
|
||||
}
|
||||
@@ -132,7 +113,7 @@ export class FileDraggableDirective implements OnInit, OnDestroy {
|
||||
* @param {event} event - DOM event.
|
||||
*/
|
||||
onDragLeave(event: Event): void {
|
||||
if (!event.defaultPrevented) {
|
||||
if (this.enabled && !event.defaultPrevented) {
|
||||
this.preventDefault(event);
|
||||
this.element.classList.remove(this.cssClassName);
|
||||
}
|
||||
@@ -144,7 +125,7 @@ export class FileDraggableDirective implements OnInit, OnDestroy {
|
||||
* @param event
|
||||
*/
|
||||
onDragOver(event: Event): void {
|
||||
if (!event.defaultPrevented) {
|
||||
if (this.enabled && !event.defaultPrevented) {
|
||||
this.preventDefault(event);
|
||||
this.element.classList.add(this.cssClassName);
|
||||
}
|
||||
|
36
ng2-components/ng2-alfresco-upload/src/events/file.event.ts
Normal file
36
ng2-components/ng2-alfresco-upload/src/events/file.event.ts
Normal 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 { FileModel, FileUploadStatus } from '../models/file.model';
|
||||
|
||||
export class FileUploadEvent {
|
||||
|
||||
constructor(
|
||||
public readonly file: FileModel,
|
||||
public readonly status: FileUploadStatus = FileUploadStatus.Pending,
|
||||
public readonly error: any = null) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class FileUploadCompleteEvent extends FileUploadEvent {
|
||||
|
||||
constructor(file: FileModel, public totalComplete: number = 0, public data?: any, public totalAborted: number = 0) {
|
||||
super(file, FileUploadStatus.Complete);
|
||||
}
|
||||
|
||||
}
|
@@ -1,25 +1,27 @@
|
||||
{
|
||||
"FILE_UPLOAD": {
|
||||
"BUTTON": {
|
||||
"UPLOAD_FILE": "Upload file",
|
||||
"UPLOAD_FOLDER": "Upload folder",
|
||||
"CANCEL_ALL": "Cancell all"
|
||||
"ADF_FILE_UPLOAD": {
|
||||
"FILE_LIST": {
|
||||
"NAME": "Name",
|
||||
"PROGRESS": "Progress",
|
||||
"SIZE": "Size",
|
||||
"ACTION": "Action"
|
||||
}
|
||||
},
|
||||
"MESSAGES": {
|
||||
"SINGLE_COMPLETED": "upload complete",
|
||||
"COMPLETED": "uploads complete",
|
||||
"PROGRESS": "Upload in progress...",
|
||||
"FOLDER_ALREADY_EXIST": "The folder {0} already exist",
|
||||
"FOLDER_NOT_SUPPORTED": "Folder upload isn't supported by your browser"
|
||||
},
|
||||
"FILE_INFO": {
|
||||
"NAME": "File name",
|
||||
"PROGRESS": "File progress",
|
||||
"SIZE": "File size",
|
||||
"ACTION": "Actions"
|
||||
},
|
||||
"ACTION": {
|
||||
"UNDO": "Undo"
|
||||
"FILE_UPLOAD": {
|
||||
"BUTTON": {
|
||||
"UPLOAD_FILE": "Upload file",
|
||||
"UPLOAD_FOLDER": "Upload folder",
|
||||
"CANCEL_ALL": "Cancell all"
|
||||
},
|
||||
"MESSAGES": {
|
||||
"SINGLE_COMPLETED": "upload complete",
|
||||
"COMPLETED": "uploads complete",
|
||||
"PROGRESS": "Upload in progress...",
|
||||
"FOLDER_ALREADY_EXIST": "The folder {0} already exist",
|
||||
"FOLDER_NOT_SUPPORTED": "Folder upload isn't supported by your browser"
|
||||
},
|
||||
"ACTION": {
|
||||
"UNDO": "Undo"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -15,126 +15,60 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* This object represent the status of an uploading file.
|
||||
*
|
||||
*
|
||||
* @returns {FileModel} .
|
||||
*/
|
||||
export class FileModel {
|
||||
id: string;
|
||||
status: number;
|
||||
statusText: string;
|
||||
progress: Object;
|
||||
name: string;
|
||||
size: string;
|
||||
response: string;
|
||||
done: boolean = false;
|
||||
error: boolean = false;
|
||||
abort: boolean = false;
|
||||
uploading: boolean = false;
|
||||
file: any;
|
||||
promiseUpload: any;
|
||||
export interface FileUploadProgress {
|
||||
loaded: number;
|
||||
total: number;
|
||||
percent: number;
|
||||
}
|
||||
|
||||
constructor(file: any) {
|
||||
export interface FileUploadOptions {
|
||||
newVersion?: boolean;
|
||||
parentId?: string;
|
||||
path?: string;
|
||||
}
|
||||
|
||||
export enum FileUploadStatus {
|
||||
Pending = 0,
|
||||
Complete = 1,
|
||||
Starting = 2,
|
||||
Progress = 3,
|
||||
Cancelled = 4,
|
||||
Aborted = 5,
|
||||
Error = 6
|
||||
}
|
||||
|
||||
export class FileModel {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly size: number;
|
||||
readonly file: File;
|
||||
|
||||
status: FileUploadStatus = FileUploadStatus.Pending;
|
||||
progress: FileUploadProgress;
|
||||
options: FileUploadOptions;
|
||||
|
||||
constructor(file: File, options?: FileUploadOptions) {
|
||||
this.file = file;
|
||||
this.id = this._generateId();
|
||||
|
||||
this.id = this.generateId();
|
||||
this.name = file.name;
|
||||
this.size = this._getFileSize(file.size);
|
||||
this.size = file.size;
|
||||
|
||||
this.progress = {
|
||||
loaded: 0,
|
||||
total: 0,
|
||||
percent: 0
|
||||
};
|
||||
|
||||
this.options = Object.assign({}, {
|
||||
newVersion: false
|
||||
}, options);
|
||||
}
|
||||
|
||||
setProgres(progress: any): void {
|
||||
this.progress = progress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit an event progress on the promise
|
||||
*/
|
||||
emitProgres(progress: any): void {
|
||||
this.setProgres(progress);
|
||||
this.promiseUpload.emit('progress', progress);
|
||||
}
|
||||
|
||||
setError(): void {
|
||||
this.error = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit an event progress on the promise
|
||||
*/
|
||||
emitError(): void {
|
||||
this.setError();
|
||||
this.promiseUpload.emit('error');
|
||||
}
|
||||
|
||||
setUploading() {
|
||||
this.uploading = true;
|
||||
}
|
||||
|
||||
setPromiseUpload(promiseUpload: any) {
|
||||
this.promiseUpload = promiseUpload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the uploading of the file.
|
||||
*/
|
||||
setAbort(): void {
|
||||
if (!this.done && !this.error) {
|
||||
this.abort = true;
|
||||
this.uploading = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit an event abort on the promise
|
||||
*/
|
||||
emitAbort(): void {
|
||||
this.setAbort();
|
||||
this.promiseUpload.abort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update status of the file when upload finish or is ended.
|
||||
*/
|
||||
onFinished(status: number, statusText: string, response: string): void {
|
||||
this.status = status;
|
||||
this.statusText = statusText;
|
||||
this.response = response;
|
||||
this.done = true;
|
||||
this.uploading = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the size of the file in kb,mb and gb.
|
||||
*
|
||||
* @param {number} sizeinbytes - size in bytes of the file.
|
||||
*/
|
||||
private _getFileSize(sizeinbytes: number): string {
|
||||
let fSExt = new Array('Bytes', 'KB', 'MB', 'GB');
|
||||
let size = sizeinbytes;
|
||||
let i = 0;
|
||||
while (size > 900) {
|
||||
size /= 1000;
|
||||
i++;
|
||||
}
|
||||
return Math.round((Math.round(size * 100) / 100)) + ' ' + fSExt[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the size of the file in kb,mb and gb.
|
||||
*
|
||||
* @return {string} - return a unique file uploading id.
|
||||
*/
|
||||
private _generateId(): string {
|
||||
return 'uploading-file-' + 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
let r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
private generateId(): string {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
let r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -19,22 +19,13 @@ import { EventEmitter } from '@angular/core';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { CoreModule } from 'ng2-alfresco-core';
|
||||
import { UploadService } from './upload.service';
|
||||
import { FileModel, FileUploadOptions } from '../models/file.model';
|
||||
|
||||
declare let jasmine: any;
|
||||
|
||||
describe('UploadService', () => {
|
||||
let service: UploadService;
|
||||
|
||||
let options = {
|
||||
host: 'fakehost',
|
||||
url: '/some/cool/url',
|
||||
baseUrlPath: 'fakebasepath',
|
||||
formFields: {
|
||||
siteid: 'fakeSite',
|
||||
containerid: 'fakeFolder'
|
||||
}
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
@@ -53,27 +44,32 @@ describe('UploadService', () => {
|
||||
});
|
||||
|
||||
it('should return an empty queue if no elements are added', () => {
|
||||
service.setOptions(options, false);
|
||||
expect(service.getQueue().length).toEqual(0);
|
||||
});
|
||||
|
||||
it('should add an element in the queue and returns it', () => {
|
||||
service.setOptions(options, false);
|
||||
let filesFake = [<File>{name: 'fake-name', size: 10}];
|
||||
let filesFake = new FileModel(<File>{ name: 'fake-name', size: 10 });
|
||||
service.addToQueue(filesFake);
|
||||
expect(service.getQueue().length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should add two elements in the queue and returns them', () => {
|
||||
service.setOptions(options, false);
|
||||
let filesFake = [
|
||||
<File>{name: 'fake-name', size: 10},
|
||||
<File>{name: 'fake-name2', size: 20}
|
||||
new FileModel(<File>{ name: 'fake-name', size: 10 }),
|
||||
new FileModel(<File>{ name: 'fake-name2', size: 20 })
|
||||
];
|
||||
service.addToQueue(filesFake);
|
||||
service.addToQueue(...filesFake);
|
||||
expect(service.getQueue().length).toEqual(2);
|
||||
});
|
||||
|
||||
it('should skip hidden macOS files', () => {
|
||||
const file1 = new FileModel(new File([''], '.git'));
|
||||
const file2 = new FileModel(new File([''], 'readme.md'));
|
||||
const result = service.addToQueue(file1, file2);
|
||||
expect(result.length).toBe(1);
|
||||
expect(result[0]).toBe(file2);
|
||||
});
|
||||
|
||||
it('should make XHR done request after the file is added in the queue', (done) => {
|
||||
let emitter = new EventEmitter();
|
||||
|
||||
@@ -81,13 +77,15 @@ describe('UploadService', () => {
|
||||
expect(e.value).toBe('File uploaded');
|
||||
done();
|
||||
});
|
||||
service.setOptions(options, false);
|
||||
let filesFake = [<File>{name: 'fake-name', size: 10}];
|
||||
service.addToQueue(filesFake);
|
||||
service.uploadFilesInTheQueue('-root-', 'fake-dir', emitter);
|
||||
let fileFake = new FileModel(
|
||||
<File>{ name: 'fake-name', size: 10 },
|
||||
<FileUploadOptions> { parentId: '-root-', path: 'fake-dir' }
|
||||
);
|
||||
service.addToQueue(fileFake);
|
||||
service.uploadFilesInTheQueue(emitter);
|
||||
|
||||
let request = jasmine.Ajax.requests.mostRecent();
|
||||
expect(request.url).toBe('http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children?autoRename=true');
|
||||
expect(request.url).toBe('http://localhost:3000/ecm/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children?autoRename=true');
|
||||
expect(request.method).toBe('POST');
|
||||
|
||||
jasmine.Ajax.requests.mostRecent().respondWith({
|
||||
@@ -104,12 +102,14 @@ describe('UploadService', () => {
|
||||
expect(e.value).toBe('Error file uploaded');
|
||||
done();
|
||||
});
|
||||
service.setOptions(options, false);
|
||||
let filesFake = [<File>{name: 'fake-name', size: 10}];
|
||||
service.addToQueue(filesFake);
|
||||
service.uploadFilesInTheQueue('-root-', '', emitter);
|
||||
let fileFake = new FileModel(
|
||||
<File>{ name: 'fake-name', size: 10 },
|
||||
<FileUploadOptions> { parentId: '-root-' }
|
||||
);
|
||||
service.addToQueue(fileFake);
|
||||
service.uploadFilesInTheQueue(emitter);
|
||||
expect(jasmine.Ajax.requests.mostRecent().url)
|
||||
.toBe('http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children?autoRename=true');
|
||||
.toBe('http://localhost:3000/ecm/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children?autoRename=true');
|
||||
|
||||
jasmine.Ajax.requests.mostRecent().respondWith({
|
||||
'status': 404,
|
||||
@@ -125,109 +125,20 @@ describe('UploadService', () => {
|
||||
expect(e.value).toEqual('File aborted');
|
||||
done();
|
||||
});
|
||||
service.setOptions(options, false);
|
||||
let filesFake = [<File>{name: 'fake-name', size: 10}];
|
||||
service.addToQueue(filesFake);
|
||||
service.uploadFilesInTheQueue('-root-', '', emitter);
|
||||
let fileFake = new FileModel(<File>{ name: 'fake-name', size: 10 });
|
||||
service.addToQueue(fileFake);
|
||||
service.uploadFilesInTheQueue(emitter);
|
||||
|
||||
let file = service.getQueue();
|
||||
file[0].emitAbort();
|
||||
});
|
||||
|
||||
it('should make XHR error request after the xhr error is called', (done) => {
|
||||
let emitter = new EventEmitter();
|
||||
|
||||
emitter.subscribe(e => {
|
||||
expect(e.value).toBe('Error file uploaded');
|
||||
done();
|
||||
});
|
||||
service.setOptions(options, false);
|
||||
let filesFake = [<File>{name: 'fake-name', size: 10}];
|
||||
service.addToQueue(filesFake);
|
||||
service.uploadFilesInTheQueue('-root-', '', emitter);
|
||||
|
||||
let file = service.getQueue();
|
||||
file[0].emitError();
|
||||
});
|
||||
|
||||
it('should make XHR progress request after the onprogress is called', (done) => {
|
||||
service.setOptions(options, false);
|
||||
let fakeProgress = {
|
||||
loaded: 500,
|
||||
total: 1234,
|
||||
percent: 44
|
||||
};
|
||||
let filesFake = [<File>{name: 'fake-name', size: 10}];
|
||||
service.addToQueue(filesFake);
|
||||
service.filesUpload$.subscribe((file) => {
|
||||
expect(file).toBeDefined();
|
||||
expect(file[0]).toBeDefined();
|
||||
expect(file[0].progress).toEqual(fakeProgress);
|
||||
done();
|
||||
});
|
||||
service.uploadFilesInTheQueue('-root-', '', null);
|
||||
|
||||
let file = service.getQueue();
|
||||
|
||||
file[0].emitProgres(fakeProgress);
|
||||
});
|
||||
|
||||
it('should make XHR done request after the folder is created', (done) => {
|
||||
let fakeRest = {
|
||||
entry: {
|
||||
isFile: false,
|
||||
isFolder: true,
|
||||
name: 'fake-folder'
|
||||
}
|
||||
};
|
||||
let fakePromise = new Promise(function (resolve, reject) {
|
||||
resolve(fakeRest);
|
||||
});
|
||||
spyOn(service, 'callApiCreateFolder').and.returnValue(fakePromise);
|
||||
service.setOptions(options, false);
|
||||
let defaultPath = '';
|
||||
let folderName = 'fake-folder';
|
||||
service.createFolder(defaultPath, folderName).subscribe(res => {
|
||||
expect(res).toEqual(fakeRest);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should throws an exception when a folder already exist', (done) => {
|
||||
let fakeRest = {
|
||||
response: {
|
||||
body: {
|
||||
error: {
|
||||
statusCode: 409
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
let fakePromise = new Promise(function (resolve, reject) {
|
||||
reject(fakeRest);
|
||||
});
|
||||
spyOn(service, 'callApiCreateFolder').and.returnValue(fakePromise);
|
||||
service.setOptions(options, false);
|
||||
let defaultPath = '';
|
||||
let folderName = 'folder-duplicate-fake';
|
||||
service.createFolder(defaultPath, folderName).subscribe(
|
||||
res => {
|
||||
},
|
||||
error => {
|
||||
expect(error).toEqual(fakeRest);
|
||||
done();
|
||||
}
|
||||
);
|
||||
service.cancelUpload(...file);
|
||||
});
|
||||
|
||||
it('If versioning is true autoRename should not be present and majorVersion should be a param', () => {
|
||||
let emitter = new EventEmitter();
|
||||
|
||||
let enableVersioning = true;
|
||||
service.setOptions(options, enableVersioning);
|
||||
let filesFake = [<File>{name: 'fake-name', size: 10}];
|
||||
const filesFake = new FileModel(<File>{ name: 'fake-name', size: 10 }, { newVersion: true });
|
||||
service.addToQueue(filesFake);
|
||||
service.uploadFilesInTheQueue('-root-', '', emitter);
|
||||
service.uploadFilesInTheQueue(emitter);
|
||||
|
||||
expect(jasmine.Ajax.requests.mostRecent().url.endsWith('autoRename=true')).toBe(false);
|
||||
expect(jasmine.Ajax.requests.mostRecent().params.has('majorVersion')).toBe(true);
|
||||
@@ -240,13 +151,15 @@ describe('UploadService', () => {
|
||||
expect(e.value).toBe('File uploaded');
|
||||
done();
|
||||
});
|
||||
service.setOptions(options, false);
|
||||
let filesFake = [<File>{name: 'fake-name', size: 10}];
|
||||
let filesFake = new FileModel(
|
||||
<File>{ name: 'fake-name', size: 10 },
|
||||
<FileUploadOptions> { parentId: '123', path: 'fake-dir' }
|
||||
);
|
||||
service.addToQueue(filesFake);
|
||||
service.uploadFilesInTheQueue('123', 'fake-dir', emitter);
|
||||
service.uploadFilesInTheQueue(emitter);
|
||||
|
||||
let request = jasmine.Ajax.requests.mostRecent();
|
||||
expect(request.url).toBe('http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/123/children?autoRename=true');
|
||||
expect(request.url).toBe('http://localhost:3000/ecm/alfresco/api/-default-/public/alfresco/versions/1/nodes/123/children?autoRename=true');
|
||||
expect(request.method).toBe('POST');
|
||||
|
||||
jasmine.Ajax.requests.mostRecent().respondWith({
|
||||
@@ -255,4 +168,26 @@ describe('UploadService', () => {
|
||||
responseText: 'File uploaded'
|
||||
});
|
||||
});
|
||||
|
||||
it('should start downloading the next one if a file of the list is aborted', (done) => {
|
||||
let emitter = new EventEmitter();
|
||||
|
||||
service.fileUploadAborted.subscribe(e => {
|
||||
expect(e).not.toBeNull();
|
||||
});
|
||||
|
||||
service.fileUploadCancelled.subscribe(e => {
|
||||
expect(e).not.toBeNull();
|
||||
done();
|
||||
});
|
||||
|
||||
let fileFake1 = new FileModel(<File>{ name: 'fake-name1', size: 10 });
|
||||
let fileFake2 = new FileModel(<File>{ name: 'fake-name2', size: 10 });
|
||||
let filelist = [fileFake1, fileFake2];
|
||||
service.addToQueue(...filelist);
|
||||
service.uploadFilesInTheQueue(emitter);
|
||||
|
||||
let file = service.getQueue();
|
||||
service.cancelUpload(...file);
|
||||
});
|
||||
});
|
||||
|
@@ -16,130 +16,45 @@
|
||||
*/
|
||||
|
||||
import { EventEmitter, Injectable } from '@angular/core';
|
||||
import { Response } from '@angular/http';
|
||||
import { Observer, Observable } from 'rxjs/Rx';
|
||||
import { AlfrescoApiService, LogService } from 'ng2-alfresco-core';
|
||||
import { FileModel } from '../models/file.model';
|
||||
import { MinimalNodeEntity, MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { Subject } from 'rxjs/Rx';
|
||||
import { AlfrescoApiService } from 'ng2-alfresco-core';
|
||||
import { FileUploadEvent, FileUploadCompleteEvent } from '../events/file.event';
|
||||
import { FileModel, FileUploadProgress, FileUploadStatus } from '../models/file.model';
|
||||
|
||||
/**
|
||||
*
|
||||
* UploadService keep the queue of the file to upload and uploads them.
|
||||
*
|
||||
* @returns {UploadService} .
|
||||
*/
|
||||
@Injectable()
|
||||
export class UploadService {
|
||||
|
||||
private formFields: Object = {};
|
||||
private queue: FileModel[] = [];
|
||||
private versioning: boolean = false;
|
||||
private filesUploadObserverProgressBar: Observer<FileModel[]>;
|
||||
private totalCompletedObserver: Observer<number>;
|
||||
private cache: { [key: string]: any } = {};
|
||||
private totalComplete: number = 0;
|
||||
private totalAborted: number = 0;
|
||||
private activeTask: Promise<any> = null;
|
||||
|
||||
totalCompleted: number = 0;
|
||||
filesUpload$: Observable<FileModel[]>;
|
||||
totalCompleted$: Observable<any>;
|
||||
queueChanged: Subject<FileModel[]> = new Subject<FileModel[]>();
|
||||
fileUpload: Subject<FileUploadEvent> = new Subject<FileUploadEvent>();
|
||||
fileUploadStarting: Subject<FileUploadEvent> = new Subject<FileUploadEvent>();
|
||||
fileUploadCancelled: Subject<FileUploadEvent> = new Subject<FileUploadEvent>();
|
||||
fileUploadProgress: Subject<FileUploadEvent> = new Subject<FileUploadEvent>();
|
||||
fileUploadAborted: Subject<FileUploadEvent> = new Subject<FileUploadEvent>();
|
||||
fileUploadError: Subject<FileUploadEvent> = new Subject<FileUploadEvent>();
|
||||
fileUploadComplete: Subject<FileUploadCompleteEvent> = new Subject<FileUploadCompleteEvent>();
|
||||
|
||||
constructor(private apiService: AlfrescoApiService,
|
||||
private logService: LogService) {
|
||||
this.filesUpload$ = new Observable<FileModel[]>(observer => this.filesUploadObserverProgressBar = observer).share();
|
||||
this.totalCompleted$ = new Observable<number>(observer => this.totalCompletedObserver = observer).share();
|
||||
constructor(private apiService: AlfrescoApiService) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the service
|
||||
* Checks whether the service is uploading a file.
|
||||
*
|
||||
* @param {Object} - options formFields to init the object
|
||||
* @param {boolean} - versioning true to indicate that a major version should be created
|
||||
* @returns {boolean}
|
||||
*
|
||||
* @memberof UploadService
|
||||
*/
|
||||
setOptions(options: any, versioning: boolean): void {
|
||||
this.formFields = options.formFields != null ? options.formFields : this.formFields;
|
||||
this.versioning = versioning != null ? versioning : this.versioning;
|
||||
isUploading(): boolean {
|
||||
return this.activeTask ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add files to the uploading queue to be uploaded.
|
||||
*
|
||||
* @param {File[]} - files to add to the upload queue.
|
||||
*
|
||||
* return {FileModel[]} - return the file added to the queue in this call.
|
||||
*/
|
||||
addToQueue(files: File[]): FileModel[] {
|
||||
const result: FileModel[] = [];
|
||||
|
||||
for (let file of files) {
|
||||
let uploadingFileModel = new FileModel(file);
|
||||
result.push(uploadingFileModel);
|
||||
this.queue.push(uploadingFileModel);
|
||||
if (this.filesUploadObserverProgressBar) {
|
||||
this.filesUploadObserverProgressBar.next(this.queue);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick all the files in the queue that are not been uploaded yet and upload it into the directory folder.
|
||||
*/
|
||||
uploadFilesInTheQueue(rootId: string, directory: string, elementEmit: EventEmitter<any>): void {
|
||||
let filesToUpload = this.queue.filter((uploadingFileModel) => {
|
||||
return !uploadingFileModel.uploading && !uploadingFileModel.done && !uploadingFileModel.abort && !uploadingFileModel.error;
|
||||
});
|
||||
|
||||
let opts: any = {};
|
||||
opts.renditions = 'doclib';
|
||||
|
||||
if (this.versioning) {
|
||||
opts.overwrite = true;
|
||||
opts.majorVersion = true;
|
||||
} else {
|
||||
opts.autoRename = true;
|
||||
}
|
||||
|
||||
filesToUpload.forEach((uploadingFileModel: FileModel) => {
|
||||
uploadingFileModel.setUploading();
|
||||
|
||||
let promiseUpload = this.apiService.getInstance().upload.uploadFile(uploadingFileModel.file, directory, rootId, null, opts)
|
||||
.on('progress', (progress: any) => {
|
||||
uploadingFileModel.setProgres(progress);
|
||||
this.updateFileListStream(this.queue);
|
||||
})
|
||||
.on('abort', () => {
|
||||
uploadingFileModel.setAbort();
|
||||
elementEmit.emit({
|
||||
value: 'File aborted'
|
||||
});
|
||||
})
|
||||
.on('error', () => {
|
||||
uploadingFileModel.setError();
|
||||
elementEmit.emit({
|
||||
value: 'Error file uploaded'
|
||||
});
|
||||
})
|
||||
.on('success', (data: any) => {
|
||||
elementEmit.emit({
|
||||
value: data
|
||||
});
|
||||
uploadingFileModel.onFinished(
|
||||
data.status,
|
||||
data.statusText,
|
||||
data.response
|
||||
);
|
||||
|
||||
this.updateFileListStream(this.queue);
|
||||
if (!uploadingFileModel.abort && !uploadingFileModel.error) {
|
||||
this.updateFileCounterStream(++this.totalCompleted);
|
||||
}
|
||||
});
|
||||
|
||||
uploadingFileModel.setPromiseUpload(promiseUpload);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all the files in the uploading queue.
|
||||
* Returns the file Queue
|
||||
*
|
||||
* @return {FileModel[]} - files in the upload queue.
|
||||
*/
|
||||
@@ -148,53 +63,183 @@ export class UploadService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a folder
|
||||
* @param name - the folder name
|
||||
* Add files to the uploading queue to be uploaded.
|
||||
*
|
||||
* Examples:
|
||||
* addToQueue(file); // pass one file
|
||||
* addToQueue(file1, file2, file3); // pass multiple files
|
||||
* addToQueue(...[file1, file2, file3]); // pass an array of files
|
||||
*/
|
||||
createFolder(relativePath: string, name: string, parentId?: string) {
|
||||
return Observable.fromPromise(this.callApiCreateFolder(relativePath, name, parentId))
|
||||
.do(data => this.logService.info('Node data', data)) // eyeball results in the console
|
||||
.catch(err => this.handleError(err));
|
||||
}
|
||||
|
||||
callApiCreateFolder(relativePath: string, name: string, parentId?: string): Promise<MinimalNodeEntity> {
|
||||
return this.apiService.getInstance().nodes.createFolder(name, relativePath, parentId);
|
||||
addToQueue(...files: FileModel[]): FileModel[] {
|
||||
const allowedFiles = files.filter(f => !f.name.startsWith('.'));
|
||||
this.queue = this.queue.concat(allowedFiles);
|
||||
this.queueChanged.next(this.queue);
|
||||
return allowedFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw the error
|
||||
* @param error
|
||||
* @returns {ErrorObservable}
|
||||
* Pick all the files in the queue that are not been uploaded yet and upload it into the directory folder.
|
||||
*
|
||||
* @param {EventEmitter<any>} emitter @deprecated emitter to invoke on file status change
|
||||
*
|
||||
* @memberof UploadService
|
||||
*/
|
||||
private handleError(error: Response) {
|
||||
// in a real world app, we may send the error to some remote logging infrastructure
|
||||
// instead of just logging it to the console
|
||||
this.logService.error(error);
|
||||
return Observable.throw(error || 'Server error');
|
||||
}
|
||||
uploadFilesInTheQueue(emitter: EventEmitter<any>): void {
|
||||
if (!this.activeTask) {
|
||||
let file = this.queue.find(f => f.status === FileUploadStatus.Pending);
|
||||
if (file) {
|
||||
this.onUploadStarting(file);
|
||||
|
||||
private updateFileListStream(fileList: FileModel[]) {
|
||||
if (this.filesUploadObserverProgressBar) {
|
||||
this.filesUploadObserverProgressBar.next(fileList);
|
||||
const promise = this.beginUpload(file, emitter);
|
||||
this.activeTask = promise;
|
||||
this.cache[file.id] = promise;
|
||||
|
||||
let next = () => {
|
||||
this.activeTask = null;
|
||||
setTimeout(() => this.uploadFilesInTheQueue(emitter), 100);
|
||||
};
|
||||
|
||||
promise.next = next;
|
||||
|
||||
promise.then(
|
||||
() => next(),
|
||||
() => next()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateFileCounterStream(total: number) {
|
||||
if (this.totalCompletedObserver) {
|
||||
this.totalCompletedObserver.next(total);
|
||||
}
|
||||
cancelUpload(...files: FileModel[]) {
|
||||
files.forEach(file => {
|
||||
file.status = FileUploadStatus.Cancelled;
|
||||
|
||||
const promise = this.cache[file.id];
|
||||
if (promise) {
|
||||
promise.abort();
|
||||
delete this.cache[file.id];
|
||||
}
|
||||
|
||||
const event = new FileUploadEvent(file, FileUploadStatus.Cancelled);
|
||||
this.fileUpload.next(event);
|
||||
this.fileUploadCancelled.next(event);
|
||||
});
|
||||
}
|
||||
|
||||
getFolderNode(nodeId: string): Observable<MinimalNodeEntryEntity> {
|
||||
clearQueue() {
|
||||
this.queue = [];
|
||||
this.totalComplete = 0;
|
||||
this.totalAborted = 0;
|
||||
}
|
||||
|
||||
private beginUpload(file: FileModel, /* @deprecated */emitter: EventEmitter<any>): any {
|
||||
let opts: any = {
|
||||
includeSource: true,
|
||||
include: ['allowableOperations']
|
||||
};
|
||||
renditions: 'doclib'
|
||||
};
|
||||
|
||||
return Observable.fromPromise(this.apiService.getInstance().nodes.getNodeInfo(nodeId, opts))
|
||||
.map((response: any) => {
|
||||
return response;
|
||||
})
|
||||
.catch(err => this.handleError(err));
|
||||
if (file.options.newVersion === true) {
|
||||
opts.overwrite = true;
|
||||
opts.majorVersion = true;
|
||||
} else {
|
||||
opts.autoRename = true;
|
||||
}
|
||||
let promise = this.apiService.getInstance().upload.uploadFile(
|
||||
file.file,
|
||||
file.options.path,
|
||||
file.options.parentId,
|
||||
null,
|
||||
opts
|
||||
);
|
||||
promise.on('progress', (progress: FileUploadProgress) => {
|
||||
this.onUploadProgress(file, progress);
|
||||
})
|
||||
.on('abort', () => {
|
||||
this.onUploadAborted(file);
|
||||
emitter.emit({ value: 'File aborted' });
|
||||
})
|
||||
.on('error', err => {
|
||||
this.onUploadError(file, err);
|
||||
emitter.emit({ value: 'Error file uploaded' });
|
||||
})
|
||||
.on('success', data => {
|
||||
this.onUploadComplete(file, data);
|
||||
emitter.emit({ value: data });
|
||||
})
|
||||
.catch(err => {
|
||||
this.onUploadError(file, err);
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
private onUploadStarting(file: FileModel): void {
|
||||
if (file) {
|
||||
file.status = FileUploadStatus.Starting;
|
||||
const event = new FileUploadEvent(file, FileUploadStatus.Starting);
|
||||
this.fileUpload.next(event);
|
||||
this.fileUploadStarting.next(event);
|
||||
}
|
||||
}
|
||||
|
||||
private onUploadProgress(file: FileModel, progress: FileUploadProgress): void {
|
||||
if (file) {
|
||||
file.progress = progress;
|
||||
file.status = FileUploadStatus.Progress;
|
||||
|
||||
const event = new FileUploadEvent(file, FileUploadStatus.Progress);
|
||||
this.fileUpload.next(event);
|
||||
this.fileUploadProgress.next(event);
|
||||
|
||||
this.queueChanged.next(this.queue);
|
||||
}
|
||||
}
|
||||
|
||||
private onUploadError(file: FileModel, error: any): void {
|
||||
if (file) {
|
||||
file.status = FileUploadStatus.Error;
|
||||
|
||||
const promise = this.cache[file.id];
|
||||
if (promise) {
|
||||
delete this.cache[file.id];
|
||||
}
|
||||
|
||||
const event = new FileUploadEvent(file, FileUploadStatus.Error, error);
|
||||
this.fileUpload.next(event);
|
||||
this.fileUploadError.next(event);
|
||||
}
|
||||
}
|
||||
|
||||
private onUploadComplete(file: FileModel, data: any): void {
|
||||
if (file) {
|
||||
file.status = FileUploadStatus.Complete;
|
||||
this.totalComplete++;
|
||||
|
||||
const promise = this.cache[file.id];
|
||||
if (promise) {
|
||||
delete this.cache[file.id];
|
||||
}
|
||||
|
||||
const event = new FileUploadCompleteEvent(file, this.totalComplete, data, this.totalAborted);
|
||||
this.fileUpload.next(event);
|
||||
this.fileUploadComplete.next(event);
|
||||
|
||||
this.queueChanged.next(this.queue);
|
||||
}
|
||||
}
|
||||
|
||||
private onUploadAborted(file: FileModel): void {
|
||||
if (file) {
|
||||
file.status = FileUploadStatus.Aborted;
|
||||
this.totalAborted++;
|
||||
|
||||
const promise = this.cache[file.id];
|
||||
if (promise) {
|
||||
delete this.cache[file.id];
|
||||
}
|
||||
|
||||
const event = new FileUploadEvent(file, FileUploadStatus.Aborted);
|
||||
this.fileUpload.next(event);
|
||||
this.fileUploadAborted.next(event);
|
||||
promise.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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",
|
||||
|
@@ -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-upload": "./index.ts"
|
||||
}
|
||||
});
|
||||
module.exports = require('./config/webpack.build.js');
|
||||
|
1
ng2-components/ng2-alfresco-upload/webpack.coverage.js
Normal file
1
ng2-components/ng2-alfresco-upload/webpack.coverage.js
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = require('./config/webpack.coverage.js');
|
@@ -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');
|
||||
|
Reference in New Issue
Block a user