update from RIFT as of 696b75d2fe9fb046261b08c616f1bcf6c0b54a9b third try
[osm/UI.git] / skyquake / plugins / composer / src / src / components / ComposerApp.js
index 861c38b..d7a40d3 100644 (file)
@@ -48,6 +48,11 @@ import TooltipManager from '../libraries/TooltipManager'
 import CatalogItemsActions from '../actions/CatalogItemsActions'
 import CommonUtils from 'utils/utils.js'
 import FileManagerActions from './filemanager/FileManagerActions';
+import { SkyquakeRBAC, isRBACValid } from 'widgets/skyquake_rbac/skyquakeRBAC.jsx';
+import ROLES from 'utils/roleConstants.js';
+import _isEmpty from 'lodash/isEmpty';
+import _isEqual from 'lodash/isEqual';
+
 import 'normalize.css'
 import '../styles/AppRoot.scss'
 import 'style/layout.scss'
@@ -60,214 +65,221 @@ const clearLocalStorage = utils.getSearchParams(window.location).hasOwnProperty(
 const preventDefault = e => e.preventDefault();
 const clearDragState = () => ComposerAppActions.setDragState(null);
 
+const PROJECT_ROLES = ROLES.PROJECT;
+const PLATFORM = ROLES.PLATFORM;
+
+const CATALOG_POLLING_INTERVAL = 2000;
+
 
 const ComposerApp = React.createClass({
-       mixins: [PureRenderMixin],
-       getInitialState() {
-               return ComposerAppStore.getState();
-       },
-       getDefaultProps() {
-               return {};
-       },
-       componentWillMount() {
-               if (clearLocalStorage) {
-                       window.localStorage.clear();
-               }
-        if(this.item) {
+    getInitialState() {
+        return ComposerAppStore.getState();
+    },
+    getDefaultProps() {
+        return {};
+    },
+    contextTypes: {
+        router: React.PropTypes.object,
+        userProfile: React.PropTypes.object
+    },
+    componentWillUpdate(nextProps, nextState, nextContext) {
+        if (!_isEmpty(nextContext.userProfile)) {
+            CatalogDataStore.setUserProfile(nextContext.userProfile);
+        }
+    },
+    shouldComponentUpdate: function (nextProps, nextState, nextContext) {
+        if (!this.userProfile && !_isEmpty(nextContext.userProfile)) {
+            this.userProfile = nextContext.userProfile;
+            CatalogDataStore.setUserProfile(nextContext.userProfile);
+            return true;
+        }
+        return !_isEqual(this.props, nextProps) ||
+            !_isEqual(this.state, nextState);
+    },
+    componentWillMount() {
+        if (clearLocalStorage) {
+            window.localStorage.clear();
+        }
+        if (this.item) {
             FileManagerActions.openFileManagerSockets();
         }
-               this.state.isLoading = CatalogDataStore.getState().isLoading;
-               ComposerAppStore.listen(this.onChange);
-               CatalogDataStore.listen(this.onCatalogDataChanged);
-               window.addEventListener('resize', this.resize);
-               window.onbeforeunload = this.onBeforeUnload;
-               // prevent browser from downloading any drop outside of our specific drop zones
-               window.addEventListener('dragover', preventDefault);
-               window.addEventListener('drop', preventDefault);
-               // ensure drags initiated in the app clear the state on drop
-               window.addEventListener('drop', clearDragState);
-               DeletionManager.addEventListeners();
-       },
-       componentWillUnmount() {
-               window.removeEventListener('resize', this.resize);
-               window.removeEventListener('dragover', preventDefault);
-               window.removeEventListener('drop', preventDefault);
-               window.removeEventListener('drop', clearDragState);
+        this.state.isLoading = CatalogDataStore.getState().isLoading;
+        ComposerAppStore.listen(this.onChange);
+        CatalogDataStore.listen(this.onCatalogDataChanged);
+        window.addEventListener('resize', this.resize);
+        // prevent browser from downloading any drop outside of our specific drop zones
+        window.addEventListener('dragover', preventDefault);
+        window.addEventListener('drop', preventDefault);
+        // ensure drags initiated in the app clear the state on drop
+        window.addEventListener('drop', clearDragState);
+        DeletionManager.addEventListeners();
+    },
+    componentWillUnmount() {
+        window.removeEventListener('resize', this.resize);
+        window.removeEventListener('dragover', preventDefault);
+        window.removeEventListener('drop', preventDefault);
+        window.removeEventListener('drop', clearDragState);
         FileManagerActions.closeFileManagerSockets();
-               // resizeManager automatically registered its event handlers
-               resizeManager.removeAllEventListeners();
-               ComposerAppStore.unlisten(this.onChange);
-               CatalogDataStore.unlisten(this.onCatalogDataChanged);
-               DeletionManager.removeEventListeners();
-               TooltipManager.removeEventListeners();
-       },
-       componentDidMount() {
-               resizeManager.addAllEventListeners();
-               const snapshot = window.localStorage.getItem('composer');
-               if (snapshot) {
-                       alt.bootstrap(snapshot);
-               }
-               document.body.addEventListener('keydown', (event) => {
-                       // prevent details editor form from blowing up the app
-                       const ENTER_KEY = 13;
-                       if (event.which === ENTER_KEY) {
-                               event.preventDefault();
-                               return false;
-                       }
-               });
-               const appRootElement = ReactDOM.findDOMNode(this.refs.appRoot);
-               TooltipManager.addEventListeners(appRootElement);
-               SelectionManager.onClearSelection = () => {
-                       if (this.state.item) {
-                               CatalogItemsActions.catalogItemMetaDataChanged.defer(this.state.item);
-                       }
-               };
-       },
-       componentDidUpdate() {
-               if (this.state.fullScreenMode) {
-                       document.body.classList.add('-is-full-screen');
-               } else {
-                       document.body.classList.remove('-is-full-screen');
-               }
-               SelectionManager.refreshOutline();
-       },
-       resize(e) {
-               PanelResizeAction.resize(e);
-       },
-       getModel() {
-               let html;
-               let self = this;
-               DescriptorModelMetaFactory.init().then(function(){
-
-                       self.setState({
-                               hasModel: true
-                       })
-               });
-       },
-       render() {
-               let html = null;
-               let self = this;
-               if(this.state.hasModel) {
+        // resizeManager automatically registered its event handlers
+        resizeManager.removeAllEventListeners();
+        ComposerAppStore.unlisten(this.onChange);
+        CatalogDataStore.unlisten(this.onCatalogDataChanged);
+        DeletionManager.removeEventListeners();
+        TooltipManager.removeEventListeners();
+        if (this.catalogMonitorId) {
+            clearTimeout(this.catalogMonitorId);
+        }
+    },
+    componentDidMount() {
+        resizeManager.addAllEventListeners();
+        const snapshot = window.localStorage.getItem('composer');
+        if (snapshot) {
+            alt.bootstrap(snapshot);
+        }
+        document.body.addEventListener('keydown', (event) => {
+            // prevent details editor form from blowing up the app
+            const ENTER_KEY = 13;
+            if (event.which === ENTER_KEY) {
+                event.preventDefault();
+                return false;
+            }
+        });
+        const loadCatalogs = () => {
+            CatalogDataStore.loadCatalogs();
+            if (CATALOG_POLLING_INTERVAL) {
+                this.catalogMonitorId = setTimeout(loadCatalogs, CATALOG_POLLING_INTERVAL);
+            }
+        };
+        loadCatalogs();
+        DescriptorModelMetaFactory.init().then(() => this.setState({ hasModel: true }));
+    },
+    componentDidUpdate() {
+        if (this.state.fullScreenMode) {
+            document.body.classList.add('-is-full-screen');
+        } else {
+            document.body.classList.remove('-is-full-screen');
+        }
+        SelectionManager.refreshOutline();
+    },
+    resize(e) {
+        PanelResizeAction.resize(e);
+    },
+    render() {
+        let html = null;
+        let self = this;
+        const User = this.userProfile || {};
+        const rbacDisabled = !isRBACValid(User, [PROJECT_ROLES.PROJECT_ADMIN, PROJECT_ROLES.CATALOG_ADMIN]);
+        if (this.state.hasModel) {
 
-                       function onClickUpdateSelection(event) {
-                               if (event.defaultPrevented) {
-                                       return
-                               }
-                               const element = SelectionManager.getClosestElementWithUID(event.target);
-                               if (element) {
-                                       SelectionManager.select(element);
-                                       SelectionManager.refreshOutline();
-                                       event.preventDefault();
-                               } else {
-                                       SelectionManager.clearSelectionAndRemoveOutline();
-                               }
-                       }
+            function onClickUpdateSelection(event) {
+                if (event.defaultPrevented) {
+                    return
+                }
+                const element = SelectionManager.getClosestElementWithUID(event.target);
+                if (element) {
+                    SelectionManager.select(element);
+                    SelectionManager.refreshOutline();
+                    event.preventDefault();
+                } else {
+                    if (event.target.offsetParent && !event.target.offsetParent.classList.contains("tray-body")) {
+                        SelectionManager.clearSelectionAndRemoveOutline();
+                    }
+                }
+            }
 
+            let cpNumber = 0;
+            let AppHeader = (<div className="AppHeader">
+                <RiftHeader />
+            </div>);
+            // AppHeader = null;
+            const classNames = ClassNames('ComposerApp');
+            const isNew = self.state.item && self.state.item.uiState.isNew;
+            const hasItem = self.state.item && self.state.item.uiState;
+            const isModified = self.state.item && self.state.item.uiState.modified;
+            const isEditingNSD = self.state.item && self.state.item.uiState && /nsd/.test(self.state.item.uiState.type);
+            const isEditingVNFD = self.state.item && self.state.item.uiState && /vnfd/.test(self.state.item.uiState.type);
+            const containers = [self.state.item].reduce(DescriptorModelFactory.buildCatalogItemFactory(CatalogDataStore.getState().catalogs), []);
 
-                       let AppHeader = (<div className="AppHeader">
-                                                               <RiftHeader />
-                                                       </div>);
-                       // AppHeader = null;
-                       const classNames = ClassNames('ComposerApp');
-                       const isNew = self.state.item && self.state.item.uiState.isNew;
-                       const hasItem = self.state.item && self.state.item.uiState;
-                       const isModified = self.state.item && self.state.item.uiState.modified;
-                       const isEditingNSD = self.state.item && self.state.item.uiState && /nsd/.test(self.state.item.uiState.type);
-                       const isEditingVNFD = self.state.item && self.state.item.uiState && /vnfd/.test(self.state.item.uiState.type);
-            const containers = self.state.containers;
-                       const canvasTitle = containers.length ? containers[0].model.name : '';
-                       const hasNoCatalogs = CatalogDataStore.getState().catalogs.length === 0;
-                       const isLoading = self.state.isLoading;
+            containers.filter(d => DescriptorModelFactory.isConnectionPoint(d)).forEach(d => {
+                d.cpNumber = ++cpNumber;
+                containers.filter(d => DescriptorModelFactory.isVnfdConnectionPointRef(d)).filter(ref => ref.key === d.key).forEach(ref => ref.cpNumber = d.cpNumber);
+            });
+            const canvasTitle = containers.length ? containers[0].model.name : '';
+            const hasNoCatalogs = CatalogDataStore.getState().catalogs.length === 0;
+            const isLoading = self.state.isLoading;
 
             //Bridge element for Crouton fix. Should eventually put Composer on same flux context
             const Bridge = this.state.ComponentBridgeElement;
 
-                       html = (
-                               <div ref="appRoot" id="RIFT_wareLaunchpadComposerAppRoot" className="AppRoot" onClick={onClickUpdateSelection}>
+            html = (
+                <div ref={element => TooltipManager.addEventListeners(element)} id="RIFT_wareLaunchpadComposerAppRoot" className="AppRoot" onClick={onClickUpdateSelection}>
                     <Bridge />
-                                       <i className="corner-accent top left" />
-                                       <i className="corner-accent top right" />
-                                       <i className="corner-accent bottom left" />
-                                       <i className="corner-accent bottom right" />
-                                       {AppHeader}
-                                       <div className="AppBody">
-                                               <div className={classNames}>
-                                                       <CatalogPanel layout={self.state.layout}
-                                                                                 isLoading={isLoading}
-                                                                                 hasNoCatalogs={hasNoCatalogs}
-                                                                                 filterByType={self.state.filterCatalogByTypeValue} />
-                                                       <CanvasPanel layout={self.state.layout}
-                                                                                hasNoCatalogs={hasNoCatalogs}
-                                                                                showMore={self.state.showMore}
-                                                                                containers={containers}
-                                                                                title={canvasTitle}
-                                                                                zoom={self.state.zoom}
-                                                                                panelTabShown={self.state.panelTabShown}
-                                                                                files={self.state.files}
-                                                                                filesState={self.state.filesState}
-                                         newPathName={self.state.newPathName}
-                                                                                item={self.state.item}
-                                                                                type={self.state.filterCatalogByTypeValue}
-                                                                                 />
-                                                       {
-                                                               (self.state.panelTabShown == 'descriptor') ?
-                                                               <DetailsPanel layout={self.state.layout}
-                                                                                 hasNoCatalogs={hasNoCatalogs}
-                                                                                 showMore={self.state.showMore}
-                                                                                 containers={containers}
-                                                                                 showJSONViewer={self.state.showJSONViewer} />
-                                                         : null
-                                                       }
-
-                                                       <ComposerAppToolbar layout={self.state.layout}
-                                                                                               showMore={self.state.showMore}
-                                                                                               isEditingNSD={isEditingNSD}
-                                                                                               isEditingVNFD={isEditingVNFD}
-                                                                                               isModified={isModified}
-                                                                                               isNew={isNew}
-                                                                                               disabled={!hasItem}
-                                                                                               onClick={event => event.stopPropagation()}
-                                                                                               panelTabShown={self.state.panelTabShown}/>
-                                               </div>
-                                       </div>
-                                       <ModalOverlay />
-                               </div>
-                       );
-               } else {
-                       this.getModel();
-               }
-               return html;
-       },
-       onChange(state) {
-               this.setState(state);
-       },
-       onCatalogDataChanged(catalogDataState) {
-               const catalogs = catalogDataState.catalogs;
-               const unsavedChanges = catalogs.reduce((result, catalog) => {
-                       if (result) {
-                               return result;
-                       }
-                       return catalog.descriptors.reduce((result, descriptor) => {
-                               if (result) {
-                                       return result;
-                               }
-                               return descriptor.uiState.modified;
-                       }, false);
-               }, false);
-               this.setState({
-                       unsavedChanges: unsavedChanges,
-                       isLoading: catalogDataState.isLoading
-               });
-       },
-       onBeforeUnload() {
-               // https://trello.com/c/c8v321Xx/160-prompt-user-to-save-changes
-               //const snapshot = alt.takeSnapshot();
-               //window.localStorage.setItem('composer', snapshot);
-               if (this.state.unsavedChanges) {
-                       return 'You have unsaved changes. If you do not onboard (or update) your changes they will be lost.';
-               }
-       }
+                    <i className="corner-accent top left" />
+                    <i className="corner-accent top right" />
+                    <i className="corner-accent bottom left" />
+                    <i className="corner-accent bottom right" />
+                    {AppHeader}
+                    <div className="AppBody">
+                        <div className={classNames}>
+                            <CatalogPanel layout={self.state.layout}
+                                isLoading={isLoading}
+                                hasNoCatalogs={hasNoCatalogs}
+                                filterByType={self.state.filterCatalogByTypeValue}
+                                rbacDisabled={rbacDisabled} />
+                            <CanvasPanel layout={self.state.layout}
+                                hasNoCatalogs={hasNoCatalogs}
+                                showMore={self.state.showMore}
+                                containers={containers}
+                                title={canvasTitle}
+                                zoom={self.state.zoom}
+                                panelTabShown={self.state.panelTabShown}
+                                files={self.state.files}
+                                filesState={self.state.filesState}
+                                newPathName={self.state.newPathName}
+                                item={self.state.item}
+                                type={self.state.filterCatalogByTypeValue}
+                                rbacDisabled={rbacDisabled}
+                                User={User}
+                            />
+                            {
+                                (self.state.panelTabShown == 'descriptor') ?
+                                    <DetailsPanel layout={self.state.layout}
+                                        hasNoCatalogs={hasNoCatalogs}
+                                        showMore={self.state.showMore}
+                                        containers={containers}
+                                        showHelp={self.state.showHelp}
+                                        collapsePanelsByDefault={self.state.collapsePanelsByDefault}
+                                        openPanelsWithData={self.state.openPanelsWithData}
+                                        showJSONViewer={self.state.showJSONViewer} />
+                                    : null
+                            }
 
+                            <ComposerAppToolbar layout={self.state.layout}
+                                showMore={self.state.showMore}
+                                isEditingNSD={isEditingNSD}
+                                isEditingVNFD={isEditingVNFD}
+                                isModified={isModified}
+                                isNew={isNew}
+                                disabled={!hasItem || rbacDisabled}
+                                onClick={event => event.stopPropagation()}
+                                panelTabShown={self.state.panelTabShown} />
+                        </div>
+                    </div>
+                    <ModalOverlay />
+                </div>
+            );
+        }
+        return html;
+    },
+    onChange(state) {
+        this.setState(state);
+    },
+    onCatalogDataChanged(catalogDataState) {
+        this.setState({
+            isLoading: catalogDataState.isLoading
+        });
+    }
 });
 
+
 export default ComposerApp;