From 6c183e974da6e18a49b33175c1e5f930835bc81f Mon Sep 17 00:00:00 2001 From: Shekhovtsov Alexander Date: Tue, 6 Nov 2018 20:40:33 +0300 Subject: [PATCH 1/2] Add Login Panel (#424) * Add Dark Mode * Prettify dark mode toggler * New line * Styles improvements * Add Login Panel * Add .DS_Store to .gitignore * Change header background color * Remove commented lines * LoginPage.js => login.js * Design improvements * Add error message on incorrect login * Design improve * Design improvement #2 * Design improvements #3 * Design improvements #4 * Fix username * Move close button to the panel * Add icons * Remove .DS_Store files * Login functional improvement * Delete .DS_Store file * Remove image samples * Prettifying code * Resolving webpack errors * Add logout button * Add sign-in and sign-out icons * Fix logout * Login improvements * Add login on backend * Making oauth work too * fabrik_login_system -> isOAuth * Fix OAuth logic * Add password hashing * Change error message --- .gitignore | 1 + backendAPI/views.py | 44 ++++-- ide/static/css/login_style.css | 267 +++++++++++++++++++++++++++++++++ ide/static/js/content.js | 1 - ide/static/js/login.js | 176 ++++++++++++++++++---- ide/templates/index.html | 1 + 6 files changed, 449 insertions(+), 41 deletions(-) create mode 100644 ide/static/css/login_style.css diff --git a/.gitignore b/.gitignore index c1cc17778..a7988d3bd 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ env/ .coverage .coverage.* .cache +.DS_Store # Django stuff: *.log diff --git a/backendAPI/views.py b/backendAPI/views.py index 99979e2c3..733e82a66 100644 --- a/backendAPI/views.py +++ b/backendAPI/views.py @@ -6,19 +6,41 @@ def check_login(request): try: - user = User.objects.get(username=request.user.username) - user_id = user.id - username = 'Anonymous' + if request.GET.get('isOAuth') == 'false': + username = request.GET['username'] + password = request.GET['password'] + user = User.objects.get(username=username) + user_id = user.id - is_authenticated = user.is_authenticated() - if (is_authenticated): - username = user.username + if not user.check_password(password): + return JsonResponse({ + 'result': False, + 'error': 'Please enter valid credentials' + }) - return JsonResponse({ - 'result': is_authenticated, - 'user_id': user_id, - 'username': username - }) + is_authenticated = user.is_authenticated() + if (is_authenticated): + username = user.username + + return JsonResponse({ + 'result': is_authenticated, + 'user_id': user_id, + 'username': username, + }) + else: + user = User.objects.get(username=request.user.username) + user_id = user.id + username = 'Anonymous' + + is_authenticated = user.is_authenticated() + if (is_authenticated): + username = user.username + + return JsonResponse({ + 'result': is_authenticated, + 'user_id': user_id, + 'username': username + }) except Exception as e: return JsonResponse({ 'result': False, diff --git a/ide/static/css/login_style.css b/ide/static/css/login_style.css new file mode 100644 index 000000000..9eafb9603 --- /dev/null +++ b/ide/static/css/login_style.css @@ -0,0 +1,267 @@ +#sidebar { + z-index: 1000; +} + +#sidebar-login-button { + background: rgb(205, 207, 210); + color: rgb(69, 80, 97); + text-align: center; + border-radius: 5px; + transition: 0.2s; + position: relative; +} + +#sidebar-login-button:hover { + cursor: pointer; +} + +#sidebar-login-button span { + position: absolute; + left: 9px; + font-size: 20px; + top: 7px; +} + +#login-prepanel { + background: rgba(51, 51, 51, 0.7); + + position: fixed; + top: 0px; + left: 0px; + width: 100%; + height: 100%; + text-align: center; + vertical-align: center; + padding: 70px 0; + transition: 0.2s; + z-index: -100; + opacity: 0; +} + +.login-prepanel-enabled { + z-index: 100 !important; + opacity: 1 !important; +} + +#login-panel-close { + position: absolute; + right: 5px; + top: 5px; + font-size: 25px; + color: white; +} + +#login-panel-close:hover { + cursor: pointer; +} + +@keyframes frames-logo-appear { + from { + top: 10px; + opacity: 0; + } + to { + opacity: 1; + top: 0px; + } +} + +.login-panel { + position: relative; + width: 350px; + background: rgba(243, 243, 243, 0.99); + box-shadow: 0px 0px 2px 1px rgb(100, 100, 100); + margin: auto; + text-align: left; + z-index: 1000; + border-radius: 3px; + + animation-name: frames-logo-appear; + animation-duration: 0.5s; + padding-bottom: 35px; +} + +.login-panel-main { + padding: 20px; +} + + +.login-logo { + text-align: center; + background: rgb(71, 80, 97); + padding: 20px; + box-shadow: 0px 1px 1px 1px rgba(100, 100, 100, 0.3); +} + +#login-logo { + position: relative; + transition: 0.5s; + max-height: 50px; + margin: 0 auto; + padding: 7px 0px 7px 0px; + + animation-name: frames-logo-appear; + animation-duration: 0.5s; +} + +.login-prepanel-enabled #login-logo { + top: 0px; + opacity: 1; +} + +.login-button { + margin-bottom: 20px; + margin-top: 10px; + z-index: 100; +} + +.login-button:hover { + opacity: 1; + cursor: pointer; +} + +.login-button .btn-social { + box-shadow: none !important; + color: rgb(69, 80, 97); + background: rgb(205, 207, 210); +} + +.login-button .btn-social:hover { + color: rgb(69, 80, 97); +} + +.btn-social span { + border-right: none !important; +} + +.extra-login { + margin-bottom: 10px !important; +} + +#login-error-message { + position: relative; + background: rgba(221, 75, 57, 0.3); + border-radius: 4px; + border: 1px solid rgba(221, 75, 57, 0.8); + padding: 7px; + color: rgb(69, 80, 97); + display: none; + padding-left: 30px; +} + +#login-error-message i { + color: rgba(221, 75, 57, 0.8); + position: absolute; + display: block; + left: 3px; + top: 5px; +} + +#login-error-message-text { + display: inline; +} + +#successful-login-notification { + position: fixed; + background: rgba(102, 187, 106, 0.3); + border-radius: 4px; + border: 1px solid rgba(102, 187, 106, 0.8); + padding: 9px; + color: rgb(69, 80, 97); + display: none; + right: 30px; + top: 30px; + padding-left: 30px; +} + +#successful-login-notification i { + color: rgba(102, 187, 106, 0.8); + position: absolute; + left: 3px; + top: 6px; +} + +.cut { + position: relative; + height: 2px; + background: black; + opacity: 0.1; + top: 75px; + margin-left: 40px; + margin-right: 40px; +} + +.login-action-icon { + position: relative; + top: 5px; + font-size: 22px; + line-height: 10px; +} + +#login-prepanel h5 { + position: relative; +} + +.login-invalid { + position: absolute; + right: 20px; + top: 10px; + color: #ff7676; + opacity: 0; + transition: 0.3s; +} + +.login-invalid-enabled { + right: 10px; + opacity: 1; +} + +.login-panel input { + outline: none; + background: none; + border: none; + color: rgb(69, 80, 97); + font-size: 15px; +} + +.login-prebtn { + display: inline; +} + +.login-panel .col-md-6 { + padding-left: 0px; + padding-right: 5px; + margin-right: 3px; +} + +.login-panel .col-md-5 { + padding-left: 0px; + padding-right: 0px; +} + +.btn-social { + width: 153px !important; + opacity: 0.8; + box-shadow: 0px 0px 1px 0px rgba(0, 0, 0, 0.6); + transition: 0.2s; +} + +.btn-social:hover { + box-shadow: 0px 0px 2px 0px rgba(0, 0, 0, 0.6); +} + +.img-fabrik-sample { + display: inline; +} + +.sample-1 { + position: absolute; + top: 0px; + left: 0px; +} + +.sample-2 { + position: absolute; + top: 0px; + right: 10px; +} \ No newline at end of file diff --git a/ide/static/js/content.js b/ide/static/js/content.js index 4d363f308..8bb70b60f 100644 --- a/ide/static/js/content.js +++ b/ide/static/js/content.js @@ -1306,7 +1306,6 @@ class Content extends React.Component { urlModal={this.urlModal} updateHistoryModal={this.updateHistoryModal} /> -
LOGIN
diff --git a/ide/static/js/login.js b/ide/static/js/login.js index 2cf71b481..8e12644ba 100644 --- a/ide/static/js/login.js +++ b/ide/static/js/login.js @@ -3,47 +3,83 @@ import React from 'react'; class Login extends React.Component { constructor(props) { super(props); - this.checkLogin = this.checkLogin.bind(this); + this.tryLogin = this.tryLogin.bind(this); this.logoutUser = this.logoutUser.bind(this); } componentWillMount() { this.setState({ loginState: false }); - this.checkLogin(); } - checkLogin() { + componentDidMount() { + this.tryLogin(false); + } + logoutUser() { $.ajax({ - url: '/backendAPI/checkLogin', + url: '/accounts/logout', type: 'GET', processData: false, // tell jQuery not to process the data contentType: false, success: function (response) { - if (response.result) { - this.setState({ loginState: response.result }); - this.props.setUserId(response.user_id); - this.props.setUserName(response.username); + if (response) { + this.setState({ loginState: false }); + this.props.setUserId(null); + this.props.setUserName(null); + + $('#login-prepanel')[0].style.display = 'none'; } }.bind(this), error: function () { - this.setState({ loginState: false }); + this.setState({ loginState: true }); + this.addError("Error occurred while logging out"); }.bind(this) }); } - logoutUser() { + openLoginPanel() { + $('#login-prepanel')[0].classList.add('login-prepanel-enabled'); + $('#login-prepanel')[0].style.display = 'block'; + } + closeLoginPanel() { + $('#login-prepanel')[0].classList.remove('login-prepanel-enabled'); + } + tryLogin(showNotification) { + let username = $('#login-input')[0].value; + let password = $('#password-input')[0].value; + $.ajax({ - url: '/accounts/logout', + url: '/backendAPI/checkLogin', type: 'GET', - processData: false, // tell jQuery not to process the data contentType: false, + data: { + username: username, + password: password, + isOAuth: (!showNotification) + }, success: function (response) { - if (response) { - this.setState({ loginState: false }); - this.props.setUserId(null); - this.props.setUserName(null); + if (response.result) { + this.setState({ loginState: response.result }); + this.props.setUserId(response.user_id); + this.props.setUserName(response.username); + + if (showNotification) { + $('#successful-login-notification')[0].style.display = 'block'; + $('#successful-login-notification-message')[0].innerHTML = 'Welcome, ' + username + '!'; + + setTimeout(() => { + let elem = $('#successful-login-notification')[0]; + if (elem) { + elem.style.display = 'none'; + } + }, 3000); + } + } + else { + if (showNotification) { + $('#login-error-message-text')[0].innerHTML = response.error; + $('#login-error-message')[0].style.display = 'block'; + } } }.bind(this), error: function () { - this.setState({ loginState: true }); - this.addError("Error occurred while logging out"); + this.setState({ loginState: false }); }.bind(this) }); } @@ -51,23 +87,105 @@ class Login extends React.Component { if(this.state.loginState) { return (
- this.logoutUser() }>Logout + +
+ done +
+
) } else { return ( -
-
- window.location="/accounts/github/login"} style={{width: '105px'}}> - Github - -
+
+ +
{ + if (e.target.id == "login-prepanel" || e.target.id == "login-panel-close") { + this.closeLoginPanel(); + } + } + }> +
) diff --git a/ide/templates/index.html b/ide/templates/index.html index 58f7de7eb..7423bd53f 100644 --- a/ide/templates/index.html +++ b/ide/templates/index.html @@ -12,6 +12,7 @@ + From 4b5469493867b59ad6f90c133c9ecf152d59b332 Mon Sep 17 00:00:00 2001 From: ZeroZeroJedenJeden <44526720+ZeroZeroJedenJeden@users.noreply.github.com> Date: Wed, 7 Nov 2018 06:41:23 +0100 Subject: [PATCH 2/2] Add Layer Filter exclusive to selected frameworks (#418) * Add files via upload * Add files via upload * Update adding_new_layers.md * Update adding_new_layers.md * Add files via upload * Add files via upload * Add files via upload * Add files via upload * Update adding_new_layers.md * Add files via upload * Add files via upload * Update filterbar.js * Add files via upload * Add files via upload * Add files via upload * Delete filterbar.js * Delete pane.js * Delete content.js * Add files via upload * Add files via upload * Update content.js * Add files via upload * Add files via upload * Add files via upload * Delete filterbar.js * Add files via upload * Add files via upload --- ide/static/css/searchbar_style.css | 22 ++++-- ide/static/css/style.css | 7 ++ ide/static/js/content.js | 12 ++-- ide/static/js/filterBar.js | 108 +++++++++++++++++++++++++++++ ide/static/js/pane.js | 2 +- ide/static/js/paneElement.js | 2 +- tutorials/adding_new_layers.md | 3 +- 7 files changed, 140 insertions(+), 16 deletions(-) create mode 100644 ide/static/js/filterBar.js diff --git a/ide/static/css/searchbar_style.css b/ide/static/css/searchbar_style.css index 3a304a166..d7e3c537a 100644 --- a/ide/static/css/searchbar_style.css +++ b/ide/static/css/searchbar_style.css @@ -1,13 +1,12 @@ .insert-layer-title { position: relative; - margin-top: 10px !important; - margin-bottom: 21px !important; } #layer-search-icon { - position: absolute; + position: relative; left: -5px; - top: 31px; + top: 0px; + float: left; } #layer-search-icon:hover { @@ -15,9 +14,11 @@ } #layer-search-input { - position: absolute; - top: 36px; - left: 20px; + position: relative; + top: 0px; + left: 0px; + width: 60%; + height: 25px; font-size: 15px; background: none; border: none; @@ -27,6 +28,13 @@ transition: 0.3s; } +.filter-button { + height: 24px !important; +} + +.filter-glyphicon { + top: 0px; +} .layer-search-input-selected { opacity: 1 !important; } diff --git a/ide/static/css/style.css b/ide/static/css/style.css index edbcda391..08adb3fa5 100644 --- a/ide/static/css/style.css +++ b/ide/static/css/style.css @@ -992,6 +992,13 @@ input[type="file"] { margin: 0px; } +.dropdown-menu .filter{ + width:88%; + font-weight: normal; + padding: 0px; + margin: 0px; +} + #sidebar-scroll::-webkit-scrollbar-track { background-color: #F5F5F5; diff --git a/ide/static/js/content.js b/ide/static/js/content.js index 8bb70b60f..36f0628eb 100644 --- a/ide/static/js/content.js +++ b/ide/static/js/content.js @@ -15,6 +15,7 @@ import UrlImportModal from './urlImportModal'; import UserProfile from './UserProfile'; import UpdateHistoryModal from './updateHistoryModal'; import CommentSidebar from './CommentSidebar'; +import FilterBar from './filterBar'; import $ from 'jquery' const infoStyle = { @@ -1307,17 +1308,16 @@ class Content extends React.Component { updateHistoryModal={this.updateHistoryModal} /> -
- -
INSERT LAYER
- search -
+
INSERT LAYER
+
+ +
- +
EXTRAS
Help diff --git a/ide/static/js/filterBar.js b/ide/static/js/filterBar.js new file mode 100644 index 000000000..287371468 --- /dev/null +++ b/ide/static/js/filterBar.js @@ -0,0 +1,108 @@ +import React from 'react'; +import $ from 'jquery' + +class FilterBar extends React.Component { + constructor(props) { + super(props); + this.changeEvent= this.changeEvent.bind(this); + } + +changeEvent(cbid) { + var kerasLayers = ["RNN_Button", "GRU_Button", "LSTM_Button", "Embed_Button", "ELU_Button", "Sigmoid_Button", + "ThresholdedReLU", "ReLU_Button", "PReLU_Button", "Softmax_Button", "BatchNorm_Button", "SELU_Button", + "GaussianNoise_Button", "GaussianDropout_Button", "AlphaDropout_Button", "TimeDistributed_Button", "TanH_Button", + "Bidirectional_Button", "RepeatVector_Button", "Masking_Button", "Permute_Button", "InnerProduct_Button", + "Deconvolution_Button", "Regularization_Button", "Softsign_Button", "Upsample_Button", "Pooling_Button", + "LocallyConnected_Button", "Input_Button", "Convolution_Button", "LRN_Button", "DepthwiseConv_Button", "Flatten_Button", + "Reshape_Button", "Concat_Button", "Softplus_Button", "HardSigmoid_Button", "Dropout_Button", "Eltwise_Button"]; + + var tensorFlowLayers = ["Input_Button", "Convolution_Button", "Pooling_Button", "DepthwiseConv_Button", "InnerProduct_Button", + "LRN_Button", "Concat_Button", "Flatten_Button", "BatchNorm_Button", "Deconvolution_Button", "Sigmoid_Button", + "Softplus_Button", "Softsign_Button", "ELU_Button", "ReLU_Button", "Softmax_Button", "TanH_Button", "SELU_Button", + "Dropout_Button", "Eltwise_Button"]; + + var caffeLayers = ["ImageData_Button", "HDF5Data_Button", "HDF5Output_Button", "Input_Button", "WindowData_Button", + "MemoryData_Button", "DummyData_Button", "Convolution_Button", "Pooling_Button", "SPP_Button", "Deconvolution_Button", + "Recurrent_Button", "RNN_Button", "LSTM_Button", "LRN_Button", "MVN_Button", "BatchNorm_Button", + "InnerProduct_Button", "Dropout_Button", "Embed_Button", "ReLU_Button", "PReLU_Button", "ELU_Button", + "Sigmoid_Button", "TanH_Button", "AbsVal_Button", "Power_Button", "Exp_Button", "Log_Button", "BNLL_Button", + "Threshold_Button", "Bias_Button", "Scale_Button", "Flatten_Button", "ThresholdedReLU", "Python_Button", + "Reshape_Button", "BatchReindex_Button", "Split_Button", "Concat_Button", "Eltwise_Button", "Filter_Button", + "Reduction_Button", "Silence_Button", "ArgMax_Button", "Softmax_Button", "MultinomialLogisticLoss_Button", + "InfogainLoss_Button", "SoftmaxWithLoss_Button", "EuclideanLoss_Button", "HingeLoss_Button", "Slice_Button", + "SigmoidCrossEntropyLoss_Button", "Accuracy_Button", "ContrastiveLoss_Button", "Data_Button", "Crop_Button"]; + var filterCheckBox_Keras = document.getElementById("filterCheckBox_Keras"); + var filterCheckBox_TensorFlow = document.getElementById("filterCheckBox_TensorFlow"); + var filterCheckBox_Caffe = document.getElementById("filterCheckBox_Caffe"); + var visible = []; + + let checkBox = document.getElementById(cbid); + checkBox.checked = !checkBox.checked; + + if (filterCheckBox_Keras.checked == false & filterCheckBox_TensorFlow.checked == false & filterCheckBox_Caffe.checked == false) { + for (let elem of $('.drowpdown-button')) { + elem.classList.remove("hide"); + } + } + if (filterCheckBox_Keras.checked == true) { + visible = visible.concat(kerasLayers); + } + if (filterCheckBox_TensorFlow.checked == true) { + visible = visible.concat(tensorFlowLayers); + } + if (filterCheckBox_Caffe.checked == true) { + visible = visible.concat(caffeLayers); + } + + for (let elem of $('.drowpdown-button')) { + for (let j = 0; j < visible.length; j++) { + let id = elem.id; + if (id == visible[j]) { + elem.classList.remove("hide"); + j = visible.length + 1; + } + else { + elem.classList.add("hide"); + } + } + } + + } + + render() { + return ( +
+ + search +
+ +
+
+ ) + } +} +export default FilterBar; diff --git a/ide/static/js/pane.js b/ide/static/js/pane.js index 0b5436407..cf35ba3fd 100644 --- a/ide/static/js/pane.js +++ b/ide/static/js/pane.js @@ -467,10 +467,10 @@ class Pane extends React.Component {
- ); } } + Pane.propTypes = { handleClick: React.PropTypes.func.isRequired, setDraggingLayer: React.PropTypes.func.isRequired diff --git a/ide/static/js/paneElement.js b/ide/static/js/paneElement.js index f1f8f98f2..0d77a0985 100644 --- a/ide/static/js/paneElement.js +++ b/ide/static/js/paneElement.js @@ -11,7 +11,7 @@ class PaneElement extends React.Component { render() { return (
your_layer_name``` this line will make your layer visible in Fabrik. +- Open [filterbar.js](https://github.com/Cloud-CV/Fabrik/blob/master/ide/static/js/filterbar.js) in a text editor, add ```"your_layer_id"``` to 1(or more) of 3 framework filter array ```var KerasLayers = [...]```, ```var TensorFlowLayers = [...]``` or ```var CaffeLayers = [...]```. This should be like this ```var KerasLayers = ["RNN_Button", "GRU_Button", "your_layer_id"]```. This arrays are placed inside ```changeEvent() {}``` function. ### Adding layer handling to the backend @@ -87,4 +88,4 @@ - Check the new layer inside the category you added it. See if all the parameters are properly displayed and usable as you wanted. - If everything is working fine commit your changes and push it to your fork then make a Pull Request. -- Congratulations! Happy contributing :-) \ No newline at end of file +- Congratulations! Happy contributing :-)