87029571a20eb7d78ff27840e23071978e51471b
[osm/UI.git] / skyquake / plugins / composer / src / src / components / filemanager / FileManager.jsx
1 /*
2  *
3  *   Copyright 2016 RIFT.IO Inc
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  *
17  */
18
19
20 //https://raw.githubusercontent.com/RIFTIO/RIFT.ware/master/rift-shell
21 import _cloneDeep from 'lodash/cloneDeep'
22 import _findIndex from 'lodash/findIndex'
23 import _uniqueId from 'lodash/uniqueId'
24 import React from 'react';
25 import ReactDOM from 'react-dom';
26 import TreeView from 'react-treeview';
27 import TextInput from 'widgets/form_controls/textInput.jsx';
28 import Button from '../Button';
29 import './FileMananger.scss';
30 import FileManagerActions from './FileManagerActions.js';
31 import imgSave from '../../../../node_modules/open-iconic/svg/data-transfer-upload.svg'
32 import { Panel, PanelWrapper } from 'widgets/panel/panel';
33 import SkyquakeComponent from 'widgets/skyquake_container/skyquakeComponent.jsx';
34 import LoadingIndicator from 'widgets/loading-indicator/loadingIndicator.jsx';
35
36 import DropZone from 'dropzone'
37 import Utils from '../../libraries/utils'
38 import FileManagerUploadDropZone from '../../libraries/FileManagerUploadDropZone';
39 let API_SERVER = require('utils/rw.js').getSearchParams(window.location).api_server;
40
41 const ASSET_TYPE = {
42     'nsd': [
43         { id: 'ICONS', folder: 'icons', title: "Icons", allowFolders: false },
44         { id: 'SCRIPTS', folder: 'scripts', title: "scripts", allowFolders: true },
45         { id: 'NS_CONFIG', folder: 'ns_config', title: "NS Config", allowFolders: false },
46         { id: 'VNF_CONFIG', folder: 'vnf_config', title: "VNF Config", allowFolders: false }
47     ],
48     'vnfd': [
49         { id: 'ICONS', folder: 'icons', title: "Icons", allowFolders: false },
50         { id: 'CHARMS', folder: 'charms', title: "charms", allowFolders: true },
51         { id: 'SCRIPTS', folder: 'scripts', title: "scripts", allowFolders: true },
52         { id: 'IMAGES', folder: 'images', title: "images", allowFolders: false },
53         { id: 'CLOUD_INIT', folder: 'cloud_init', title: "cloud_init", allowFolders: false },
54         { id: 'README', folder: '.', title: ".", allowFolders: false }
55     ]
56 }
57
58 const createDropZone = function (action, clickable, getUploadPropsCallback, dropTarget) {
59     const dropZone = new FileManagerUploadDropZone(ReactDOM.findDOMNode(dropTarget), [clickable], action, getUploadPropsCallback);
60     // dropZone.on('dragover', this.onDragOver);
61     // dropZone.on('dragend', this.onDragEnd);
62     // dropZone.on('addedfile', this.onFileAdded);
63     return dropZone;
64 };
65
66 function normalizeAssets(packageType, assetInfo, filesStatus) {
67     let assets = {};
68     let assetTypes = ASSET_TYPE[packageType];
69     assetTypes.forEach(assetGroup => {
70         const typeFolder = assetGroup.folder;
71         let folders = assetInfo.id.filter(name => name.startsWith(typeFolder));
72         if (folders.length) {
73             folders.reverse();
74             assets[assetGroup.id] = folders.map(fullName => {
75                 let path = fullName.slice(typeFolder.length + 1);
76                 let files = assetInfo.data[fullName].map(info => ({
77                     name: info.name.startsWith(fullName) ? info.name.slice(fullName.length + 1) : info.name,
78                     status: filesStatus[info.name]
79                 }));
80                 return { path, files };
81             });
82         }
83     });
84     return assets;
85 }
86
87 function sendDeleteFileRequest(assetType, path, name) {
88     path = path ? path + '/' + name : name;
89     FileManagerActions.deletePackageFile({ assetType, path });
90 }
91
92 class FileManager extends React.Component {
93     constructor(props) {
94         super(props)
95         let assests = props.files;
96     }
97     componentWillMount() {
98         // FileManagerActions.openFileManagerSockets()
99     }
100     componentWillUnmount() {
101         // FileManagerActions.closeFileManagerSockets();
102     }
103     generateFolder(data, nesting) {
104         let nestingLevel = nesting || 1;
105     }
106     render() {
107         let { files, filesState, type, item, actions } = this.props;
108         let assets = normalizeAssets(type, files, filesState);
109         let children = [];
110         let assetTypes = ASSET_TYPE[type];
111         assetTypes.forEach(assetGroup => {
112             const typeFolder = assetGroup.folder;
113             let folders = assets[assetGroup.id];
114             let rootAssets = { path: '', files: [] };
115             let subFolders = null;
116             if (folders && folders.length) {
117                 rootAssets = folders[0];
118                 subFolders = folders.slice(1);
119             }
120             children.push(
121                 <AssetGroup
122                     key={typeFolder}
123                     packageId={item.id}
124                     packageType={type}
125                     title={assetGroup.title}
126                     assetGroup={assetGroup}
127                     path={rootAssets.path}
128                     files={rootAssets.files}
129                     allowsFolders={assetGroup.allowFolders}
130                     folders={subFolders}
131                     showNotification={actions.showNotification}
132                 />
133             )
134         }, this);
135
136         let html = (
137             <div className="FileManager">
138                 <PanelWrapper style={{ flexDirection: 'column' }}>
139                     {children}
140                 </PanelWrapper>
141             </div>
142         )
143         return html;
144     }
145
146 }
147
148 function NewHierachy(props) {
149     return (
150         <Panel className="addFileSection" style={{ backgroundColor: 'transparent' }} no-corners>
151             <div className="inputSection">
152                 <TextInput placeholder="some/path" label="create a folder hierarchy" onChange={FileManagerActions.newPathNameUpdated} />
153                 <Button label="Create" onClick={e => FileManagerActions.createDirectory(props.assetGroup.id)} />
154             </div>
155         </Panel>
156     );
157 }
158
159 class AssetGroup extends React.Component {
160     constructor(props) {
161         super(props);
162         this.state = { downloadPath: "" };
163     }
164
165     render() {
166         let { title, packageType, packageId, assetGroup, files, allowsFolders, folders, path, inputState, showNotification } = this.props;
167         let children = [];
168         if (folders && folders.length) {
169             folders.map(function (folder, i) {
170                 children.push(
171                     <AssetGroup
172                         key={folder.path}
173                         packageId={packageId}
174                         title={folder.path}
175                         packageType={packageType}
176                         files={folder.files}
177                         assetGroup={assetGroup}
178                         path={folder.path}
179                         showNotification={showNotification}
180                     />
181                 )
182             });
183         }
184         let folderCreateComponent = allowsFolders ? <NewHierachy assetGroup={assetGroup} /> : null;
185         let entries = null
186
187         function uploadFileFromUrl(url) {
188             if (!url || url == "") {
189                 return;
190             }
191             let splitUrl = url.split('/');
192             let fileName = splitUrl[splitUrl.length - 1];
193             if (files.findIndex(f => f.name === fileName) > -1) {
194                 showNotification('It seems you\'re attempting to upload a file with a duplicate file name');
195             } else {
196                 FileManagerActions.sendDownloadFileRequest({ url, assetType: assetGroup.id, path: path });
197             }
198         }
199
200         return (
201             <Panel title={title} itemClassName="nested" no-corners>
202                 {folderCreateComponent}
203                 <div className="folder">
204                     <FileAssetList files={files} path={path} packageId={packageId} packageType={packageType} assetGroup={assetGroup} />
205                     <Panel className="addFileSection" no-corners>
206                         <ItemUpload packageType={packageType} packageId={packageId} path={path} assetGroup={assetGroup} />
207                         <div style={{ marginLeft: '0.5rem' }}>
208                             OR
209                     </div>
210                         <div className="inputSection">
211                             <TextInput placeholder="URL" className="" label="External URL" value={this.state.downloadPath} onChange={e => this.setState({ downloadPath: e.target.value })} />
212                             <Button className='ComposerAppSave' label="DOWNLOAD" onClick={e => uploadFileFromUrl(this.state.downloadPath)} />
213                         </div>
214                     </Panel>
215                     <div>
216                         {children}
217                     </div>
218                 </div>
219             </Panel>
220         );
221     }
222 }
223
224 function FileAssetList(props) {
225     let { packageType, packageId, assetGroup, files, path } = props;
226     let children = null;
227     if (files) {
228         children = files.map(function (file, i) {
229             if (!file.hasOwnProperty('contents')) {
230                 return <FileAsset key={file.name} file={file} path={path} id={packageId} type={packageType} assetGroup={assetGroup} />
231             }
232         })
233     }
234     return (
235         <div className='file-list'>
236             {children}
237         </div>
238     );
239
240 }
241
242 function FileAsset(props) {
243     let { file, path, type, assetGroup, id } = props;
244     const name = file.name;
245     const downloadHost = API_SERVER.match('localhost') || API_SERVER.match('127.0.0.1') ? `${window.location.protocol}//${window.location.hostname}` : API_SERVER;
246     //{`${window.location.protocol}//${API_SERVER}:4567/api/package${type}/${id}/${path}/${name}`}
247     return (
248         <div className="file">
249             <div className="file-section">
250                 <div className="file-info">
251                     <div className="file-status"
252                         style={{ display: (file.status && file.status.toLowerCase() != 'completed') ? 'inherit' : 'none', color: (file.status == 'FAILED' ? 'red' : 'inherit') }}>
253                         {file.status && (file.status == 'IN_PROGRESS' || file.status == 'DOWNLOADING') ? <LoadingIndicator size={2} /> : file.status}
254                     </div>
255                     <div className="file-name">
256                         <a target="_blank" href={`${downloadHost}:4567/api/package/${type}/${id}/${assetGroup.folder}${path}/${name}`}>{name}</a>
257                     </div>
258                 </div>
259                 <div className="file-action"
260                     style={{ display: (!file.status || (file && file.status.toLowerCase() != 'loading...')) ? 'inherit' : 'none', cursor: 'pointer' }}
261                     onClick={e => sendDeleteFileRequest(assetGroup.id, path, name)}>
262                     X
263                 </div>
264             </div>
265         </div>
266     )
267 }
268
269 class ItemUpload extends React.Component {
270     constructor(props) {
271         super(props);
272         this.state = { dropzoneIdClass: 'DZ-' + _uniqueId() };
273     }
274     componentDidMount() {
275         createDropZone(
276             FileManagerUploadDropZone.ACTIONS.onboard,
277             '.ComposerAppAddFile.' + this.state.dropzoneIdClass,
278             () => {
279             let theCode = 'crap';
280             return ({
281                 packageType: this.props.packageType,
282                 packageId: this.props.packageId,
283                 assetGroup: this.props.assetGroup,
284                 path: this.props.path
285             })},
286             this);
287     }
288
289     render() {
290         let { dropzoneIdClass } = this.props;
291         return (
292             <div className="inputSection">
293                 <label className="sqTextInput" style={{ flexDirection: 'row', alignItems: 'center' }}>
294                     <span>Upload File</span>
295                     <Button className={'ComposerAppAddFile ' + this.state.dropzoneIdClass} label="BROWSE" />
296                 </label>
297             </div>
298         )
299     }
300 }
301
302 function stripPath(name, path, returnPath) {
303     let stripSlash = (name.indexOf('/') > -1) ? '/' : '';
304     // return name.split(path + stripSlash)[1].replace('/', '');
305     let split = name.split(path + stripSlash)[returnPath ? 0 : 1];
306     return split ? split.replace('/', '') : name;
307 }
308
309
310
311 export default SkyquakeComponent(FileManager)
312 /**
313  * Sample Data
314  */
315 // let files = {
316 //   "name": ".",
317 //   "contents": [
318 //     {
319 //       "name": "pong_vnfd",
320 //       "contents": [
321 //         {
322 //           "name": "pong_vnfd/checksums.txt",
323 //           "last_modified_time": 1474458399.6218443,
324 //           "byte_size": 168
325 //         },
326 //         {
327 //           "name": "pong_vnfd/pong_vnfd.yaml",
328 //           "last_modified_time": 1474458399.6258445,
329 //           "byte_size": 3514
330 //         },
331 //         {
332 //           "name": "pong_vnfd/icons",
333 //           "contents": [
334 //             {
335 //               "name": "pong_vnfd/icons/rift_logo.png",
336 //               "last_modified_time": 1474458399.6218443,
337 //               "byte_size": 1658
338 //             }
339 //           ],
340 //           "last_modified_time": 1474458399.6218443,
341 //           "byte_size": 3
342 //         },
343 //         {
344 //           "name": "pong_vnfd/cloud_init",
345 //           "contents": [
346 //             {
347 //               "name": "pong_vnfd/cloud_init/pong_cloud_init.cfg",
348 //               "last_modified_time": 1474458399.6258445,
349 //               "byte_size": 227
350 //             }
351 //           ],
352 //           "last_modified_time": 1474458399.6258445,
353 //           "byte_size": 3
354 //         }
355 //       ],
356 //       "last_modified_time": 1474458399.6258445,
357 //       "byte_size": 6
358 //     }
359 //   ],
360 //   "last_modified_time": 1474458399.6218443,
361 //   "byte_size": 3
362 // };