Commit 3e78f924 authored by Matija Obreza's avatar Matija Obreza
Browse files

Admin: File repository basics

- Navigate
- Upload files
- Delete folders
parent ac5004aa
import RepositoryFile from 'model/repository/RepositoryFile';
import RepositoryFolder from 'model/repository/RepositoryFolder';
import ImageGallery from 'model/repository/ImageGallery';
/*
* Defined in Swagger as '#/definitions/FolderDetails'
*/
class FolderDetails {
public files: RepositoryFile[];
public folder: RepositoryFolder;
public subFolders: RepositoryFolder[];
public gallery: ImageGallery;
}
export default FolderDetails;
import RepositoryFolder from 'model/repository/RepositoryFolder';
import RepositoryImage from 'model/repository/RepositoryImage';
import { Permissions, IUserPermissions } from 'model/acl.model';
/*
* Defined in Swagger as '#/definitions/ImageGallery'
*/
class ImageGallery implements IUserPermissions {
public _permissions: Permissions;
public clazz: string = 'org.genesys.filerepository.model.ImageGallery';
public active: boolean;
public createdBy: number;
public createdDate: Date;
public description: string;
public folder: RepositoryFolder;
public id: number;
public images: RepositoryImage[];
public lastModifiedBy: number;
public lastModifiedDate: Date;
public title: string;
public version: number;
}
export default ImageGallery;
import RepositoryFolder from 'model/repository/RepositoryFolder';
import { Permissions, IUserPermissions } from 'model/acl.model';
/*
* Defined in Swagger as '#/definitions/RepositoryFile'
*/
class RepositoryFile implements IUserPermissions {
public _permissions: Permissions;
public clazz: string = 'org.genesys.filerepository.model.RepositoryFile';
public accessRights: string;
public active: boolean;
public bibliographicCitation: string;
public contentType: string;
public created: string;
public createdBy: number;
public createdDate: Date;
public creator: string;
public dateRetrieved: Date;
public dateSubmitted: Date;
public description: string;
public extension: string;
public extent: string;
public filename: string;
public folder: RepositoryFolder;
public format: string;
public id: number;
public identifier: string;
public lastModifiedBy: number;
public lastModifiedDate: Date;
public license: string;
public md5Sum: string;
public metadataFilename: string;
public modified: Date;
public originalFilename: string;
public originalUrl: string;
public rightsHolder: string;
public sha1Sum: string;
public size: number;
public storageFolder: string;
public storagePath: string;
public subject: string;
public title: string;
public uuid: string;
public version: number;
}
export default RepositoryFile;
import { IUserPermissions, Permissions } from 'model/acl.model';
/*
* Defined in Swagger as '#/definitions/RepositoryFolder'
*/
class RepositoryFolder implements IUserPermissions {
public _permissions: Permissions;
public static clazz: string = 'org.genesys.filerepository.model.RepositoryFolder';
public active: boolean;
public children: string[]; // uuids
public createdBy: number;
public createdDate: Date;
public description: string;
public id: number;
public lastModifiedBy: number;
public lastModifiedDate: Date;
public name: string;
public path: string;
public title: string;
public uuid: string;
public version: number;
}
export default RepositoryFolder;
import RepositoryFile from 'model/repository/RepositoryFile';
/*
* Defined in Swagger as '#/definitions/RepositoryImage'
*/
class RepositoryImage extends RepositoryFile {
public format: string;
public height: number;
public orientation: string;
public thumbnailPath: string;
public width: number;
}
export default RepositoryImage;
import { UuidModel } from 'model/common.model';
class RepositoryFile extends UuidModel {
public title: string;
public description: string;
public path: string;
public originalFilename: string;
public contentType: string;
public size: number;
public format: string;
public constructor(obj?) {
super(obj);
}
public getClassname(): string {
return 'org.genesys.filerepository.model.RepositoryFile';
}
}
export { RepositoryFile };
......@@ -14,25 +14,30 @@ import institutes from './institute';
import applicationConfig from './applicationConfig';
import crop from 'crop/reducers';
import user from 'user/reducers';
import repository from 'repository/reducers';
import requests from './requests';
const rootReducer = combineReducers({
login,
serverInfo,
routing: routerReducer,
form: formReducer,
appMounted,
history,
login,
serverInfo,
pageTitle,
applicationConfig,
snackbar,
filterCode,
routing: routerReducer,
form: formReducer,
crop,
user,
repository,
subsets,
accessions,
institutes,
applicationConfig,
crop,
requests,
user,
});
export default rootReducer;
import { normalize } from 'path';
// Constants
import { RECEIVE_FOLDER_DETAILS } from 'repository/constants';
// Model
import FolderDetails from 'model/repository/FolderDetails';
// Service
import RepositoryService from 'service/genesys/RepositoryService';
// Util
import { log } from 'utilities/debug';
import RepositoryFile from 'model/repository/RepositoryFile';
import RepositoryFolder from 'model/repository/RepositoryFolder';
const receiveFolder = (folder: FolderDetails, error = null) => ({
type: RECEIVE_FOLDER_DETAILS,
payload: { folder, error },
});
// Get folder details
export const getFolder = (path: string = '/') => (dispatch, getState) => {
return RepositoryService.getFolder(getState().login.access_token, normalize(path))
.then((folder: FolderDetails) => {
dispatch(receiveFolder(folder));
})
.catch((error) => {
log('Error', error);
});
};
// Get folder details
export const deleteFolder = (path: string) => (dispatch, getState) => {
return RepositoryService.deleteFolder(getState().login.access_token, normalize(path))
.then((folder: RepositoryFolder) => {
console.log(`Folder deleted ${path}`, folder);
return true;
})
.catch((error) => {
log('Error', error);
return false;
});
};
// Upload file
export const uploadFile = (path: string = '/', file: File) => (dispatch, getState) => {
return RepositoryService.uploadFile(getState().login.access_token, normalize(path), file)
.then((file: RepositoryFile) => {
dispatch(getFolder(file.folder.path));
})
.catch((error) => {
log('Error', error);
});
};
export const RECEIVE_FOLDER_DETAILS = 'App/Repository/RECEIVE_FOLDER_DETAILS';
import update from 'immutability-helper';
import { IReducerAction } from 'model/common.model';
import { RECEIVE_FOLDER_DETAILS } from 'repository/constants';
import RepositoryFile from 'model/repository/RepositoryFile';
import FolderDetails from 'model/repository/FolderDetails';
const INITIAL_STATE: {
folder: FolderDetails;
file: RepositoryFile;
loading: boolean;
} = {
folder: null,
file: null,
loading: false,
};
export default function reducer(state = INITIAL_STATE, action: IReducerAction = { type: '' }) {
switch (action.type) {
case RECEIVE_FOLDER_DETAILS: {
return update(state, {
loading: { $set: false},
folder: { $set: action.payload.folder },
error: { $set: action.payload.error },
});
break;
}
default:
return state;
}
}
import { combineReducers } from 'redux';
// import dashboard from './dashboard';
// import publicState from './public';
import admin from './admin';
const rootReducer = combineReducers({
// dashboard,
// public: publicState,
admin,
});
export default rootReducer;
// Admin
import RepositoryBrowser from 'repository/ui/RepositoryBrowser';
// Public
export const publicRoutes = [
// Root
];
export const dashboardRoutes = [
// Dashboard
];
export const adminRoutes = [
// Admin
{
path: '/repository:path(.+)/',
component: RepositoryBrowser,
exact: true,
root: '',
extraProps: {
title: 'File repository',
},
},
];
import * as React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { withStyles } from '@material-ui/core/styles';
import { normalize } from 'path';
import { getFolder, uploadFile, deleteFolder } from 'repository/actions/public';
import FolderDetails from 'model/repository/FolderDetails';
// import RepositoryFile from 'model/repository/RepositoryFile';
import Loading from 'ui/common/Loading';
import { PageContents } from 'ui/layout/PageLayout';
import ContentHeaderWithButton from 'ui/common/heading/ContentHeaderWithButton';
import Permissions from 'ui/common/permission/Permissions';
import FileCard from './c/FileCard';
import FolderCard from './c/FolderCard';
import RepositoryFolder from 'model/repository/RepositoryFolder';
import Button from '@material-ui/core/Button';
import { navigateTo } from 'actions/navigation';
import confirmAlert from 'utilities/confirmAlert';
// import Grid from '@material-ui/core/Grid';
interface IRepositoryBrowserProps extends React.ClassAttributes<any> {
classes: any;
t?: any;
root: string;
path: string;
folder: FolderDetails;
getFolder: (path: string) => any;
uploadFile: (path: string, file: File) => any;
deleteFolder: (path: string) => any;
navigateTo: (path: string, qs?: any) => any;
}
const styles = (theme) => ({
});
class RepositoryBrowser extends React.Component<IRepositoryBrowserProps, any> {
protected static needs = [
({ root, path }) => getFolder(root + path),
];
public componentWillMount() {
const { root, path, folder, getFolder } = this.props;
const folderPath: string = normalize(root + path).replace(/^(.+)\/$/, '$1');
console.log(`RepositoryBrowser.componentWillMount...`, normalize(root + path));
if (!folder || (folder.folder && folder.folder.path !== folderPath)) {
console.log(`Loading folder root=${root} path=${path}`);
getFolder(folderPath);
}
}
public componentWillReceiveProps(nextProps) {
const { path, root, folder } = nextProps;
const { folder: oldFolder, getFolder } = this.props;
const folderPath: string = normalize(root + path).replace(/^(.+)\/$/, '$1');
console.log(`Props root=${root} path=${path} folderPath=${folderPath} at=${folder && folder.folder && folder.folder.path}`, oldFolder);
if (!folder || (!folder.folder && path && path !== '/') || (folder.folder && folder.folder.path !== folderPath)) {
console.log(`Loading folder root=${root} path=${path} ${folderPath}`);
getFolder(folderPath);
}
}
protected upload = (e) => {
const { uploadFile, root, path } = this.props;
const folderPath: string = normalize(root + path).replace(/^(.+)\/$/, '$1');
const file = e.target.files[0];
uploadFile(folderPath, file);
e.target.files = null;
e.target.value = null;
}
protected deleteFolder = (e) => {
const { deleteFolder, navigateTo, root, path } = this.props;
const folderPath: string = normalize(root + path).replace(/^(.+)\/$/, '$1');
deleteFolder(folderPath).then((result) => {
if (result) {
navigateTo('..');
} else {
confirmAlert(<p>Folder <b>{ folderPath }</b> could not be deleted.</p>);
}
});
}
public render() {
const { folder, root, path } = this.props;
const parentFolder = new RepositoryFolder();
parentFolder.path = '..';
parentFolder.name = '..';
const stillLoading: boolean = !folder;
console.log(folder);
return (
stillLoading ? <Loading /> : (
<div>
<ContentHeaderWithButton title={ path } buttons={
<span>
{ ! folder.folder ? null :
folder.gallery ? <Button key="viewg" variant="raised">View gallery</Button> : <Button key="createg">Create gallery</Button> }
{ folder.folder && folder.folder._permissions.delete && <Button onClick={ this.deleteFolder } key="deletef">Delete folder</Button> }
{ folder.folder && folder.folder._permissions.manage && <Permissions clazz={ RepositoryFolder.clazz } id={ folder.folder.id } /> }
</span>
} />
<PageContents>
<div>
{ folder.folder && root !== `${folder.folder.path}/` && <FolderCard compact key="parent" folder={ parentFolder } /> }
{ folder.subFolders.map((subFolder) => <FolderCard compact key={ subFolder.uuid } folder={ subFolder } />) }
</div>
<div>
{ folder.files.map((file) => <FileCard key={ file.uuid } file={ file } />) }
</div>
<div>
<input type="file" onChange={ this.upload } />
</div>
</PageContents>
</div>
)
);
}
}
const mapStateToProps = (state, ownProps) => ({
root: ownProps.route.root || '/',
path: ownProps.match.params.path || '/',
folder: state.repository.admin.folder,
loading: state.repository.admin.loading,
});
const mapDispatchToProps = (dispatch) => bindActionCreators({
getFolder,
uploadFile,
deleteFolder,
navigateTo,
}, dispatch);
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(RepositoryBrowser));
import * as React from 'react';
// Models
import RepositoryFile from 'model/repository/RepositoryFile';
// UI
import Card, {CardHeader, CardContent} from 'ui/common/Card';
import {Properties, PropertiesItem} from 'ui/common/Properties';
import DownloadIcon from '@material-ui/icons/GetApp';
import PrettyDate from 'ui/common/time/PrettyDate';
import Grid from '@material-ui/core/Grid';
import withStyles from '@material-ui/core/styles/withStyles';
const style = (theme) => ({
root: {
height: '100%',
flexDirection: 'column' as 'column',
justifyContent: 'space-between',
display: 'flex',
},
});
const FileCard = ({file, classes, compact = false, edit = false, ...other}: { file: RepositoryFile, classes?: any, compact?: boolean, edit?: boolean }) => {
if (!file) {
return null;
}
return compact ? (
<Grid item xs={ 12 }>
<Card className={ classes.root }>
<CardHeader title={ <a href={ `/proxy/api/v1/repository/download/${file.uuid}` }>{ file.originalFilename }</a> } />
<CardContent>
{ file.storageFolder }
</CardContent>
</Card>
</Grid>
)
:
(
<Grid item xs={ 12 }>
<Card className={ classes.root }>
<CardHeader title={ <span><a href={ `/proxy/api/v1/repository/download/${file.uuid}` }><DownloadIcon /></a> <span>{ file.originalFilename }</span></span> } />
<CardContent>
<Properties>
<PropertiesItem title="Original name">{ file.originalFilename }</PropertiesItem>
<PropertiesItem title="Registered"><PrettyDate value={ new Date(file.createdDate) }/></PropertiesItem>
<PropertiesItem title="Last modified"><PrettyDate value={ new Date(file.lastModifiedDate) }/></PropertiesItem>
</Properties>
</CardContent>
</Card>
</Grid>
);
};
export default withStyles(style)(FileCard);
import * as React from 'react';
import { Link } from 'react-router-dom';
// Models
import RepositoryFolder from 'model/repository/RepositoryFolder';
// UI
import Card, {CardHeader, CardContent} from 'ui/common/Card';
import FolderIcon from '@material-ui/icons/Folder';
import {Properties, PropertiesItem} from 'ui/common/Properties';
import PrettyDate from 'ui/common/time/PrettyDate';
import Grid from '@material-ui/core/Grid';
import withStyles from '@material-ui/core/styles/withStyles';
const style = (theme) => ({
root: {
height: '100%',
flexDirection: 'column' as 'column',
justifyContent: 'space-between',
display: 'flex',
},
});
const FolderCard = ({root = '.', folder, classes, compact = false, edit = false, key, ...other}: { root?: string, folder: RepositoryFolder, classes?: any, compact?: boolean, edit?: boolean, key?: string }) => {
if (!folder) {
return null;
}
return compact ? (
<Grid key={ key } item xs={ 12 }>
<Card className={ classes.root }>
<CardHeader title={ <Link to={ `${root}/${folder.name}/` }><FolderIcon /> { folder.name }</Link> } />
</Card>
</Grid>
)
:
(
<Grid key={ key } item xs={ 12 }>
<Card className={ classes.root }>
<CardHeader title={ <Link to={ `${root}/${folder.name}/` }><FolderIcon /> { folder.name }</Link> } />
<CardContent>
<Properties>
<PropertiesItem title="Path">{ folder.path }</PropertiesItem>