[ADF-1774] Extend "Create Folder" and add "Edit Folder" dialogs to ADF (#2585)

* [ADF-1774] Extend "Create Folder" and add "Edit Folder" dialogs to ADF

- added create and edit folder directives and used them inside the demo-shell file component
- added FolderDialogComponent for edit/create actions with name validators: forbidSpecialCharacters, forbidEndingDot and forbidOnlySpaces
- updated the translation files
- added unit tests for the new components
- removed the CreateFolderDialogComponent and its usage inside demo-shell
- added documentation files for FolderCreateDirective and FolderEditDirective

* [ADF-1774] fix messed up translation files because of previous commit

* [ADF-1774] changes required on code review comments
This commit is contained in:
suzanadirla
2017-11-03 16:45:42 +02:00
committed by Eugenio Romano
parent c74b696a6e
commit 5dabc39841
28 changed files with 1431 additions and 117 deletions

View File

@@ -34,9 +34,14 @@
<div fxFlex="0 0 auto" class="adf-document-action-buttons" fxShow fxHide.lt-sm="true">
<button mat-icon-button
(click)="onCreateFolderClicked($event)">
[adf-create-folder]="getDocumentListCurrentFolderId()">
<mat-icon>create_new_folder</mat-icon>
</button>
<button mat-icon-button
[disabled]="!canEditFolder(documentList.selection)"
[adf-edit-folder]="documentList.selection[0]?.entry">
<mat-icon>create</mat-icon>
</button>
<button mat-icon-button
[disabled]="!hasSelection(documentList.selection)"
title="Download"
@@ -83,10 +88,16 @@
</button>
<mat-menu #menu="matMenu">
<button mat-menu-item
(click)="onCreateFolderClicked($event)">
[adf-create-folder]="getDocumentListCurrentFolderId()">
<mat-icon>create_new_folder</mat-icon>
<span>New folder</span>
</button>
<button mat-menu-item
[disabled]="!canEditFolder(documentList.selection)"
[adf-edit-folder]="documentList.selection[0]?.entry">
<mat-icon>create</mat-icon>
<span>Edit folder</span>
</button>
<button mat-menu-item
[disabled]="!hasSelection(documentList.selection)"
title="Download"

View File

@@ -15,18 +15,19 @@
* limitations under the License.
*/
import { ChangeDetectorRef, Component, Input, OnInit, Optional, ViewChild } from '@angular/core';
import { ChangeDetectorRef, Component, Input, OnInit, OnDestroy, Optional, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { MinimalNodeEntity } from 'alfresco-js-api';
import {
AlfrescoApiService, AlfrescoContentService, AlfrescoTranslationService, CreateFolderDialogComponent,
AlfrescoApiService, AlfrescoContentService, AlfrescoTranslationService,
DownloadZipDialogComponent, FileUploadEvent, FolderCreatedEvent, LogService, NotificationService,
SiteModel, UploadService
} from 'ng2-alfresco-core';
import { DataColumn, DataRow } from 'ng2-alfresco-datatable';
import { DocumentListComponent, PermissionStyleModel } from 'ng2-alfresco-documentlist';
import { VersionManagerDialogAdapterComponent } from './version-manager-dialog-adapter.component';
import { Subscription } from 'rxjs/Rx';
const DEFAULT_FOLDER_TO_SHOW = '-my-';
@@ -35,7 +36,7 @@ const DEFAULT_FOLDER_TO_SHOW = '-my-';
templateUrl: './files.component.html',
styleUrls: ['./files.component.scss']
})
export class FilesComponent implements OnInit {
export class FilesComponent implements OnInit, OnDestroy {
// The identifier of a node. You can also use one of these well-known aliases: -my- | -shared- | -root-
currentFolderId: string = DEFAULT_FOLDER_TO_SHOW;
@@ -87,6 +88,9 @@ export class FilesComponent implements OnInit {
permissionsStyle: PermissionStyleModel[] = [];
private onCreateFolder: Subscription;
private onEditFolder: Subscription;
constructor(private changeDetector: ChangeDetectorRef,
private apiService: AlfrescoApiService,
private notificationService: NotificationService,
@@ -130,11 +134,18 @@ export class FilesComponent implements OnInit {
this.uploadService.fileUploadComplete.asObservable().debounceTime(300).subscribe(value => this.onFileUploadEvent(value));
this.uploadService.fileUploadDeleted.subscribe((value) => this.onFileUploadEvent(value));
this.contentService.folderCreated.subscribe(value => this.onFolderCreated(value));
this.onCreateFolder = this.contentService.folderCreate.subscribe(value => this.onFolderAction(value));
this.onEditFolder = this.contentService.folderEdit.subscribe(value => this.onFolderAction(value));
// this.permissionsStyle.push(new PermissionStyleModel('document-list__create', PermissionsEnum.CREATE));
// this.permissionsStyle.push(new PermissionStyleModel('document-list__disable', PermissionsEnum.NOT_CREATE, false, true));
}
ngOnDestroy() {
this.onCreateFolder.unsubscribe();
this.onEditFolder.unsubscribe();
}
getCurrentDocumentListNode(): MinimalNodeEntity[] {
if (this.documentList.folderNode) {
return [{ entry: this.documentList.folderNode }];
@@ -167,6 +178,13 @@ export class FilesComponent implements OnInit {
}
}
onFolderAction(node) {
this.logService.log(node);
if (node && node.parentId === this.documentList.currentFolderId) {
this.documentList.reload();
}
}
handlePermissionError(event: any) {
this.translateService.get('OPERATION.ERROR.NO_PERMISSION_EVENT', {
permission: event.permission,
@@ -214,18 +232,6 @@ export class FilesComponent implements OnInit {
this.uploadService.fileDeleted.next(message);
}
onCreateFolderClicked(event: Event) {
let dialogRef = this.dialog.open(CreateFolderDialogComponent);
dialogRef.afterClosed().subscribe(folderName => {
if (folderName) {
this.contentService.createFolder('', folderName, this.documentList.currentFolderId).subscribe(
node => this.logService.log(node),
err => this.logService.log(err)
);
}
});
}
onManageVersions(event) {
const contentEntry = event.value.entry;
@@ -337,4 +343,15 @@ export class FilesComponent implements OnInit {
}
return null;
}
canEditFolder(selection: Array<MinimalNodeEntity>): boolean {
if (selection && selection.length === 1) {
const entry = selection[0].entry;
if (entry && entry.isFolder) {
return this.contentService.hasPermission(entry, 'update');
}
}
return false;
}
}

View File

@@ -253,6 +253,8 @@ for more information about installing and using the source code.
- [Node permission directive](node-permission.directive.md)
- [Node restore directive](node-restore.directive.md)
- [Upload directive](upload.directive.md)
- [Folder create directive](folder-create.directive.md)
- [Folder edit directive](folder-edit.directive.md)
### Models

View File

@@ -0,0 +1,40 @@
# Folder Create directive
<!-- markdown-toc start - Don't edit this section. npm run toc to generate it-->
<!-- toc -->
- [Basic Usage](#basic-usage)
* [Properties](#properties)
* [Events](#events)
- [Details](#details)
<!-- tocstop -->
<!-- markdown-toc end -->
## Basic Usage
```html
<adf-toolbar>
<button mat-icon-button
[adf-create-folder]="documentList.currentFolderId">
<mat-icon>create_new_folder</mat-icon>
</button>
</adf-toolbar>
<adf-document-list #documentList ...>
...
</adf-document-list>
```
### Properties
| Name | Type | Default | Description |
| ----------------- | ------------------- | --------- | ----------------------------------------------------------------- |
| adf-create-folder | string | '-my-' | Parent folder where the new folder will be located after creation |
## Details
'FolderCreateDirective' directive needs the id of the parent folder where we want the new folder node to be created. If no value is provided, the '-my-' alias is used.
It opens the FolderDialogComponent to receive data for the new folder. If data is valid, on dialog close, it emits folderCreate event.

View File

@@ -0,0 +1,40 @@
# Folder Edit directive
<!-- markdown-toc start - Don't edit this section. npm run toc to generate it-->
<!-- toc -->
- [Basic Usage](#basic-usage)
* [Properties](#properties)
* [Events](#events)
- [Details](#details)
<!-- tocstop -->
<!-- markdown-toc end -->
## Basic Usage
```html
<adf-toolbar title="toolbar example">
<button mat-icon-button
[adf-edit-folder]="documentList.selection[0]?.entry">
<mat-icon>create</mat-icon>
</button>
</adf-toolbar>
<adf-document-list #documentList ...>
...
</adf-document-list>
```
### Properties
| Name | Type | Default | Description |
| ----------------- | ---------------------- | ------- | ----------------------------------- |
| adf-edit-folder | MinimalNodeEntryEntity | | The folder node entity for editing |
## Details
'FolderEditDirective' directive needs a selection folder entry of #documentList to open the folder dialog component to edit the name and description properties of that selected folder.
If data is valid, on dialog close, it emits folderEdit event.

View File

@@ -29,8 +29,8 @@ import { CardViewModule } from './src/components/view/card-view.module';
import { MaterialModule } from './src/material.module';
import { AppConfigModule } from './src/services/app-config.service';
import { CreateFolderDialogComponent } from './src/dialogs/create-folder.dialog';
import { DownloadZipDialogComponent } from './src/dialogs/download-zip.dialog';
import { FolderDialogComponent } from './src/dialogs/folder.dialog';
import { AlfrescoApiService } from './src/services/alfresco-api.service';
import { AlfrescoContentService } from './src/services/alfresco-content.service';
@@ -52,6 +52,8 @@ import { AlfrescoTranslateLoader } from './src/services/translate-loader.service
import { TRANSLATION_PROVIDER, TranslationService } from './src/services/translation.service';
import { UploadService } from './src/services/upload.service';
import { FolderCreateDirective } from './src/directives/folder-create.directive';
import { FolderEditDirective } from './src/directives/folder-edit.directive';
import { HighlightDirective } from './src/directives/highlight.directive';
import { LogoutDirective } from './src/directives/logout.directive';
import { NodeDeleteDirective } from './src/directives/node-delete.directive';
@@ -73,8 +75,8 @@ import { UserPreferencesService } from './src/services/user-preferences.service'
export { MomentDateAdapter, MOMENT_DATE_FORMATS } from './src/utils/momentDateAdapter';
import { MomentDateAdapter } from './src/utils/momentDateAdapter';
export { CreateFolderDialogComponent } from './src/dialogs/create-folder.dialog';
export { DownloadZipDialogComponent } from './src/dialogs/download-zip.dialog';
export { FolderDialogComponent } from './src/dialogs/folder.dialog';
export { PageTitleService } from './src/services/page-title.service';
export { ContentService } from './src/services/content.service';
@@ -264,6 +266,8 @@ export function createTranslateLoader(http: Http, logService: LogService) {
],
declarations: [
...pipes(),
FolderCreateDirective,
FolderEditDirective,
LogoutDirective,
UploadDirective,
NodeRestoreDirective,
@@ -281,8 +285,8 @@ export function createTranslateLoader(http: Http, logService: LogService) {
InfoDrawerContentDirective,
LanguageMenuComponent,
HostSettingsComponent,
CreateFolderDialogComponent,
DownloadZipDialogComponent,
FolderDialogComponent,
InfinitePaginationComponent,
PaginationComponent
],
@@ -312,6 +316,8 @@ export function createTranslateLoader(http: Http, logService: LogService) {
CollapsableModule,
ToolbarModule,
...pipes(),
FolderCreateDirective,
FolderEditDirective,
LogoutDirective,
UploadDirective,
NodeRestoreDirective,
@@ -322,8 +328,8 @@ export function createTranslateLoader(http: Http, logService: LogService) {
HostSettingsComponent,
DataColumnComponent,
DataColumnListComponent,
CreateFolderDialogComponent,
DownloadZipDialogComponent,
FolderDialogComponent,
InfoDrawerComponent,
InfoDrawerTabComponent,
InfoDrawerLayoutComponent,
@@ -335,8 +341,8 @@ export function createTranslateLoader(http: Http, logService: LogService) {
PaginationComponent
],
entryComponents: [
CreateFolderDialogComponent,
DownloadZipDialogComponent
DownloadZipDialogComponent,
FolderDialogComponent
]
})
export class CoreModule {}

View File

@@ -1,45 +0,0 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, ViewEncapsulation } from '@angular/core';
@Component({
selector: 'adf-create-folder-dialog',
template: `
<h1 matDialogTitle>Create a new folder</h1>
<div mat-dialog-content>
<mat-form-field class="create-folder--name">
<input matInput placeholder="Folder name" [(ngModel)]="value">
</mat-form-field>
</div>
<div mat-dialog-actions>
<button mat-button matDialogClose>Cancel</button>
<button mat-button [matDialogClose]="value">Create</button>
</div>
`,
styles: [
`
.create-folder--name {
width: 100%;
}
`
],
encapsulation: ViewEncapsulation.None
})
export class CreateFolderDialogComponent {
value: string = '';
}

View File

@@ -0,0 +1,45 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { FormControl } from '@angular/forms';
const I18N_ERRORS_PATH = 'CORE.FOLDER_DIALOG.FOLDER_NAME.ERRORS';
export function forbidSpecialCharacters({ value }: FormControl) {
const specialCharacters: RegExp = /([\*\"\<\>\\\/\?\:\|])/;
const isValid: boolean = !specialCharacters.test(value);
return (isValid) ? null : {
message: `${I18N_ERRORS_PATH}.SPECIAL_CHARACTERS`
};
}
export function forbidEndingDot({ value }: FormControl) {
const isValid: boolean = ((value || '').trim().split('').pop() !== '.');
return isValid ? null : {
message: `${I18N_ERRORS_PATH}.ENDING_DOT`
};
}
export function forbidOnlySpaces({ value }: FormControl) {
const isValid: boolean = !!((value || '')).trim();
return isValid ? null : {
message: `${I18N_ERRORS_PATH}.ONLY_SPACES`
};
}

View File

@@ -0,0 +1,64 @@
<h2 mat-dialog-title>
{{
(editing
? 'CORE.FOLDER_DIALOG.EDIT_FOLDER_TITLE'
: 'CORE.FOLDER_DIALOG.CREATE_FOLDER_TITLE'
) | translate
}}
</h2>
<mat-dialog-content>
<form [formGroup]="form" (submit)="submit()">
<mat-input-container class="adf-full-width">
<input
placeholder="{{ 'CORE.FOLDER_DIALOG.FOLDER_NAME.LABEL' | translate }}"
matInput
required
[formControl]="form.controls['name']"
/>
<mat-hint *ngIf="form.controls['name'].dirty">
<span *ngIf="form.controls['name'].errors?.required">
{{ 'CORE.FOLDER_DIALOG.FOLDER_NAME.ERRORS.REQUIRED' | translate }}
</span>
<span *ngIf="!form.controls['name'].errors?.required && form.controls['name'].errors?.message">
{{ form.controls['name'].errors?.message | translate }}
</span>
</mat-hint>
</mat-input-container>
<br />
<br />
<mat-input-container class="adf-full-width">
<textarea
matInput
placeholder="{{ 'CORE.FOLDER_DIALOG.FOLDER_DESCRIPTION.LABEL' | translate }}"
rows="4"
[formControl]="form.controls['description']"></textarea>
</mat-input-container>
</form>
</mat-dialog-content>
<mat-dialog-actions>
<span class="adf-fill-remaining-space"></span>
<button
mat-button
mat-dialog-close>
{{ 'CORE.FOLDER_DIALOG.CANCEL_BUTTON.LABEL' | translate }}
</button>
<button
mat-button
(click)="submit()"
[disabled]="!form.valid">
{{
(editing
? 'CORE.FOLDER_DIALOG.UPDATE_BUTTON.LABEL'
: 'CORE.FOLDER_DIALOG.CREATE_BUTTON.LABEL'
) | translate
}}
</button>
</mat-dialog-actions>

View File

@@ -0,0 +1,7 @@
.adf-fill-remaining-space {
flex: 1 1 auto;
}
.adf-full-width {
width: 100%;
}

View File

@@ -0,0 +1,273 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { async, TestBed } from '@angular/core/testing';
import { ComponentFixture } from '@angular/core/testing';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatDialogModule, MatDialogRef } from '@angular/material';
import { BrowserDynamicTestingModule } from '@angular/platform-browser-dynamic/testing';
import { Observable } from 'rxjs/Rx';
import { AlfrescoTranslationService, CoreModule } from '../../index';
import { TranslationMock } from '../assets/translation.service.mock';
import { NodesApiService } from '../services/nodes-api.service';
import { NotificationService } from '../services/notification.service';
import { TranslationService } from '../services/translation.service';
import { FolderDialogComponent } from './folder.dialog';
describe('FolderDialogComponent', () => {
let fixture: ComponentFixture<FolderDialogComponent>;
let component: FolderDialogComponent;
let translationService: TranslationService;
let nodesApi: NodesApiService;
let notificationService: NotificationService;
let dialogRef;
beforeEach(async(() => {
dialogRef = {
close: jasmine.createSpy('close')
};
TestBed.configureTestingModule({
imports: [
CoreModule,
MatDialogModule,
FormsModule,
ReactiveFormsModule,
BrowserDynamicTestingModule
],
providers: [
{ provide: MatDialogRef, useValue: dialogRef },
{ provide: AlfrescoTranslationService, useClass: TranslationMock }
]
});
// entryComponents are not supported yet on TestBed, that is why this ugly workaround:
// https://github.com/angular/angular/issues/10760
TestBed.overrideModule(BrowserDynamicTestingModule, {
set: {entryComponents: [ FolderDialogComponent ]}
});
TestBed.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FolderDialogComponent);
component = fixture.componentInstance;
nodesApi = TestBed.get(NodesApiService);
notificationService = TestBed.get(NotificationService);
translationService = TestBed.get(TranslationService);
spyOn(translationService, 'get').and.returnValue(Observable.of('message'));
});
describe('Edit', () => {
beforeEach(() => {
component.data = {
folder: {
id: 'node-id',
name: 'folder-name',
properties: {
['cm:description']: 'folder-description'
}
}
};
fixture.detectChanges();
});
it('should init form with folder name and description', () => {
expect(component.name).toBe('folder-name');
expect(component.description).toBe('folder-description');
});
it('should update form input', () => {
component.form.controls['name'].setValue('folder-name-update');
component.form.controls['description'].setValue('folder-description-update');
expect(component.name).toBe('folder-name-update');
expect(component.description).toBe('folder-description-update');
});
it('should submit updated values if form is valid', () => {
spyOn(nodesApi, 'updateNode').and.returnValue(Observable.of({}));
component.form.controls['name'].setValue('folder-name-update');
component.form.controls['description'].setValue('folder-description-update');
component.submit();
expect(nodesApi.updateNode).toHaveBeenCalledWith(
'node-id',
{
name: 'folder-name-update',
properties: {
'cm:title': 'folder-name-update',
'cm:description': 'folder-description-update'
}
}
);
});
it('should call dialog to close with form data when submit is succesfluly', () => {
const folder = {
data: 'folder-data'
};
spyOn(nodesApi, 'updateNode').and.returnValue(Observable.of(folder));
component.submit();
expect(dialogRef.close).toHaveBeenCalledWith(folder);
});
it('should not submit if form is invalid', () => {
spyOn(nodesApi, 'updateNode');
component.form.controls['name'].setValue('');
component.form.controls['description'].setValue('');
component.submit();
expect(component.form.valid).toBe(false);
expect(nodesApi.updateNode).not.toHaveBeenCalled();
});
it('should not call dialog to close if submit fails', () => {
spyOn(nodesApi, 'updateNode').and.returnValue(Observable.throw('error'));
spyOn(component, 'handleError').and.callFake(val => val);
component.submit();
expect(component.handleError).toHaveBeenCalled();
expect(dialogRef.close).not.toHaveBeenCalled();
});
});
describe('Create', () => {
beforeEach(() => {
component.data = {
parentNodeId: 'parentNodeId',
folder: null
};
fixture.detectChanges();
});
it('should init form with empty inputs', () => {
expect(component.name).toBe('');
expect(component.description).toBe('');
});
it('should update form input', () => {
component.form.controls['name'].setValue('folder-name-update');
component.form.controls['description'].setValue('folder-description-update');
expect(component.name).toBe('folder-name-update');
expect(component.description).toBe('folder-description-update');
});
it('should submit updated values if form is valid', () => {
spyOn(nodesApi, 'createFolder').and.returnValue(Observable.of({}));
component.form.controls['name'].setValue('folder-name-update');
component.form.controls['description'].setValue('folder-description-update');
component.submit();
expect(nodesApi.createFolder).toHaveBeenCalledWith(
'parentNodeId',
{
name: 'folder-name-update',
properties: {
'cm:title': 'folder-name-update',
'cm:description': 'folder-description-update'
}
}
);
});
it('should call dialog to close with form data when submit is succesfluly', () => {
const folder = {
data: 'folder-data'
};
component.form.controls['name'].setValue('name');
component.form.controls['description'].setValue('description');
spyOn(nodesApi, 'createFolder').and.returnValue(Observable.of(folder));
component.submit();
expect(dialogRef.close).toHaveBeenCalledWith(folder);
});
it('should not submit if form is invalid', () => {
spyOn(nodesApi, 'createFolder');
component.form.controls['name'].setValue('');
component.form.controls['description'].setValue('');
component.submit();
expect(component.form.valid).toBe(false);
expect(nodesApi.createFolder).not.toHaveBeenCalled();
});
it('should not call dialog to close if submit fails', () => {
spyOn(nodesApi, 'createFolder').and.returnValue(Observable.throw('error'));
spyOn(component, 'handleError').and.callFake(val => val);
component.form.controls['name'].setValue('name');
component.form.controls['description'].setValue('description');
component.submit();
expect(component.handleError).toHaveBeenCalled();
expect(dialogRef.close).not.toHaveBeenCalled();
});
});
describe('handleError()', () => {
it('should raise error for 409', () => {
spyOn(notificationService, 'openSnackMessage').and.stub();
const error = {
message: '{ "error": { "statusCode" : 409 } }'
};
component.handleError(error);
expect(notificationService.openSnackMessage).toHaveBeenCalled();
expect(translationService.get).toHaveBeenCalledWith('CORE.MESSAGES.ERRORS.EXISTENT_FOLDER');
});
it('should raise generic error', () => {
spyOn(notificationService, 'openSnackMessage').and.stub();
const error = {
message: '{ "error": { "statusCode" : 123 } }'
};
component.handleError(error);
expect(notificationService.openSnackMessage).toHaveBeenCalled();
expect(translationService.get).toHaveBeenCalledWith('CORE.MESSAGES.ERRORS.GENERIC');
});
});
});

View File

@@ -0,0 +1,142 @@
/*!
* @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 { Observable } from 'rxjs/Rx';
import { Component, Inject, OnInit, Optional } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
import { NodesApiService } from '../services/nodes-api.service';
import { NotificationService } from '../services/notification.service';
import { TranslationService } from '../services/translation.service';
import { forbidEndingDot, forbidOnlySpaces, forbidSpecialCharacters } from './folder-name.validators';
@Component({
selector: 'adf-folder-dialog',
styleUrls: ['./folder.dialog.scss'],
templateUrl: './folder.dialog.html'
})
export class FolderDialogComponent implements OnInit {
form: FormGroup;
folder: MinimalNodeEntryEntity = null;
constructor(
private formBuilder: FormBuilder,
private dialog: MatDialogRef<FolderDialogComponent>,
private nodesApi: NodesApiService,
private translation: TranslationService,
private notification: NotificationService,
@Optional()
@Inject(MAT_DIALOG_DATA)
public data: any
) {}
get editing(): boolean {
return !!this.data.folder;
}
ngOnInit() {
const { folder } = this.data;
let name = '';
let description = '';
if (folder) {
const { properties } = folder;
name = folder.name || '';
description = properties ? properties['cm:description'] : '';
}
const validators = {
name: [
Validators.required,
forbidSpecialCharacters,
forbidEndingDot,
forbidOnlySpaces
]
};
this.form = this.formBuilder.group({
name: [ name, validators.name ],
description: [ description ]
});
}
get name(): string {
let { name } = this.form.value;
return (name || '').trim();
}
get description(): string {
let { description } = this.form.value;
return (description || '').trim();
}
private get properties(): any {
const { name: title, description } = this;
return {
'cm:title': title,
'cm:description': description
};
}
private create(): Observable<MinimalNodeEntryEntity> {
const { name, properties, nodesApi, data: { parentNodeId} } = this;
return nodesApi.createFolder(parentNodeId, { name, properties });
}
private edit(): Observable<MinimalNodeEntryEntity> {
const { name, properties, nodesApi, data: { folder: { id: nodeId }} } = this;
return nodesApi.updateNode(nodeId, { name, properties });
}
submit() {
const { form, dialog, editing } = this;
if (!form.valid) { return; }
(editing ? this.edit() : this.create())
.subscribe(
(folder: MinimalNodeEntryEntity) => dialog.close(folder),
(error) => this.handleError(error)
);
}
handleError(error: any): any {
let i18nMessageString = 'CORE.MESSAGES.ERRORS.GENERIC';
try {
const { error: { statusCode } } = JSON.parse(error.message);
if (statusCode === 409) {
i18nMessageString = 'CORE.MESSAGES.ERRORS.EXISTENT_FOLDER';
}
} catch (err) { /* Do nothing, keep the original message */ }
this.translation.get(i18nMessageString).subscribe(message => {
this.notification.openSnackMessage(message, 3000);
});
return error;
}
}

View File

@@ -0,0 +1,115 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { MatDialog, MatDialogModule } from '@angular/material';
import { By } from '@angular/platform-browser';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { Observable } from 'rxjs/Rx';
import { AppConfigService, providers } from '../../index';
import { AlfrescoContentService } from '../services/alfresco-content.service';
import { AlfrescoTranslateLoader } from '../services/translate-loader.service';
import { FolderCreateDirective } from './folder-create.directive';
@Component({
template: '<div [adf-create-folder]="parentNode"></div>'
})
class TestComponent {
parentNode = '';
}
describe('FolderCreateDirective', () => {
let fixture: ComponentFixture<TestComponent>;
let element;
let node: any;
let dialog: MatDialog;
let contentService: AlfrescoContentService;
let dialogRefMock;
const event: any = {
type: 'click',
preventDefault: () => null
};
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
HttpModule,
MatDialogModule,
FormsModule,
ReactiveFormsModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: AlfrescoTranslateLoader
}
})
],
declarations: [
TestComponent,
FolderCreateDirective
],
providers: [
AlfrescoContentService,
AppConfigService,
...providers()
]
});
TestBed.compileComponents();
fixture = TestBed.createComponent(TestComponent);
element = fixture.debugElement.query(By.directive(FolderCreateDirective));
dialog = TestBed.get(MatDialog);
contentService = TestBed.get(AlfrescoContentService);
});
beforeEach(() => {
node = { entry: { id: 'nodeId' } };
dialogRefMock = {
afterClosed: val => Observable.of(val)
};
spyOn(dialog, 'open').and.returnValue(dialogRefMock);
});
it('should emit folderCreate event when input value is not undefined', () => {
spyOn(dialogRefMock, 'afterClosed').and.returnValue(Observable.of(node));
contentService.folderCreate.subscribe((val) => {
expect(val).toBe(node);
});
element.triggerEventHandler('click', event);
fixture.detectChanges();
});
it('should not emit folderCreate event when input value is undefined', () => {
spyOn(dialogRefMock, 'afterClosed').and.returnValue(Observable.of(null));
spyOn(contentService.folderCreate, 'next');
element.triggerEventHandler('click', event);
fixture.detectChanges();
expect(contentService.folderCreate.next).not.toHaveBeenCalled();
});
});

View File

@@ -0,0 +1,68 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Directive, HostListener, Input } from '@angular/core';
import { MatDialog, MatDialogConfig } from '@angular/material';
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
import { FolderDialogComponent } from '../dialogs/folder.dialog';
import { AlfrescoContentService } from '../services/alfresco-content.service';
const DEFAULT_FOLDER_PARENT_ID = '-my-';
@Directive({
selector: '[adf-create-folder]'
})
export class FolderCreateDirective {
static DIALOG_WIDTH: number = 400;
@Input('adf-create-folder')
parentNodeId: string = DEFAULT_FOLDER_PARENT_ID;
@HostListener('click', [ '$event' ])
onClick(event) {
event.preventDefault();
this.openDialog();
}
constructor(
public dialogRef: MatDialog,
public content: AlfrescoContentService
) {}
private get dialogConfig(): MatDialogConfig {
const { DIALOG_WIDTH: width } = FolderCreateDirective;
const { parentNodeId } = this;
return {
data: { parentNodeId },
width: `${width}px`
};
}
private openDialog(): void {
const { dialogRef, dialogConfig, content } = this;
const dialogInstance = dialogRef.open(FolderDialogComponent, dialogConfig);
dialogInstance.afterClosed().subscribe((node: MinimalNodeEntryEntity) => {
if (node) {
content.folderCreate.next(node);
}
});
}
}

View File

@@ -0,0 +1,117 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HttpModule } from '@angular/http';
import { MatDialog, MatDialogModule } from '@angular/material';
import { By } from '@angular/platform-browser';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { Observable } from 'rxjs/Rx';
import { AppConfigService, providers } from '../../index';
import { AlfrescoContentService } from '../services/alfresco-content.service';
import { AlfrescoTranslateLoader } from '../services/translate-loader.service';
import { FolderEditDirective } from './folder-edit.directive';
@Component({
template: '<div [adf-edit-folder]="folder"></div>'
})
class TestComponent {
folder = {};
}
describe('FolderEditDirective', () => {
let fixture: ComponentFixture<TestComponent>;
let element;
let node: any;
let dialog: MatDialog;
let contentService: AlfrescoContentService;
let dialogRefMock;
const event = {
type: 'click',
preventDefault: () => null
};
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
HttpModule,
MatDialogModule,
FormsModule,
ReactiveFormsModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: AlfrescoTranslateLoader
}
})
],
declarations: [
TestComponent,
FolderEditDirective
]
,
providers: [
AlfrescoContentService,
AppConfigService,
...providers()
]
});
TestBed.compileComponents();
fixture = TestBed.createComponent(TestComponent);
element = fixture.debugElement.query(By.directive(FolderEditDirective));
dialog = TestBed.get(MatDialog);
contentService = TestBed.get(AlfrescoContentService);
});
beforeEach(() => {
node = { entry: { id: 'folderId' } };
dialogRefMock = {
afterClosed: val => Observable.of(val)
};
spyOn(dialog, 'open').and.returnValue(dialogRefMock);
});
it('should emit folderEdit event when input value is not undefined', () => {
spyOn(dialogRefMock, 'afterClosed').and.returnValue(Observable.of(node));
contentService.folderEdit.subscribe((val) => {
expect(val).toBe(node);
});
element.triggerEventHandler('click', event);
fixture.detectChanges();
});
it('should not emit folderEdit event when input value is undefined', () => {
spyOn(dialogRefMock, 'afterClosed').and.returnValue(Observable.of(null));
spyOn(contentService.folderEdit, 'next');
element.triggerEventHandler('click', event);
fixture.detectChanges();
expect(contentService.folderEdit.next).not.toHaveBeenCalled();
});
});

View File

@@ -0,0 +1,69 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
import { MatDialog, MatDialogConfig } from '@angular/material';
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
import { FolderDialogComponent } from '../dialogs/folder.dialog';
import { AlfrescoContentService } from '../services/alfresco-content.service';
@Directive({
selector: '[adf-edit-folder]'
})
export class FolderEditDirective {
static DIALOG_WIDTH: number = 400;
@Input('adf-edit-folder')
folder: MinimalNodeEntryEntity;
@HostListener('click', [ '$event' ])
onClick(event) {
event.preventDefault();
if (this.folder) {
this.openDialog();
}
}
constructor(
public dialogRef: MatDialog,
public elementRef: ElementRef,
public content: AlfrescoContentService
) {}
private get dialogConfig(): MatDialogConfig {
const { DIALOG_WIDTH: width } = FolderEditDirective;
const { folder } = this;
return {
data: { folder },
width: `${width}px`
};
}
private openDialog(): void {
const { dialogRef, dialogConfig, content } = this;
const dialogInstance = dialogRef.open(FolderDialogComponent, dialogConfig);
dialogInstance.afterClosed().subscribe((node: MinimalNodeEntryEntity) => {
if (node) {
content.folderEdit.next(node);
}
});
}
}

View File

@@ -4,7 +4,39 @@
"ITEMS_RANGE": "Angezeigt werden {{ range }} von {{ total }}",
"ITEMS_PER_PAGE": "Elemente pro Seite",
"CURRENT_PAGE": "Seite {{ number }}",
"TOTAL_PAGES": "von {{ total }}",
"TOTAL_PAGES": "von {{ total }}"
},
"FOLDER_DIALOG": {
"CREATE_FOLDER_TITLE": "Neuen Ordner erstellen",
"EDIT_FOLDER_TITLE": "Ordner bearbeiten",
"FOLDER_NAME": {
"LABEL": "Name",
"ERRORS": {
"REQUIRED": "Ordnername ist erforderlich",
"SPECIAL_CHARACTERS": "Ordnername darf die folgenden Zeichen nicht enthalten: * \" < > \\ / ? : |",
"ENDING_DOT": "Ordnername darf nicht mit einem Punkt enden.",
"ONLY_SPACES": "Ordnername darf nicht nur aus Leerzeichen bestehen."
}
},
"FOLDER_DESCRIPTION": {
"LABEL": "Beschreibung"
},
"CREATE_BUTTON": {
"LABEL": "Erstellen"
},
"UPDATE_BUTTON": {
"LABEL": "Aktualisieren"
},
"CANCEL_BUTTON": {
"LABEL": "Abbrechen"
}
},
"MESSAGES": {
"ERRORS": {
"GENERIC": "Die Aktion war nicht erfolgreich. Versuchen Sie es noch einmal oder wenden Sie sich an Ihr IT-Team.",
"EXISTENT_FOLDER": "Es gibt bereits einen Ordner mit diesem Namen. Probieren Sie es mit einem anderen Namen."
}
},
"HOST_SETTINGS": {
"TITLE": "Einstellungen",
"CS-HOST": "Konfiguration der Host-URL für Content Services",
@@ -14,4 +46,3 @@
}
}
}
}

View File

@@ -14,6 +14,37 @@
"TITLE": "Adding files to zip, this could take a few minutes"
}
},
"FOLDER_DIALOG": {
"CREATE_FOLDER_TITLE": "Create new folder",
"EDIT_FOLDER_TITLE": "Edit folder",
"FOLDER_NAME": {
"LABEL": "Name",
"ERRORS": {
"REQUIRED": "Folder name is required",
"SPECIAL_CHARACTERS": "Folder name can't contain these characters * \" < > \\ / ? : |",
"ENDING_DOT": "Folder name can't end with a period .",
"ONLY_SPACES": "Folder name can't contain only spaces"
}
},
"FOLDER_DESCRIPTION": {
"LABEL": "Description"
},
"CREATE_BUTTON": {
"LABEL": "Create"
},
"UPDATE_BUTTON": {
"LABEL": "Update"
},
"CANCEL_BUTTON": {
"LABEL": "Cancel"
}
},
"MESSAGES": {
"ERRORS": {
"GENERIC": "The action was unsuccessful. Try again or contact your IT Team.",
"EXISTENT_FOLDER": "There's already a folder with this name. Try a different name."
}
},
"RESTORE_NODE": {
"VIEW": "View",
"PARTIAL_PLURAL": "{{ number }} items not restored because of issues with the restore location",

View File

@@ -6,6 +6,37 @@
"CURRENT_PAGE": "Página {{ number }}",
"TOTAL_PAGES": "de {{ total }}"
},
"FOLDER_DIALOG": {
"CREATE_FOLDER_TITLE": "Crear nueva carpeta",
"EDIT_FOLDER_TITLE": "Editar carpeta",
"FOLDER_NAME": {
"LABEL": "Nombre",
"ERRORS": {
"REQUIRED": "Se necesita un nombre de carpeta",
"SPECIAL_CHARACTERS": "El nombre de la carpeta no puede tener los caracteres * \" < > \\ / ? : |",
"ENDING_DOT": "El nombre de la carpeta no puede terminar en punto .",
"ONLY_SPACES": "El nombre de la carpeta no puede tener solo espacios"
}
},
"FOLDER_DESCRIPTION": {
"LABEL": "Descripción"
},
"CREATE_BUTTON": {
"LABEL": "Crear"
},
"UPDATE_BUTTON": {
"LABEL": "Actualizar"
},
"CANCEL_BUTTON": {
"LABEL": "Cancelar"
}
},
"MESSAGES": {
"ERRORS": {
"GENERIC": "La acción no ha sido satisfactoria. Vuelva a intentarlo o póngase en contacto con el equipo de TI.",
"EXISTENT_FOLDER": "Ya existe una carpeta con este nombre. Pruebe un nombre diferente."
}
},
"HOST_SETTINGS": {
"TITLE": "Configuración",
"CS-HOST": "Configuración de URL de host de Content Services",

View File

@@ -6,12 +6,43 @@
"CURRENT_PAGE": "Page {{ number }}",
"TOTAL_PAGES": "sur {{ total }}"
},
"FOLDER_DIALOG": {
"CREATE_FOLDER_TITLE": "Créer un nouveau filtre",
"EDIT_FOLDER_TITLE": "Modifier le dossier",
"FOLDER_NAME": {
"LABEL": "Nom",
"ERRORS": {
"REQUIRED": "Nom de dossier requis",
"SPECIAL_CHARACTERS": "Un nom de dossier ne peut pas contenir les caractères * \" < > \\ / ? : |",
"ENDING_DOT": "Un nom de dossier ne peut pas se terminer par un point .",
"ONLY_SPACES": "Un nom de dossier ne peut pas contenir uniquement des espaces"
}
},
"FOLDER_DESCRIPTION": {
"LABEL": "Description"
},
"CREATE_BUTTON": {
"LABEL": "Créer"
},
"UPDATE_BUTTON": {
"LABEL": "Mis à jour"
},
"CANCEL_BUTTON": {
"LABEL": "Annuler"
}
},
"MESSAGES": {
"ERRORS": {
"GENERIC": "L'action a échoué. Réessayez ou contactez le service informatique.",
"EXISTENT_FOLDER": "Un dossier du même nom existe déjà. Essayez avec un nom différent."
}
},
"HOST_SETTINGS": {
"TITLE": "Paramètres",
"CS-HOST": "Configuration de l'URL de l'hôte de Content Services",
"BP-HOST": "Configuration de l'URL de l'hôte de Process Services",
"BACK": "Retour",
"APPLY": "APPLIQUER"
},
}
}
}

View File

@@ -6,6 +6,37 @@
"CURRENT_PAGE": "Pagina {{ number }}",
"TOTAL_PAGES": "di {{ total }}"
},
"FOLDER_DIALOG": {
"CREATE_FOLDER_TITLE": "Crea nuova cartella",
"EDIT_FOLDER_TITLE": "Modifica cartella",
"FOLDER_NAME": {
"LABEL": "Nome",
"ERRORS": {
"REQUIRED": "Nome cartella obbligatorio",
"SPECIAL_CHARACTERS": "Il nome della cartella non può contenere questi caratteri * \" < > \\ / ? : |",
"ENDING_DOT": "Il nome della cartella non può terminare con un punto.",
"ONLY_SPACES": "Il nome della cartella non può contenere solo spazi"
}
},
"FOLDER_DESCRIPTION": {
"LABEL": "Descrizione"
},
"CREATE_BUTTON": {
"LABEL": "Crea"
},
"UPDATE_BUTTON": {
"LABEL": "Aggiorna"
},
"CANCEL_BUTTON": {
"LABEL": "Annulla"
}
},
"MESSAGES": {
"ERRORS": {
"GENERIC": "Azione non eseguita correttamente. Riprovare o contattare il team IT.",
"EXISTENT_FOLDER": "Esiste già una cartella con questo nome. Provare un nome diverso."
}
},
"HOST_SETTINGS": {
"TITLE": "Impostazioni",
"CS-HOST": "Configurazione URL host Content Services",

View File

@@ -6,6 +6,37 @@
"CURRENT_PAGE": "ページ: {{ number }}",
"TOTAL_PAGES": "/ {{ total }}"
},
"FOLDER_DIALOG": {
"CREATE_FOLDER_TITLE": "新しいフォルダの作成",
"EDIT_FOLDER_TITLE": "フォルダの編集",
"FOLDER_NAME": {
"LABEL": "名前",
"ERRORS": {
"REQUIRED": "フォルダ名を指定してください",
"SPECIAL_CHARACTERS": "名前に次の文字を含めることはできません。 * \" < > \\ / ? : |",
"ENDING_DOT": "フォルダ名の最後にピリオド (.) を付けることはできません。",
"ONLY_SPACES": "フォルダ名にスペースだけを含めることはできません"
}
},
"FOLDER_DESCRIPTION": {
"LABEL": "説明"
},
"CREATE_BUTTON": {
"LABEL": "作成"
},
"UPDATE_BUTTON": {
"LABEL": "更新"
},
"CANCEL_BUTTON": {
"LABEL": "キャンセル"
}
},
"MESSAGES": {
"ERRORS": {
"GENERIC": "処理が失敗しました。もう一度操作をやり直すか、IT 担当者に連絡してください。",
"EXISTENT_FOLDER": "この名前のフォルダが既に存在します。別の名前を使用してください。"
}
},
"HOST_SETTINGS": {
"TITLE": "設定",
"CS-HOST": "Content Services ホストの URL の設定",

View File

@@ -6,6 +6,37 @@
"CURRENT_PAGE": "Side {{ number }}",
"TOTAL_PAGES": "av {{ total }}"
},
"FOLDER_DIALOG": {
"CREATE_FOLDER_TITLE": "Opprett ny mappe",
"EDIT_FOLDER_TITLE": "Rediger mappe",
"FOLDER_NAME": {
"LABEL": "Navn",
"ERRORS": {
"REQUIRED": "Mappenavn påkrevd",
"SPECIAL_CHARACTERS": "Mappenavn kan ikke inneholde disse tegnene * \" < > \\ / ? : |",
"ENDING_DOT": "Mappenavn kan ikke slutte med punktum .",
"ONLY_SPACES": "Mappenavn kan ikke inneholde bare mellomrom"
}
},
"FOLDER_DESCRIPTION": {
"LABEL": "Beskrivelse"
},
"CREATE_BUTTON": {
"LABEL": "Opprett"
},
"UPDATE_BUTTON": {
"LABEL": "Oppdater"
},
"CANCEL_BUTTON": {
"LABEL": "Avbryt"
}
},
"MESSAGES": {
"ERRORS": {
"GENERIC": "Handlingen var mislykket. Prøv på nytt, eller kontakt IT-teamet.",
"EXISTENT_FOLDER": "Det finnes allerede en mappe med det navnet, Prøv med et annet navn."
}
},
"HOST_SETTINGS": {
"TITLE": "Innstillinger",
"CS-HOST": "Innholdstjenestevert URL-konfigurasjon",

View File

@@ -6,6 +6,37 @@
"CURRENT_PAGE": "Pagina {{ number }}",
"TOTAL_PAGES": "van {{ total }}"
},
"FOLDER_DIALOG": {
"CREATE_FOLDER_TITLE": "Nieuwe map maken",
"EDIT_FOLDER_TITLE": "Map bewerken",
"FOLDER_NAME": {
"LABEL": "Naam",
"ERRORS": {
"REQUIRED": "Mapnaam is vereist",
"SPECIAL_CHARACTERS": "Mapnaam mag de volgende tekens niet bevatten: * \" < > \\ / ? : |",
"ENDING_DOT": "Mapnaam mag niet eindigen met een punt .",
"ONLY_SPACES": "Mapnaam mag niet alleen uit spaties bestaan"
}
},
"FOLDER_DESCRIPTION": {
"LABEL": "Beschrijving"
},
"CREATE_BUTTON": {
"LABEL": "Maken"
},
"UPDATE_BUTTON": {
"LABEL": "Bijwerken"
},
"CANCEL_BUTTON": {
"LABEL": "Annuleren"
}
},
"MESSAGES": {
"ERRORS": {
"GENERIC": "De actie is mislukt. Probeer het opnieuw of neem contact op met het IT-team.",
"EXISTENT_FOLDER": "Er bestaat al een map met deze naam. Probeer een andere naam."
}
},
"HOST_SETTINGS": {
"TITLE": "Instellingen",
"CS-HOST": "URL-configuratie Content Services-host",

View File

@@ -6,6 +6,37 @@
"CURRENT_PAGE": "Página {{ number }}",
"TOTAL_PAGES": "de {{ total }}"
},
"FOLDER_DIALOG": {
"CREATE_FOLDER_TITLE": "Criar nova pasta",
"EDIT_FOLDER_TITLE": "Editar pasta",
"FOLDER_NAME": {
"LABEL": "Nome",
"ERRORS": {
"REQUIRED": "O nome da pasta é obrigatório",
"SPECIAL_CHARACTERS": "O nome da pasta não pode conter estes caracteres * \" < > \\ / ? : |",
"ENDING_DOT": "O nome da pasta não pode terminar com ponto final .",
"ONLY_SPACES": "O nome da pasta não pode conter somente espaços"
}
},
"FOLDER_DESCRIPTION": {
"LABEL": "Descrição"
},
"CREATE_BUTTON": {
"LABEL": "Criar"
},
"UPDATE_BUTTON": {
"LABEL": "Atualizar"
},
"CANCEL_BUTTON": {
"LABEL": "Cancelar"
}
},
"MESSAGES": {
"ERRORS": {
"GENERIC": "A ação não teve êxito. Tente novamente ou entre em contato com a Equipe de TI.",
"EXISTENT_FOLDER": "Já existe uma pasta com este nome. Tente outro nome."
}
},
"HOST_SETTINGS": {
"TITLE": "Configurações",
"CS-HOST": "Configuração de URL de host do Content Services",

View File

@@ -6,6 +6,37 @@
"CURRENT_PAGE": "Страница {{ number }}",
"TOTAL_PAGES": "из {{ total }}"
},
"FOLDER_DIALOG": {
"CREATE_FOLDER_TITLE": "Создать новую папку",
"EDIT_FOLDER_TITLE": "Редактировать папку",
"FOLDER_NAME": {
"LABEL": "Имя",
"ERRORS": {
"REQUIRED": "Имя папки является обязательным",
"SPECIAL_CHARACTERS": "Имя папки не может содержать следующие символы * \" < > \\ / ? : |",
"ENDING_DOT": "Имя папки не может заканчиваться точкой .",
"ONLY_SPACES": "Имя папки не может состоять только из пробелов"
}
},
"FOLDER_DESCRIPTION": {
"LABEL": "Описание"
},
"CREATE_BUTTON": {
"LABEL": "Создать"
},
"UPDATE_BUTTON": {
"LABEL": "Обновить"
},
"CANCEL_BUTTON": {
"LABEL": "Отмена"
}
},
"MESSAGES": {
"ERRORS": {
"GENERIC": "Действие не выполнено. Повторите попытку или обратитесь к IT-специалистам.",
"EXISTENT_FOLDER": "Папка с таким же именем уже существует. Используйте другое имя."
}
},
"HOST_SETTINGS": {
"TITLE": "Параметры",
"CS-HOST": "Конфигурация URL-адреса хоста Content Services",

View File

@@ -6,6 +6,37 @@
"CURRENT_PAGE": "第 {{number}} 页",
"TOTAL_PAGES": "/共 {{total}} 页"
},
"FOLDER_DIALOG": {
"CREATE_FOLDER_TITLE": "新建文件夹",
"EDIT_FOLDER_TITLE": "编辑文件夹",
"FOLDER_NAME": {
"LABEL": "名称",
"ERRORS": {
"REQUIRED": "需要文件夹名称",
"SPECIAL_CHARACTERS": "文件夹名称不能包含 * \" < > \\ / ? : | 字符",
"ENDING_DOT": "文件夹名称不能以句点结尾。",
"ONLY_SPACES": "文件夹名称不能只包含空格"
}
},
"FOLDER_DESCRIPTION": {
"LABEL": "说明"
},
"CREATE_BUTTON": {
"LABEL": "创建"
},
"UPDATE_BUTTON": {
"LABEL": "更新"
},
"CANCEL_BUTTON": {
"LABEL": "取消"
}
},
"MESSAGES": {
"ERRORS": {
"GENERIC": "此操作未成功,请重试或联系您的 IT 团队.",
"EXISTENT_FOLDER": "存在使用此名称的文件夹。请尝试使用其他名称。"
}
},
"HOST_SETTINGS": {
"TITLE": "设置",
"CS-HOST": "Content Services 主机 URL 配置",

View File

@@ -16,7 +16,7 @@
*/
import { Injectable } from '@angular/core';
import { ContentApi } from 'alfresco-js-api';
import { ContentApi, MinimalNodeEntryEntity } from 'alfresco-js-api';
import { Observable, Subject } from 'rxjs/Rx';
import { FolderCreatedEvent } from '../events/folder-created.event';
import { PermissionsEnum } from '../models/permissions.enum';
@@ -28,6 +28,8 @@ import { LogService } from './log.service';
export class AlfrescoContentService {
folderCreated: Subject<FolderCreatedEvent> = new Subject<FolderCreatedEvent>();
folderCreate: Subject<MinimalNodeEntryEntity> = new Subject<MinimalNodeEntryEntity>();
folderEdit: Subject<MinimalNodeEntryEntity> = new Subject<MinimalNodeEntryEntity>();
constructor(public authService: AuthenticationService,
public apiService: AlfrescoApiService,