[ACA-4365] Expanded info drawer for permissions, properties and comments (#2080)

* [ACA-4365] Expanded info drawer for permissions, properties and comments

* Add unit tests for details component

* Fix linting

* Fix e2e tests

* Fix e2e tests

* Change to Progress Bar

* Add unit test for node effects

* Fix unit test

* Standardize styles info drawer

* Remove permissions tab

* Updadte ADF dependencies

* Update permissions icon

* Improve navigation

* Fix linting
This commit is contained in:
davidcanonieto 2021-04-28 09:30:07 +01:00 committed by GitHub
parent febaf6601a
commit cc6bb7e95b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 534 additions and 422 deletions

View File

@ -525,7 +525,7 @@
"id": "app.toolbar.permissions",
"order": 1200,
"title": "APP.ACTIONS.PERMISSIONS",
"icon": "settings_input_component",
"icon": "supervisor_account",
"actions": {
"click": "MANAGE_PERMISSIONS"
},
@ -801,7 +801,7 @@
{
"id": "app.context.menu.permissions",
"title": "APP.ACTIONS.PERMISSIONS",
"icon": "settings_input_component",
"icon": "supervisor_account",
"order": 1500,
"actions": {
"click": "MANAGE_PERMISSIONS"
@ -1062,7 +1062,7 @@
"id": "app.viewer.permissions",
"order": 1000,
"title": "APP.ACTIONS.PERMISSIONS",
"icon": "settings_input_component",
"icon": "supervisor_account",
"actions": {
"click": "MANAGE_PERMISSIONS"
},

View File

@ -525,7 +525,7 @@
"id": "app.toolbar.permissions",
"order": 1200,
"title": "APP.ACTIONS.PERMISSIONS",
"icon": "settings_input_component",
"icon": "supervisor_account",
"actions": {
"click": "MANAGE_PERMISSIONS"
},
@ -729,7 +729,7 @@
{
"id": "app.context.menu.permissions",
"title": "APP.ACTIONS.PERMISSIONS",
"icon": "settings_input_component",
"icon": "supervisor_account",
"order": 1500,
"actions": {
"click": "MANAGE_PERMISSIONS"
@ -990,7 +990,7 @@
"id": "app.viewer.permissions",
"order": 1000,
"title": "APP.ACTIONS.PERMISSIONS",
"icon": "settings_input_component",
"icon": "supervisor_account",
"actions": {
"click": "MANAGE_PERMISSIONS"
},

View File

@ -552,7 +552,7 @@
"id": "app.toolbar.permissions",
"order": 1200,
"title": "APP.ACTIONS.PERMISSIONS",
"icon": "settings_input_component",
"icon": "supervisor_account",
"actions": {
"click": "MANAGE_PERMISSIONS"
},
@ -756,7 +756,7 @@
{
"id": "app.context.menu.permissions",
"title": "APP.ACTIONS.PERMISSIONS",
"icon": "settings_input_component",
"icon": "supervisor_account",
"order": 1500,
"actions": {
"click": "MANAGE_PERMISSIONS"
@ -1017,7 +1017,7 @@
"id": "app.viewer.permissions",
"order": 1000,
"title": "APP.ACTIONS.PERMISSIONS",
"icon": "settings_input_component",
"icon": "supervisor_account",
"actions": {
"click": "MANAGE_PERMISSIONS"
},

View File

@ -525,7 +525,7 @@
"id": "app.toolbar.permissions",
"order": 1200,
"title": "APP.ACTIONS.PERMISSIONS",
"icon": "settings_input_component",
"icon": "supervisor_account",
"actions": {
"click": "MANAGE_PERMISSIONS"
},
@ -729,7 +729,7 @@
{
"id": "app.context.menu.permissions",
"title": "APP.ACTIONS.PERMISSIONS",
"icon": "settings_input_component",
"icon": "supervisor_account",
"order": 1500,
"actions": {
"click": "MANAGE_PERMISSIONS"
@ -990,7 +990,7 @@
"id": "app.viewer.permissions",
"order": 1000,
"title": "APP.ACTIONS.PERMISSIONS",
"icon": "settings_input_component",
"icon": "supervisor_account",
"actions": {
"click": "MANAGE_PERMISSIONS"
},

View File

@ -525,7 +525,7 @@
"id": "app.toolbar.permissions",
"order": 1200,
"title": "APP.ACTIONS.PERMISSIONS",
"icon": "settings_input_component",
"icon": "supervisor_account",
"actions": {
"click": "MANAGE_PERMISSIONS"
},
@ -729,7 +729,7 @@
{
"id": "app.context.menu.permissions",
"title": "APP.ACTIONS.PERMISSIONS",
"icon": "settings_input_component",
"icon": "supervisor_account",
"order": 1500,
"actions": {
"click": "MANAGE_PERMISSIONS"
@ -990,7 +990,7 @@
"id": "app.viewer.permissions",
"order": 1000,
"title": "APP.ACTIONS.PERMISSIONS",
"icon": "settings_input_component",
"icon": "supervisor_account",
"actions": {
"click": "MANAGE_PERMISSIONS"
},

View File

@ -525,7 +525,7 @@
"id": "app.toolbar.permissions",
"order": 1200,
"title": "APP.ACTIONS.PERMISSIONS",
"icon": "settings_input_component",
"icon": "supervisor_account",
"actions": {
"click": "MANAGE_PERMISSIONS"
},
@ -729,7 +729,7 @@
{
"id": "app.context.menu.permissions",
"title": "APP.ACTIONS.PERMISSIONS",
"icon": "settings_input_component",
"icon": "supervisor_account",
"order": 1500,
"actions": {
"click": "MANAGE_PERMISSIONS"
@ -990,7 +990,7 @@
"id": "app.viewer.permissions",
"order": 1000,
"title": "APP.ACTIONS.PERMISSIONS",
"icon": "settings_input_component",
"icon": "supervisor_account",
"actions": {
"click": "MANAGE_PERMISSIONS"
},

View File

@ -525,7 +525,7 @@
"id": "app.toolbar.permissions",
"order": 1200,
"title": "APP.ACTIONS.PERMISSIONS",
"icon": "settings_input_component",
"icon": "supervisor_account",
"actions": {
"click": "MANAGE_PERMISSIONS"
},
@ -729,7 +729,7 @@
{
"id": "app.context.menu.permissions",
"title": "APP.ACTIONS.PERMISSIONS",
"icon": "settings_input_component",
"icon": "supervisor_account",
"order": 1500,
"actions": {
"click": "MANAGE_PERMISSIONS"
@ -1016,7 +1016,7 @@
"order": 1000,
"title": "APP.ACTIONS.PERMISSIONS",
"disabled": true,
"icon": "settings_input_component",
"icon": "supervisor_account",
"actions": {
"click": "MANAGE_PERMISSIONS"
},

30
package-lock.json generated
View File

@ -5,9 +5,9 @@
"requires": true,
"dependencies": {
"@alfresco/adf-cli": {
"version": "4.4.0-32192",
"resolved": "https://registry.npmjs.org/@alfresco/adf-cli/-/adf-cli-4.4.0-32192.tgz",
"integrity": "sha512-weZMZzFlqp+bs/oKm9E4lRuBEON4gnaxEWogfCjtw9Fj6Ya1hlLZmUKIzhLUaLmy3544l+xUUUQ5A/TTj0l9Ig==",
"version": "4.4.0-32262",
"resolved": "https://registry.npmjs.org/@alfresco/adf-cli/-/adf-cli-4.4.0-32262.tgz",
"integrity": "sha512-VQf/Ovcp24Oc+OEPjR3SZJwEnxxV5qzwcnOBiEtp8XhDXbLgv7p08eRPCIPqIMkpEFUNEnu3HHoTtTLPggisXA==",
"dev": true,
"requires": {
"@alfresco/js-api": "4.4.0-3371",
@ -30,33 +30,33 @@
}
},
"@alfresco/adf-content-services": {
"version": "4.4.0-32192",
"resolved": "https://registry.npmjs.org/@alfresco/adf-content-services/-/adf-content-services-4.4.0-32192.tgz",
"integrity": "sha512-FM0qJQ0ltBMnpdGs0Mh1EVx/DJBqoWlGo6SZZUi2g4d1+19HN3PiYr3Uk/78EXqof0FPDQRrSm3hdufj78nC9g==",
"version": "4.4.0-32262",
"resolved": "https://registry.npmjs.org/@alfresco/adf-content-services/-/adf-content-services-4.4.0-32262.tgz",
"integrity": "sha512-ekycBJ9zPHrrKWyEz4OkqZmhYYdF79Qr6NglAWcaJxJg5ccewQIfpLr3mYXdoLGFJ98UfCR8EbPUZYEyK7R5tg==",
"requires": {
"tslib": "^2.0.0"
}
},
"@alfresco/adf-core": {
"version": "4.4.0-32192",
"resolved": "https://registry.npmjs.org/@alfresco/adf-core/-/adf-core-4.4.0-32192.tgz",
"integrity": "sha512-zQ3m5BVEAK4hC5pwy+DRpcV+KHIAkJrSXOxYh2eAaikKoiNxk5bD+xxKGzOae+SfO1hP+Y9kb8K/aoX2gfe1DA==",
"version": "4.4.0-32262",
"resolved": "https://registry.npmjs.org/@alfresco/adf-core/-/adf-core-4.4.0-32262.tgz",
"integrity": "sha512-CXOaGnRbPMliRcTNUeztNc4Bc0zHWdQ2vp1JucplMQY6vaDrmKXBXasFTunA9y3v0WR/YHTqDCP2r+5y5LYZTg==",
"requires": {
"tslib": "^2.0.0"
}
},
"@alfresco/adf-extensions": {
"version": "4.4.0-32192",
"resolved": "https://registry.npmjs.org/@alfresco/adf-extensions/-/adf-extensions-4.4.0-32192.tgz",
"integrity": "sha512-M4zu2GJPufv0hnm17qbR5YzwpDdIgcb+Q4DoNFH7bD2pDtceYJSvNJWc7m47HkNXZVGKq0p0i5ml6T5TEeahzw==",
"version": "4.4.0-32262",
"resolved": "https://registry.npmjs.org/@alfresco/adf-extensions/-/adf-extensions-4.4.0-32262.tgz",
"integrity": "sha512-1qpqaJCH2TI6t3ZkLPeEXYmR+7+Z+QQt3g22CYt+DsTti+TZBD7vHp1iV62IXGC4tqpcFLF56ktseOIsrZku8Q==",
"requires": {
"tslib": "^2.0.0"
}
},
"@alfresco/adf-testing": {
"version": "4.4.0-32192",
"resolved": "https://registry.npmjs.org/@alfresco/adf-testing/-/adf-testing-4.4.0-32192.tgz",
"integrity": "sha512-R258bcFpdFHJKdgk9b0B4CpvaAr8uXbH3iyhVQP1Me+nPkcjJ3okcrPDzBpwqwMbDaCGv3MZ91uWG/bNHZ/67A==",
"version": "4.4.0-32262",
"resolved": "https://registry.npmjs.org/@alfresco/adf-testing/-/adf-testing-4.4.0-32262.tgz",
"integrity": "sha512-3N3e8pIYH8BIexTELc6xCR8jnHNwpxtaBJBLbcz5LCeeq/YQmxtPzCne3KSFs62umBrG3K2icXhxVUGbGGc1Uw==",
"dev": true,
"requires": {
"tslib": "^2.0.0"

View File

@ -25,9 +25,9 @@
},
"private": true,
"dependencies": {
"@alfresco/adf-content-services": "4.4.0-32192",
"@alfresco/adf-core": "4.4.0-32192",
"@alfresco/adf-extensions": "4.4.0-32192",
"@alfresco/adf-content-services": "4.4.0-32262",
"@alfresco/adf-core": "4.4.0-32262",
"@alfresco/adf-extensions": "4.4.0-32262",
"@alfresco/js-api": "4.4.0-3371",
"@angular/animations": "10.0.4",
"@angular/cdk": "^10.0.2",
@ -57,8 +57,8 @@
"zone.js": "~0.10.2"
},
"devDependencies": {
"@alfresco/adf-cli": "4.4.0-32192",
"@alfresco/adf-testing": "4.4.0-32192",
"@alfresco/adf-cli": "4.4.0-32262",
"@alfresco/adf-testing": "4.4.0-32262",
"@angular-custom-builders/lite-serve": "^0.2.3",
"@angular-devkit/build-angular": "^0.1002.0",
"@angular-devkit/build-ng-packagr": "^0.1002.0",

View File

@ -46,7 +46,8 @@ export enum NodeActionTypes {
UnlockForWriting = 'UNLOCK_WRITE_LOCK',
AddFavorite = 'ADD_FAVORITE',
RemoveFavorite = 'REMOVE_FAVORITE',
ChangeAspects = 'ASPECT_LIST'
ChangeAspects = 'ASPECT_LIST',
ExpandInfoDrawer = 'EXPAND_INFO_DRAWER'
}
export class SetSelectedNodesAction implements Action {
@ -126,6 +127,11 @@ export class ManagePermissionsAction implements Action {
constructor(public payload: MinimalNodeEntity) {}
}
export class ExpandInfoDrawerAction implements Action {
readonly type = NodeActionTypes.ExpandInfoDrawer;
constructor(public payload: MinimalNodeEntity) {}
}
export class PrintFileAction implements Action {
readonly type = NodeActionTypes.PrintFile;

View File

@ -30,7 +30,8 @@ export enum RouterActionTypes {
NavigateUrl = 'NAVIGATE_URL',
NavigateRoute = 'NAVIGATE_ROUTE',
NavigateFolder = 'NAVIGATE_FOLDER',
NavigateParentFolder = 'NAVIGATE_PARENT_FOLDER'
NavigateParentFolder = 'NAVIGATE_PARENT_FOLDER',
NavigateToPreviousPage = 'NAVIGATE_TO_PREVIOUS_PAGE'
}
export class NavigateUrlAction implements Action {
@ -56,3 +57,9 @@ export class NavigateToParentFolder implements Action {
constructor(public payload: MinimalNodeEntity) {}
}
export class NavigateToPreviousPage implements Action {
readonly type = RouterActionTypes.NavigateToPreviousPage;
constructor() {}
}

View File

@ -30,12 +30,20 @@ import { MinimalNodeEntryEntity, PathInfoEntity } from '@alfresco/js-api';
import { map } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { AppStore } from '../states/app.state';
import { NavigateUrlAction, RouterActionTypes, NavigateRouteAction, NavigateToFolder, NavigateToParentFolder } from '../actions/router.actions';
import { Location } from '@angular/common';
import {
NavigateUrlAction,
RouterActionTypes,
NavigateRouteAction,
NavigateToFolder,
NavigateToParentFolder,
NavigateToPreviousPage
} from '../actions/router.actions';
import { SnackbarErrorAction } from '../actions/snackbar.actions';
@Injectable()
export class RouterEffects {
constructor(private store: Store<AppStore>, private actions$: Actions, private router: Router) {}
constructor(private store: Store<AppStore>, private actions$: Actions, private router: Router, private location: Location) {}
@Effect({ dispatch: false })
navigateUrl$ = this.actions$.pipe(
@ -75,6 +83,12 @@ export class RouterEffects {
})
);
@Effect({ dispatch: false })
navigateToPreviousPage$ = this.actions$.pipe(
ofType<NavigateToPreviousPage>(RouterActionTypes.NavigateToPreviousPage),
map(() => this.location.back())
);
private navigateToFolder(node: MinimalNodeEntryEntity) {
let link: any[] = null;
const { path, id } = node;

View File

@ -51,7 +51,6 @@ import { ExtensionsModule } from '@alfresco/adf-extensions';
import { AppToolbarModule } from './components/toolbar/toolbar.module';
import { AppCreateMenuModule } from './components/create-menu/create-menu.module';
import { AppSidenavModule } from './components/sidenav/sidenav.module';
import { AppPermissionsModule } from './components/permissions/permissions.module';
import { AppCommonModule } from './components/common/common.module';
import { AppLayoutModule } from './components/layout/layout.module';
import { AppSearchInputModule } from './components/search/search-input.module';
@ -65,6 +64,7 @@ import { RecentFilesComponent } from './components/recent-files/recent-files.com
import { SharedFilesComponent } from './components/shared-files/shared-files.component';
import { CreateFromTemplateDialogComponent } from './dialogs/node-template/create-from-template.dialog';
import { environment } from '../environments/environment';
import { DetailsComponent } from './components/details/details.component';
import { registerLocaleData } from '@angular/common';
import localeFr from '@angular/common/locales/fr';
@ -129,7 +129,6 @@ registerLocaleData(localeSv);
AppSidenavModule,
AppCreateMenuModule,
DocumentListCustomComponentsModule,
AppPermissionsModule,
AppSearchInputModule,
AppSearchResultsModule,
AppHeaderModule,
@ -139,6 +138,7 @@ registerLocaleData(localeSv);
declarations: [
AppComponent,
FilesComponent,
DetailsComponent,
LibrariesComponent,
FavoriteLibrariesComponent,
NodeVersionsDialogComponent,

View File

@ -36,6 +36,7 @@ import { AuthGuardEcm, BlankPageComponent } from '@alfresco/adf-core';
import { FavoritesComponent } from './components/favorites/favorites.component';
import { RecentFilesComponent } from './components/recent-files/recent-files.component';
import { SharedFilesComponent } from './components/shared-files/shared-files.component';
import { DetailsComponent } from './components/details/details.component';
export const APP_ROUTES: Routes = [
{
@ -91,6 +92,25 @@ export const APP_ROUTES: Routes = [
defaultNodeId: '-my-'
}
},
{
path: 'details/:nodeId',
children: [
{
path: '',
component: DetailsComponent,
data: {
navigateSource: 'personal-files'
}
},
{
path: ':activeTab',
component: DetailsComponent,
data: {
navigateSource: 'personal-files'
}
}
]
},
// deprecated, backwards compatibility with ACA 1.8
{
path: 'preview/:nodeId',

View File

@ -0,0 +1,59 @@
<aca-page-layout>
<aca-page-layout-header>
<adf-breadcrumb [root]="title"
[folderNode]="node"
(navigate)="goBack()">
</adf-breadcrumb>
<adf-toolbar class="adf-toolbar--inline">
<ng-container *ngFor="let entry of actions; trackBy: trackByActionId">
<aca-toolbar-action [actionRef]="entry"></aca-toolbar-action>
</ng-container>
</adf-toolbar>
</aca-page-layout-header>
<aca-page-layout-content>
<div class="acs-details-container">
<div class="acs-details-title">
<div class="acs-details-breadcrumb"
*ngIf="node">
<span class="acs-details-breadcrumb-library"> {{node.name}} </span>
-
<span class="acs-details-breadcrumb-item">{{'APP.INFO_DRAWER.TITLE' | translate}}</span>
</div>
<div class="acs-close-memebers-container">
<button mat-icon-button
data-automation-id="close-library"
(click)="goBack()">
<mat-icon>close</mat-icon>
</button>
</div>
</div>
<mat-tab-group [selectedIndex]="activeTab"
class="adw-details-tabs">
<mat-tab label="{{ 'APP.INFO_DRAWER.TABS.PROPERTIES' | translate }}">
<app-metadata-tab *ngIf="node && !isLoading; else loading"
[node]="node">
</app-metadata-tab>
</mat-tab>
<mat-tab label="{{ 'APP.INFO_DRAWER.TABS.COMMENTS' | translate }}">
<app-comments-tab *ngIf="node && !isLoading; else loading"
[node]="node">
</app-comments-tab>
</mat-tab>
<mat-tab label="{{ 'APP.INFO_DRAWER.TABS.PERMISSIONS' | translate }}">
<adf-permission-list *ngIf="node && !isLoading; else loading"
[nodeId]="node.id"></adf-permission-list>
</mat-tab>
</mat-tab-group>
</div>
</aca-page-layout-content>
</aca-page-layout>
<ng-template #loading>
<mat-progress-bar color="primary"
mode="indeterminate">
</mat-progress-bar>
</ng-template>

View File

@ -0,0 +1,45 @@
@mixin app-details-theme($theme) {
$background: map-get($theme, background);
.acs-details-container {
background-color: mat-color($background, card);
width: 100%;
}
.adw-details-tabs {
margin-top: 40px;
height: calc(100% - 80px);
.mat-tab-body-wrapper {
height: 100%;
}
.mat-tab-labels {
text-transform: uppercase;
}
}
.acs-details-title {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
.acs-close-memebers-container {
margin-right: 15px;
}
}
.acs-details-breadcrumb {
font-size: 18px;
margin-left: 20px;
.acs-details-breadcrumb-library {
font-weight: 900;
}
.acs-details-breadcrumb-item {
font-weight: 100;
}
}
}

View File

@ -0,0 +1,109 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2020 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AppTestingModule } from '../../testing/app-testing.module';
import { DetailsComponent } from './details.component';
import { MetadataTabComponent } from './../info-drawer/metadata-tab/metadata-tab.component';
import { CommentsTabComponent } from './../info-drawer/comments-tab/comments-tab.component';
import { ActivatedRoute, Router } from '@angular/router';
import { of, Subject } from 'rxjs';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { Store } from '@ngrx/store';
import { ContentManagementService } from '../../services/content-management.service';
import { AppExtensionService } from '@alfresco/adf-extensions';
import { ContentApiService } from '@alfresco/aca-shared';
import { SetSelectedNodesAction } from '@alfresco/aca-shared/store';
import { NodeEntry } from '@alfresco/js-api';
describe('DetailsComponent', () => {
let component: DetailsComponent;
let fixture: ComponentFixture<DetailsComponent>;
let contentApiService: ContentApiService;
let store;
const router: any = {
url: '',
navigate: jasmine.createSpy('navigate')
};
const mockStream = new Subject();
const storeMock = {
dispatch: jasmine.createSpy('dispatch'),
select: () => mockStream
};
beforeEach(() => {
TestBed.configureTestingModule({
imports: [AppTestingModule],
declarations: [DetailsComponent, CommentsTabComponent, MetadataTabComponent],
providers: [
ContentManagementService,
AppExtensionService,
{
provide: Router,
useValue: router
},
{ provide: Store, useValue: storeMock },
{
provide: ActivatedRoute,
useValue: {
snapshot: { data: { preferencePrefix: 'prefix' } },
params: of({ nodeId: 'someId', activeTab: 'permissions' })
}
}
],
schemas: [NO_ERRORS_SCHEMA]
});
fixture = TestBed.createComponent(DetailsComponent);
component = fixture.componentInstance;
contentApiService = TestBed.inject(ContentApiService);
store = TestBed.inject(Store);
spyOn(contentApiService, 'getNode').and.returnValue(of({ entry: { id: 'libraryId' } } as NodeEntry));
});
afterEach(() => {
fixture.destroy();
});
it('should get node id from router', () => {
fixture.detectChanges();
expect(component.nodeId).toBe('someId');
});
it('should set active tab from router', () => {
fixture.detectChanges();
expect(component.activeTab).toBe(2);
});
it('should get node info after setting node from router', () => {
fixture.detectChanges();
expect(contentApiService.getNode).toHaveBeenCalled();
});
it('should dispatch node selection', () => {
fixture.detectChanges();
expect(store.dispatch).toHaveBeenCalledWith(new SetSelectedNodesAction([{ entry: { id: 'libraryId' } } as NodeEntry]));
});
});

View File

@ -0,0 +1,99 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2020 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { Component, OnInit, ViewEncapsulation, OnDestroy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { PageComponent } from '../page.component';
import { AppExtensionService, ContentApiService } from '@alfresco/aca-shared';
import { AppStore, NavigateToPreviousPage, SetSelectedNodesAction } from '@alfresco/aca-shared/store';
import { Store } from '@ngrx/store';
import { ContentManagementService } from '../../services/content-management.service';
import { Subject } from 'rxjs';
@Component({
selector: 'app-details-manager',
templateUrl: './details.component.html',
styleUrls: ['details.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class DetailsComponent extends PageComponent implements OnInit, OnDestroy {
nodeId: string;
isLoading: boolean;
onDestroy$ = new Subject<boolean>();
activeTab = 1;
constructor(
private route: ActivatedRoute,
private contentApi: ContentApiService,
store: Store<AppStore>,
content: ContentManagementService,
extensions: AppExtensionService
) {
super(store, extensions, content);
}
ngOnInit(): void {
super.ngOnInit();
this.isLoading = true;
const { route } = this;
const { data } = route.snapshot;
this.title = data.title;
this.route.params.subscribe((params) => {
this.isLoading = true;
this.setActiveTab(params.activeTab);
this.nodeId = params.nodeId;
this.contentApi.getNode(this.nodeId).subscribe((node) => {
this.node = node.entry;
this.isLoading = false;
this.store.dispatch(new SetSelectedNodesAction([{ entry: this.node }]));
});
});
}
setActiveTab(tabName: string) {
switch (tabName) {
case 'comments':
this.activeTab = 1;
break;
case 'permissions':
this.activeTab = 2;
break;
case 'metadata':
default:
this.activeTab = 0;
}
}
goBack() {
this.store.dispatch(new NavigateToPreviousPage());
}
ngOnDestroy(): void {
this.store.dispatch(new SetSelectedNodesAction([]));
this.onDestroy$.next();
this.onDestroy$.complete();
}
}

View File

@ -29,7 +29,7 @@ import { NodePermissionService, isLocked } from '@alfresco/aca-shared';
@Component({
selector: 'app-comments-tab',
template: `<adf-comments [readOnly]="!canUpdateNode" [nodeId]="node?.id"></adf-comments>`
template: `<mat-card><adf-comments [readOnly]="!canUpdateNode" [nodeId]="node?.id"></adf-comments></mat-card>`
})
export class CommentsTabComponent {
@Input()

View File

@ -23,7 +23,7 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { ContentMetadataModule, VersionManagerModule } from '@alfresco/adf-content-services';
import { ContentMetadataModule, ContentModule, VersionManagerModule } from '@alfresco/adf-content-services';
import { CoreModule } from '@alfresco/adf-core';
import { ExtensionsModule } from '@alfresco/adf-extensions';
import { CommonModule } from '@angular/common';
@ -47,6 +47,7 @@ export function components() {
CommonModule,
MaterialModule,
CoreModule.forChild(),
ContentModule.forChild(),
ExtensionsModule,
ContentMetadataModule,
VersionManagerModule,

View File

@ -27,7 +27,7 @@ import { Component, Input } from '@angular/core';
import { SiteEntry } from '@alfresco/js-api';
@Component({
selector: 'app-metadata-tab',
selector: 'app-library-metadata-tab',
template: '<app-library-metadata-form [node]="node"></app-library-metadata-form>',
host: { class: 'app-metadata-tab' }
})

View File

@ -1,11 +0,0 @@
<header role="heading" aria-level="2" mat-dialog-title>
{{'PERMISSIONS.DIALOG.TITLE' | translate}}
</header>
<section mat-dialog-content>
<app-permission-manager [nodeId]="nodeId"></app-permission-manager>
</section>
<footer mat-dialog-actions>
<button mat-button color="primary" [mat-dialog-close]="true" cdkFocusInitial>
{{'PERMISSIONS.DIALOG.CLOSE' | translate}}
</button>
</footer>

View File

@ -1,32 +0,0 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2020 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { NodePermissionsDialogComponent } from './node-permissions.dialog';
describe('NodePermissionsDialogComponent', () => {
it('should be defined', () => {
expect(NodePermissionsDialogComponent).toBeDefined();
});
});

View File

@ -1,40 +0,0 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2020 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { Component, Inject, ViewEncapsulation } from '@angular/core';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
@Component({
templateUrl: './node-permissions.dialog.html',
encapsulation: ViewEncapsulation.None,
host: { class: 'aca-node-permissions-dialog' }
})
export class NodePermissionsDialogComponent {
nodeId: string;
constructor(@Inject(MAT_DIALOG_DATA) data: any) {
this.nodeId = data.nodeId;
}
}

View File

@ -1,27 +0,0 @@
<div class="inherit_permission_button">
<button
mat-raised-button
adf-inherit-permission
[nodeId]="nodeId"
[color]="toggleStatus ? 'accent' : 'primary'"
(error)="onError($event)"
(updated)="onUpdatedPermissions($event)"
>
{{
(toggleStatus
? 'PERMISSIONS.DIALOG.INHERITED_PERMISSIONS_BUTTON'
: 'PERMISSIONS.DIALOG.INHERIT_PERMISSIONS_BUTTON') | translate
}}
</button>
<button mat-button (click)="openAddPermissionDialog()">
{{ 'PERMISSIONS.DIALOG.ADD_USER_OR_GROUP' | translate }}
</button>
</div>
<adf-permission-list
#permissionList
[nodeId]="nodeId"
(error)="onError($event)"
(update)="onUpdate()"
>
</adf-permission-list>

View File

@ -1,40 +0,0 @@
aca-permissions-dialog-panel {
height: 400px;
}
.aca-node-permissions-dialog {
.mat-dialog-title {
font-size: 20px;
font-weight: 600;
font-style: normal;
font-stretch: normal;
line-height: 1.6;
margin: 0;
letter-spacing: -0.5px;
color: var(--theme-foreground-text-bold-color);
}
.mat-dialog-content {
flex: 1 1 auto;
position: relative;
overflow: auto;
adf-permission-list {
display: flex;
}
}
.mat-dialog-actions {
flex: 0 0 auto;
padding: 8px 8px 24px 8px;
display: flex;
justify-content: flex-end;
color: var(--theme-text-color);
button {
text-transform: uppercase;
font-weight: normal;
}
}
}

View File

@ -1,32 +0,0 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2020 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { PermissionsManagerComponent } from './permission-manager.component';
describe('PermissionsManagerComponent', () => {
it('should be defined', () => {
expect(PermissionsManagerComponent).toBeDefined();
});
});

View File

@ -1,90 +0,0 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2020 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { AppStore, SnackbarErrorAction } from '@alfresco/aca-shared/store';
import { NodePermissionDialogService, PermissionListComponent } from '@alfresco/adf-content-services';
import { MinimalNodeEntryEntity } from '@alfresco/js-api';
import { Component, Input, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { Store } from '@ngrx/store';
import { ContentApiService } from '@alfresco/aca-shared';
import { NodePermissionsDialogComponent } from '../permission-dialog/node-permissions.dialog';
@Component({
selector: 'app-permission-manager',
templateUrl: './permission-manager.component.html',
styleUrls: ['permission-manager.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class PermissionsManagerComponent implements OnInit {
@ViewChild('permissionList', { static: true })
permissionList: PermissionListComponent;
@Input()
nodeId: string;
toggleStatus = false;
constructor(
private store: Store<AppStore>,
private dialog: MatDialog,
private contentApi: ContentApiService,
private nodePermissionDialogService: NodePermissionDialogService
) {}
ngOnInit() {
this.contentApi.getNodeInfo(this.nodeId, { include: ['permissions'] }).subscribe((currentNode: MinimalNodeEntryEntity) => {
this.toggleStatus = currentNode.permissions.isInheritanceEnabled;
});
}
onError(errorMessage: string) {
this.store.dispatch(new SnackbarErrorAction(errorMessage));
}
onUpdate() {
this.permissionList.reload();
}
onUpdatedPermissions(node: MinimalNodeEntryEntity) {
this.toggleStatus = node.permissions.isInheritanceEnabled;
this.permissionList.reload();
}
openAddPermissionDialog() {
this.nodePermissionDialogService.updateNodePermissionByDialog(this.nodeId).subscribe(
() => {
this.dialog.open(NodePermissionsDialogComponent, {
data: { nodeId: this.nodeId },
panelClass: 'aca-permissions-dialog-panel',
width: '800px'
});
},
(error) => {
this.store.dispatch(new SnackbarErrorAction(error));
}
);
}
}

View File

@ -1,38 +0,0 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2020 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CoreModule } from '@alfresco/adf-core';
import { ContentModule } from '@alfresco/adf-content-services';
import { NodePermissionsDialogComponent } from './permission-dialog/node-permissions.dialog';
import { PermissionsManagerComponent } from './permission-manager/permission-manager.component';
@NgModule({
imports: [CommonModule, CoreModule.forChild(), ContentModule.forChild()],
declarations: [PermissionsManagerComponent, NodePermissionsDialogComponent],
exports: [PermissionsManagerComponent, NodePermissionsDialogComponent]
})
export class AppPermissionsModule {}

View File

@ -66,7 +66,6 @@ import { MatSnackBar } from '@angular/material/snack-bar';
import { Store } from '@ngrx/store';
import { forkJoin, Observable, of, Subject, zip } from 'rxjs';
import { catchError, map, mergeMap, take, tap } from 'rxjs/operators';
import { NodePermissionsDialogComponent } from '../components/permissions/permission-dialog/node-permissions.dialog';
import { NodeVersionsDialogComponent } from '../dialogs/node-versions/node-versions.dialog';
import { NodeActionsService } from './node-actions.service';
@ -133,24 +132,6 @@ export class ContentManagementService {
}
}
managePermissions(node: MinimalNodeEntity): void {
if (node && node.entry) {
const { nodeId, id } = node.entry as any;
const siteId = node.entry['guid'];
const targetId = siteId || nodeId || id;
if (targetId) {
this.dialogRef.open(NodePermissionsDialogComponent, {
data: { nodeId: targetId },
panelClass: 'aca-permissions-dialog-panel',
width: '730px'
});
} else {
this.store.dispatch(new SnackbarErrorAction('APP.MESSAGES.ERRORS.PERMISSION'));
}
}
}
manageVersions(node: any) {
if (node && node.entry) {
// shared and favorite

View File

@ -42,21 +42,24 @@ import {
EditFolderAction,
CopyNodesAction,
MoveNodesAction,
ManagePermissionsAction,
UnlockWriteAction,
FullscreenViewerAction,
PrintFileAction,
SetCurrentFolderAction,
ManageAspectsAction
ManageAspectsAction,
ManagePermissionsAction
} from '@alfresco/aca-shared/store';
import { ViewUtilService } from '@alfresco/adf-core';
import { ViewerEffects } from './viewer.effects';
import { Router } from '@angular/router';
import { of } from 'rxjs';
describe('NodeEffects', () => {
let store: Store<any>;
let contentService: ContentManagementService;
let viewUtilService: ViewUtilService;
let viewerEffects: ViewerEffects;
let router: Router;
beforeEach(() => {
TestBed.configureTestingModule({
@ -68,6 +71,7 @@ describe('NodeEffects', () => {
contentService = TestBed.inject(ContentManagementService);
viewUtilService = TestBed.inject(ViewUtilService);
viewerEffects = TestBed.inject(ViewerEffects);
router = TestBed.inject(Router);
});
describe('shareNode$', () => {
@ -366,33 +370,27 @@ describe('NodeEffects', () => {
describe('managePermissions$', () => {
it('should manage permissions from the payload', () => {
spyOn(contentService, 'managePermissions').and.stub();
const node: any = { entry: { isFile: true } };
spyOn(router, 'navigate').and.stub();
const node: any = { entry: { isFile: true, id: 'fileId' } };
store.dispatch(new ManagePermissionsAction(node));
expect(contentService.managePermissions).toHaveBeenCalledWith(node);
expect(router.navigate).toHaveBeenCalledWith(['personal-files/details', 'fileId', 'permissions']);
});
it('should manage permissions from the active selection', fakeAsync(() => {
spyOn(contentService, 'managePermissions').and.stub();
const node: any = { entry: { isFile: true } };
store.dispatch(new SetSelectedNodesAction([node]));
tick(100);
it('should manage permissions from the active selection', () => {
spyOn(store, 'select').and.returnValue(of({ isEmpty: false, first: { entry: { id: 'fileId' } } }));
spyOn(router, 'navigate').and.stub();
store.dispatch(new ManagePermissionsAction(null));
expect(contentService.managePermissions).toHaveBeenCalledWith(node);
}));
expect(router.navigate).toHaveBeenCalledWith(['personal-files/details', 'fileId', 'permissions']);
});
it('should do nothing if invoking manage permissions with no data', () => {
spyOn(contentService, 'managePermissions').and.stub();
spyOn(store, 'select').and.returnValue(of(null));
spyOn(router, 'navigate').and.stub();
store.dispatch(new ManagePermissionsAction(null));
expect(contentService.managePermissions).not.toHaveBeenCalled();
expect(router.navigate).not.toHaveBeenCalled();
});
});

View File

@ -46,7 +46,9 @@ import {
PrintFileAction,
getCurrentFolder,
getAppSelection,
ManageAspectsAction
ManageAspectsAction,
NavigateRouteAction,
ExpandInfoDrawerAction
} from '@alfresco/aca-shared/store';
import { ContentManagementService } from '../../services/content-management.service';
import { ViewUtilService } from '@alfresco/adf-core';
@ -246,14 +248,37 @@ export class NodeEffects {
ofType<ManagePermissionsAction>(NodeActionTypes.ManagePermissions),
map((action) => {
if (action && action.payload) {
this.contentService.managePermissions(action.payload);
const route = 'personal-files/details';
this.store.dispatch(new NavigateRouteAction([route, action.payload.entry.id, 'permissions']));
} else {
this.store
.select(getAppSelection)
.pipe(take(1))
.subscribe((selection) => {
if (selection && !selection.isEmpty) {
this.contentService.managePermissions(selection.first);
const route = 'personal-files/details';
this.store.dispatch(new NavigateRouteAction([route, selection.first.entry.id, 'permissions']));
}
});
}
})
);
@Effect({ dispatch: false })
expandInfoDrawer$ = this.actions$.pipe(
ofType<ExpandInfoDrawerAction>(NodeActionTypes.ExpandInfoDrawer),
map((action) => {
if (action && action.payload) {
const route = 'personal-files/details';
this.store.dispatch(new NavigateRouteAction([route, action.payload.entry.id]));
} else {
this.store
.select(getAppSelection)
.pipe(take(1))
.subscribe((selection) => {
if (selection && !selection.isEmpty) {
const route = 'personal-files/details';
this.store.dispatch(new NavigateRouteAction([route, selection.first.entry.id]));
}
});
}

View File

@ -5,6 +5,7 @@
@import '../components/search/search-input/search-input.component.theme';
@import '../components/create-menu/create-menu.component.scss';
@import '../dialogs/node-template/create-from-template.dialog.scss';
@import '../components/details/details.component.scss';
@import './overrides/adf-style-fixes.theme';
@ -30,6 +31,7 @@ $primary: map-get($custom-theme, primary);
@include aca-search-input-theme($theme);
@include sidenav-component-theme($theme);
@include app-create-from-template-theme($theme);
@include app-details-theme($theme);
@include app-create-menu-theme($theme);
@include adf-style-fixes($theme);

View File

@ -9,40 +9,74 @@
"$description": "Core application extensions and features",
"$references": [],
"$ignoreReferenceList": [],
"rules": [
{
"id": "app.toolbar.favorite.canAdd",
"type": "core.every",
"parameters": [
{ "type": "rule", "value": "app.selection.canAddFavorite" },
{ "type": "rule", "value": "app.navigation.isNotRecentFiles" },
{ "type": "rule", "value": "app.navigation.isNotSharedFiles" },
{ "type": "rule", "value": "app.navigation.isNotSearchResults" },
{ "type": "rule", "value": "app.navigation.isNotFavorites" }
{
"type": "rule",
"value": "app.selection.canAddFavorite"
},
{
"type": "rule",
"value": "app.navigation.isNotRecentFiles"
},
{
"type": "rule",
"value": "app.navigation.isNotSharedFiles"
},
{
"type": "rule",
"value": "app.navigation.isNotSearchResults"
},
{
"type": "rule",
"value": "app.navigation.isNotFavorites"
}
]
},
{
"id": "app.toolbar.favorite.canRemove",
"type": "core.every",
"parameters": [
{ "type": "rule", "value": "app.selection.canRemoveFavorite" },
{ "type": "rule", "value": "app.navigation.isNotRecentFiles" },
{ "type": "rule", "value": "app.navigation.isNotSharedFiles" },
{ "type": "rule", "value": "app.navigation.isNotSearchResults" },
{ "type": "rule", "value": "app.navigation.isNotFavorites" }
{
"type": "rule",
"value": "app.selection.canRemoveFavorite"
},
{
"type": "rule",
"value": "app.navigation.isNotRecentFiles"
},
{
"type": "rule",
"value": "app.navigation.isNotSharedFiles"
},
{
"type": "rule",
"value": "app.navigation.isNotSearchResults"
},
{
"type": "rule",
"value": "app.navigation.isNotFavorites"
}
]
},
{
"id": "app.toolbar.favorite.canEditMetadata",
"type": "core.every",
"parameters": [
{ "type": "rule", "value": "app.selection.library" },
{ "type": "rule", "value": "isLibraryManager" }
{
"type": "rule",
"value": "app.selection.library"
},
{
"type": "rule",
"value": "isLibraryManager"
}
]
}
],
"features": {
"header": [
{
@ -543,7 +577,7 @@
"id": "app.toolbar.permissions",
"order": 1200,
"title": "APP.ACTIONS.PERMISSIONS",
"icon": "settings_input_component",
"icon": "supervisor_account",
"actions": {
"click": "MANAGE_PERMISSIONS"
},
@ -759,7 +793,7 @@
{
"id": "app.context.menu.permissions",
"title": "APP.ACTIONS.PERMISSIONS",
"icon": "settings_input_component",
"icon": "supervisor_account",
"order": 1500,
"actions": {
"click": "MANAGE_PERMISSIONS"
@ -1003,7 +1037,6 @@
"type": "separator",
"order": 800
},
{
"id": "app.viewer.versions",
"order": 900,
@ -1032,7 +1065,7 @@
"id": "app.viewer.permissions",
"order": 1000,
"title": "APP.ACTIONS.PERMISSIONS",
"icon": "settings_input_component",
"icon": "supervisor_account",
"actions": {
"click": "MANAGE_PERMISSIONS"
},
@ -1081,6 +1114,20 @@
]
},
"sidebar": {
"toolbar": [
{
"id": "app.sidebar.expand",
"order": 100,
"title": "APP.INFO_DRAWER.TABS.EXPAND",
"icon": "chevron_left",
"actions": {
"click": "EXPAND_INFO_DRAWER"
},
"rules": {
"visible": "app.navigation.isNotLibraries"
}
}
],
"tabs": [
{
"id": "app.sidebar.properties",
@ -1141,10 +1188,8 @@
"dp:restrictable",
"fm:commentsRollup",
"qshare:shared",
"exif:exif",
"cm:effectivity",
"cm:likesRatingSchemeRollups",
"cm:lockable",
"cm:ownable"

View File

@ -51,6 +51,9 @@
"PREVIEW": {
"TITLE": "Preview"
},
"DETAILS": {
"TITLE": "Details"
},
"NEW_MENU": {
"LABEL": "New",
"TOOLTIP": "Add new files and folders, or create a new File Library",
@ -353,7 +356,9 @@
"PROPERTIES": "Properties",
"LIBRARY_PROPERTIES": "About",
"VERSIONS": "Versions",
"COMMENTS": "Comments"
"COMMENTS": "Comments",
"PERMISSIONS": "Permissions",
"EXPAND": "Expand"
}
}
},
@ -428,6 +433,12 @@
"GENERIC": "There was an error versioning the file"
}
},
"INFO_PANEL": {
"TABS": {
},
"DETAILS": "Details"
},
"LIBRARY": {
"DIALOG": {
"CREATE_TITLE": "Create Library",