2017-09-20 23:51:28 +02:00
/ * *
* Copyright JS Foundation and other contributors , http : //js.foundation
*
* Licensed under the Apache License , Version 2.0 ( the "License" ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an "AS IS" BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
* * /
RED . projects . settings = ( function ( ) {
var trayWidth = 700 ;
var settingsVisible = false ;
var panes = [ ] ;
function addPane ( options ) {
panes . push ( options ) ;
}
// TODO: DRY - tab-info.js
function addTargetToExternalLinks ( el ) {
$ ( el ) . find ( "a" ) . each ( function ( el ) {
var href = $ ( this ) . attr ( 'href' ) ;
if ( /^https?:/ . test ( href ) ) {
$ ( this ) . attr ( 'target' , '_blank' ) ;
}
} ) ;
return el ;
}
function show ( initialTab ) {
if ( settingsVisible ) {
return ;
}
2018-01-12 22:00:11 +01:00
if ( ! RED . user . hasPermission ( "projects.write" ) ) {
RED . notify ( RED . _ ( "user.errors.notAuthorized" ) , "error" ) ;
return ;
}
2017-09-20 23:51:28 +02:00
settingsVisible = true ;
var tabContainer ;
var trayOptions = {
2018-02-05 11:58:09 +01:00
title : "Project Settings" , // RED._("menu.label.userSettings"),, // TODO: nls
2017-09-20 23:51:28 +02:00
buttons : [
{
id : "node-dialog-ok" ,
text : RED . _ ( "common.label.close" ) ,
class : "primary" ,
click : function ( ) {
RED . tray . close ( ) ;
}
}
] ,
resize : function ( dimensions ) {
trayWidth = dimensions . width ;
} ,
open : function ( tray ) {
var project = RED . projects . getActiveProject ( ) ;
2017-12-04 12:42:44 +01:00
2017-09-20 23:51:28 +02:00
var trayBody = tray . find ( '.editor-tray-body' ) ;
var settingsContent = $ ( '<div></div>' ) . appendTo ( trayBody ) ;
var tabContainer = $ ( '<div></div>' , { id : "user-settings-tabs-container" } ) . appendTo ( settingsContent ) ;
$ ( '<ul></ul>' , { id : "user-settings-tabs" } ) . appendTo ( tabContainer ) ;
var settingsTabs = RED . tabs . create ( {
id : "user-settings-tabs" ,
vertical : true ,
onchange : function ( tab ) {
setTimeout ( function ( ) {
$ ( "#user-settings-tabs-content" ) . children ( ) . hide ( ) ;
$ ( "#" + tab . id ) . show ( ) ;
if ( tab . pane . focus ) {
tab . pane . focus ( ) ;
}
} , 50 ) ;
}
} ) ;
var tabContents = $ ( '<div></div>' , { id : "user-settings-tabs-content" } ) . appendTo ( settingsContent ) ;
panes . forEach ( function ( pane ) {
settingsTabs . addTab ( {
id : "project-settings-tab-" + pane . id ,
label : pane . title ,
pane : pane
} ) ;
pane . get ( project ) . hide ( ) . appendTo ( tabContents ) ;
} ) ;
settingsContent . i18n ( ) ;
2017-09-26 23:51:08 +02:00
settingsTabs . activateTab ( "project-settings-tab-" + ( initialTab || 'main' ) )
2017-09-20 23:51:28 +02:00
$ ( "#sidebar-shade" ) . show ( ) ;
} ,
close : function ( ) {
settingsVisible = false ;
panes . forEach ( function ( pane ) {
if ( pane . close ) {
pane . close ( ) ;
}
} ) ;
$ ( "#sidebar-shade" ) . hide ( ) ;
} ,
show : function ( ) { }
}
if ( trayWidth !== null ) {
trayOptions . width = trayWidth ;
}
RED . tray . show ( trayOptions ) ;
}
function editDescription ( activeProject , container ) {
RED . editor . editMarkdown ( {
title : RED . _ ( 'sidebar.project.editDescription' ) ,
2018-01-25 12:24:30 +01:00
header : $ ( '<span><i class="fa fa-book"></i> README.md</span>' ) ,
2017-09-20 23:51:28 +02:00
value : activeProject . description ,
complete : function ( v ) {
container . empty ( ) ;
2017-11-22 00:31:41 +01:00
var spinner = utils . addSpinnerOverlay ( container ) ;
2017-09-20 23:51:28 +02:00
var done = function ( err , res ) {
if ( err ) {
return editDescription ( activeProject , container ) ;
}
activeProject . description = v ;
updateProjectDescription ( activeProject , container ) ;
}
utils . sendRequest ( {
url : "projects/" + activeProject . name ,
type : "PUT" ,
responses : {
0 : function ( error ) {
done ( error , null ) ;
} ,
200 : function ( data ) {
done ( null , data ) ;
2017-12-11 18:05:12 +01:00
RED . sidebar . versionControl . refresh ( true ) ;
2017-09-20 23:51:28 +02:00
} ,
400 : {
2018-02-05 16:59:11 +01:00
'*' : function ( error ) {
utils . reportUnexpectedError ( error ) ;
2017-09-20 23:51:28 +02:00
done ( error , null ) ;
}
} ,
}
} , { description : v } ) . always ( function ( ) {
spinner . remove ( ) ;
} ) ;
}
} ) ;
}
function updateProjectDescription ( activeProject , container ) {
container . empty ( ) ;
2017-12-04 12:42:44 +01:00
var desc ;
if ( activeProject . description ) {
desc = marked ( activeProject . description ) ;
} else {
desc = '<span class="node-info-none">' + 'No description available' + '</span>' ;
}
2017-09-20 23:51:28 +02:00
var description = addTargetToExternalLinks ( $ ( '<span class="bidiAware" dir=\"' + RED . text . bidi . resolveBaseTextDir ( desc ) + '">' + desc + '</span>' ) ) . appendTo ( container ) ;
description . find ( ".bidiAware" ) . contents ( ) . filter ( function ( ) { return this . nodeType === 3 && this . textContent . trim ( ) !== "" } ) . wrap ( "<span></span>" ) ;
}
function editSummary ( activeProject , summary , container ) {
var editButton = container . prev ( ) ;
editButton . hide ( ) ;
container . empty ( ) ;
2017-12-20 15:37:34 +01:00
var bg = $ ( '<span class="button-row" style="position: relative; float: right; margin-right:0;"></span>' ) . appendTo ( container ) ;
2017-09-20 23:51:28 +02:00
var input = $ ( '<input type="text" style="width: calc(100% - 150px); margin-right: 10px;">' ) . val ( summary || "" ) . appendTo ( container ) ;
$ ( '<button class="editor-button">Cancel</button>' )
. appendTo ( bg )
. click ( function ( evt ) {
evt . preventDefault ( ) ;
updateProjectSummary ( activeProject . summary , container ) ;
editButton . show ( ) ;
} ) ;
2017-10-19 22:38:53 +02:00
$ ( '<button class="editor-button">Save</button>' )
2017-09-20 23:51:28 +02:00
. appendTo ( bg )
. click ( function ( evt ) {
evt . preventDefault ( ) ;
var v = input . val ( ) ;
updateProjectSummary ( v , container ) ;
2017-11-22 00:31:41 +01:00
var spinner = utils . addSpinnerOverlay ( container ) ;
2017-09-20 23:51:28 +02:00
var done = function ( err , res ) {
if ( err ) {
spinner . remove ( ) ;
return editSummary ( activeProject , summary , container ) ;
}
activeProject . summary = v ;
spinner . remove ( ) ;
updateProjectSummary ( activeProject . summary , container ) ;
editButton . show ( ) ;
}
utils . sendRequest ( {
url : "projects/" + activeProject . name ,
type : "PUT" ,
responses : {
0 : function ( error ) {
done ( error , null ) ;
} ,
200 : function ( data ) {
2017-12-11 18:05:12 +01:00
RED . sidebar . versionControl . refresh ( true ) ;
2017-09-20 23:51:28 +02:00
done ( null , data ) ;
} ,
400 : {
2018-02-05 16:59:11 +01:00
'*' : function ( error ) {
utils . reportUnexpectedError ( error ) ;
2017-09-20 23:51:28 +02:00
done ( error , null ) ;
}
} ,
}
} , { summary : v } ) ;
} ) ;
}
function updateProjectSummary ( summary , container ) {
container . empty ( ) ;
if ( summary ) {
container . text ( summary ) . removeClass ( 'node-info-node' ) ;
} else {
2017-09-21 12:19:24 +02:00
container . text ( "No summary available" ) . addClass ( 'node-info-none' ) ; // TODO: nls
2017-09-20 23:51:28 +02:00
}
}
2017-09-21 12:19:24 +02:00
function createMainPane ( activeProject ) {
2017-09-20 23:51:28 +02:00
var pane = $ ( '<div id="project-settings-tab-main" class="project-settings-tab-pane node-help"></div>' ) ;
$ ( '<h1>' ) . text ( activeProject . name ) . appendTo ( pane ) ;
var summary = $ ( '<div style="position: relative">' ) . appendTo ( pane ) ;
var summaryContent = $ ( '<div></div>' , { style : "color: #999" } ) . appendTo ( summary ) ;
updateProjectSummary ( activeProject . summary , summaryContent ) ;
2018-01-12 22:00:11 +01:00
if ( RED . user . hasPermission ( "projects.write" ) ) {
2018-01-25 14:30:27 +01:00
$ ( '<button class="editor-button editor-button-small" style="float: right;">edit description</button>' )
2018-01-12 22:00:11 +01:00
. prependTo ( summary )
. click ( function ( evt ) {
evt . preventDefault ( ) ;
editSummary ( activeProject , activeProject . summary , summaryContent ) ;
} ) ;
}
2017-09-20 23:51:28 +02:00
$ ( '<hr>' ) . appendTo ( pane ) ;
var description = $ ( '<div class="node-help" style="position: relative"></div>' ) . appendTo ( pane ) ;
var descriptionContent = $ ( '<div>' , { style : "min-height: 200px" } ) . appendTo ( description ) ;
updateProjectDescription ( activeProject , descriptionContent ) ;
2018-01-12 22:00:11 +01:00
if ( RED . user . hasPermission ( "projects.write" ) ) {
2018-01-25 14:30:27 +01:00
$ ( '<button class="editor-button editor-button-small" style="float: right;">edit README.md</button>' )
2018-01-12 22:00:11 +01:00
. prependTo ( description )
. click ( function ( evt ) {
evt . preventDefault ( ) ;
editDescription ( activeProject , descriptionContent ) ;
} ) ;
}
2017-09-20 23:51:28 +02:00
return pane ;
}
2017-09-21 12:19:24 +02:00
function updateProjectDependencies ( activeProject , depsList ) {
depsList . editableList ( 'empty' ) ;
2017-09-26 23:51:08 +02:00
var totalCount = 0 ;
var unknownCount = 0 ;
var unusedCount = 0 ;
2018-01-22 14:46:11 +01:00
var notInstalledCount = 0 ;
2017-09-21 12:19:24 +02:00
for ( var m in modulesInUse ) {
if ( modulesInUse . hasOwnProperty ( m ) ) {
depsList . editableList ( 'addItem' , {
2018-01-22 14:46:11 +01:00
id : modulesInUse [ m ] . module ,
2017-09-21 12:19:24 +02:00
version : modulesInUse [ m ] . version ,
count : modulesInUse [ m ] . count ,
2018-01-22 14:46:11 +01:00
known : activeProject . dependencies . hasOwnProperty ( m ) ,
installed : true
2017-09-21 12:19:24 +02:00
} ) ;
2017-09-26 23:51:08 +02:00
totalCount ++ ;
if ( modulesInUse [ m ] . count === 0 ) {
unusedCount ++ ;
}
if ( ! activeProject . dependencies . hasOwnProperty ( m ) ) {
unknownCount ++ ;
}
2017-09-21 12:19:24 +02:00
}
}
if ( activeProject . dependencies ) {
for ( var m in activeProject . dependencies ) {
if ( activeProject . dependencies . hasOwnProperty ( m ) && ! modulesInUse . hasOwnProperty ( m ) ) {
2018-01-22 14:46:11 +01:00
var installed = ! ! RED . nodes . registry . getModule ( m ) ;
2017-09-21 12:19:24 +02:00
depsList . editableList ( 'addItem' , {
2018-01-22 14:46:11 +01:00
id : m ,
2017-09-21 12:19:24 +02:00
version : activeProject . dependencies [ m ] , //RED.nodes.registry.getModule(module).version,
count : 0 ,
2018-01-22 14:46:11 +01:00
known : true ,
installed : installed
2017-09-21 12:19:24 +02:00
} ) ;
2017-09-26 23:51:08 +02:00
totalCount ++ ;
2018-01-22 14:46:11 +01:00
if ( installed ) {
unusedCount ++ ;
} else {
notInstalledCount ++ ;
}
2017-09-21 12:19:24 +02:00
}
}
}
2018-01-22 14:46:11 +01:00
// if (notInstalledCount > 0) {
// depsList.editableList('addItem',{index:1, label:"Missing dependencies"}); // TODO: nls
// }
// if (unknownCount > 0) {
// depsList.editableList('addItem',{index:1, label:"Unlisted dependencies"}); // TODO: nls
// }
// if (unusedCount > 0) {
// depsList.editableList('addItem',{index:3, label:"Unused dependencies"}); // TODO: nls
// }
2017-09-26 23:51:08 +02:00
if ( totalCount === 0 ) {
depsList . editableList ( 'addItem' , { index : 0 , label : "None" } ) ; // TODO: nls
}
2017-09-21 12:19:24 +02:00
}
2018-01-22 14:46:11 +01:00
function saveDependencies ( depsList , container , dependencies , complete ) {
var activeProject = RED . projects . getActiveProject ( ) ;
var spinner = utils . addSpinnerOverlay ( container ) . addClass ( 'projects-dialog-spinner-contain' ) ;
var done = function ( err , res ) {
spinner . remove ( ) ;
if ( err ) {
return complete ( err ) ;
}
activeProject . dependencies = dependencies ;
RED . sidebar . versionControl . refresh ( true ) ;
complete ( ) ;
}
utils . sendRequest ( {
url : "projects/" + activeProject . name ,
type : "PUT" ,
responses : {
0 : function ( error ) {
done ( error , null ) ;
} ,
200 : function ( data ) {
RED . sidebar . versionControl . refresh ( true ) ;
done ( null , data ) ;
} ,
400 : {
'*' : function ( error ) {
done ( error , null ) ;
}
} ,
}
} , { dependencies : dependencies } ) ;
}
2017-09-21 12:19:24 +02:00
function editDependencies ( activeProject , depsJSON , container , depsList ) {
2017-09-26 23:51:08 +02:00
var json = depsJSON || JSON . stringify ( activeProject . dependencies || { } , "" , 4 ) ;
if ( json === "{}" ) {
json = "{\n\n}" ;
}
2017-09-21 12:19:24 +02:00
RED . editor . editJSON ( {
title : RED . _ ( 'sidebar.project.editDependencies' ) ,
2017-09-26 23:51:08 +02:00
value : json ,
2017-09-21 12:19:24 +02:00
requireValid : true ,
complete : function ( v ) {
try {
var parsed = JSON . parse ( v ) ;
2018-01-22 14:46:11 +01:00
saveDependencies ( depsList , container , parsed , function ( err ) {
2017-09-21 12:19:24 +02:00
if ( err ) {
2017-09-26 23:51:08 +02:00
return editDependencies ( activeProject , v , container , depsList ) ;
2017-09-21 12:19:24 +02:00
}
activeProject . dependencies = parsed ;
updateProjectDependencies ( activeProject , depsList ) ;
} ) ;
} catch ( err ) {
editDependencies ( activeProject , v , container , depsList ) ;
}
}
} ) ;
}
function createDependenciesPane ( activeProject ) {
var pane = $ ( '<div id="project-settings-tab-deps" class="project-settings-tab-pane node-help"></div>' ) ;
2018-01-12 22:00:11 +01:00
if ( RED . user . hasPermission ( "projects.write" ) ) {
$ ( '<button class="editor-button editor-button-small" style="margin-top:10px;float: right;">edit</button>' )
. appendTo ( pane )
. click ( function ( evt ) {
evt . preventDefault ( ) ;
editDependencies ( activeProject , null , pane , depsList )
} ) ;
}
2017-09-21 12:19:24 +02:00
var depsList = $ ( "<ol>" , { style : "position: absolute;top: 60px;bottom: 20px;left: 20px;right: 20px;" } ) . appendTo ( pane ) ;
depsList . editableList ( {
addButton : false ,
addItem : function ( row , index , entry ) {
// console.log(entry);
var headerRow = $ ( '<div>' , { class : "palette-module-header" } ) . appendTo ( row ) ;
if ( entry . label ) {
2017-09-26 23:51:08 +02:00
if ( entry . index === 0 ) {
headerRow . addClass ( "red-ui-search-empty" )
} else {
row . parent ( ) . addClass ( "palette-module-section" ) ;
}
2017-09-21 12:19:24 +02:00
headerRow . text ( entry . label ) ;
2018-01-22 14:46:11 +01:00
// if (RED.user.hasPermission("projects.write")) {
// if (entry.index === 1) {
// var addButton = $('<button class="editor-button editor-button-small palette-module-button">add to project</button>').appendTo(headerRow).click(function(evt) {
// evt.preventDefault();
// var deps = $.extend(true, {}, activeProject.dependencies);
// for (var m in modulesInUse) {
// if (modulesInUse.hasOwnProperty(m) && !modulesInUse[m].known) {
// deps[m] = modulesInUse[m].version;
// }
// }
// editDependencies(activeProject,JSON.stringify(deps,"",4),pane,depsList);
// });
// } else if (entry.index === 3) {
// var removeButton = $('<button class="editor-button editor-button-small palette-module-button">remove from project</button>').appendTo(headerRow).click(function(evt) {
// evt.preventDefault();
// var deps = $.extend(true, {}, activeProject.dependencies);
// for (var m in activeProject.dependencies) {
// if (activeProject.dependencies.hasOwnProperty(m) && !modulesInUse.hasOwnProperty(m)) {
// delete deps[m];
// }
// }
// editDependencies(activeProject,JSON.stringify(deps,"",4),pane,depsList);
// });
// }
// }
2017-09-21 12:19:24 +02:00
} else {
2017-09-26 23:51:08 +02:00
headerRow . addClass ( "palette-module-header" ) ;
2018-01-22 14:46:11 +01:00
if ( ! entry . installed ) {
headerRow . addClass ( "palette-module-not-installed" ) ;
} else if ( entry . count === 0 ) {
headerRow . addClass ( "palette-module-unused" ) ;
} else if ( ! entry . known ) {
headerRow . addClass ( "palette-module-unknown" ) ;
}
2017-09-21 12:19:24 +02:00
entry . element = headerRow ;
var titleRow = $ ( '<div class="palette-module-meta palette-module-name"></div>' ) . appendTo ( headerRow ) ;
2018-01-22 14:46:11 +01:00
var iconClass = "fa-cube" ;
if ( ! entry . installed ) {
iconClass = "fa-warning" ;
}
var icon = $ ( '<i class="fa ' + iconClass + '"></i>' ) . appendTo ( titleRow ) ;
2017-09-21 12:19:24 +02:00
entry . icon = icon ;
2018-05-11 15:05:52 +02:00
$ ( '<span>' ) . text ( entry . id ) . appendTo ( titleRow ) ;
2017-09-21 12:19:24 +02:00
var metaRow = $ ( '<div class="palette-module-meta palette-module-version"><i class="fa fa-tag"></i></div>' ) . appendTo ( headerRow ) ;
2018-05-11 15:05:52 +02:00
var versionSpan = $ ( '<span>' ) . text ( entry . version ) . appendTo ( metaRow ) ;
2018-01-22 14:46:11 +01:00
metaRow = $ ( '<div class="palette-module-meta"></div>' ) . appendTo ( headerRow ) ;
var buttons = $ ( '<div class="palette-module-button-group"></div>' ) . appendTo ( metaRow ) ;
if ( RED . user . hasPermission ( "projects.write" ) ) {
if ( ! entry . installed && RED . settings . theme ( 'palette.editable' ) !== false ) {
$ ( '<a href="#" class="editor-button editor-button-small">install</a>' ) . appendTo ( buttons )
. click ( function ( evt ) {
evt . preventDefault ( ) ;
RED . palette . editor . install ( entry , row , function ( err ) {
if ( ! err ) {
entry . installed = true ;
var spinner = RED . utils . addSpinnerOverlay ( row , true ) ;
setTimeout ( function ( ) {
depsList . editableList ( 'removeItem' , entry ) ;
refreshModuleInUseCounts ( ) ;
entry . count = modulesInUse [ entry . id ] . count ;
depsList . editableList ( 'addItem' , entry ) ;
} , 500 ) ;
}
} ) ;
} )
} else if ( entry . known && entry . count === 0 ) {
$ ( '<a href="#" class="editor-button editor-button-small">remove from project</a>' ) . appendTo ( buttons )
. click ( function ( evt ) {
evt . preventDefault ( ) ;
var deps = $ . extend ( true , { } , activeProject . dependencies ) ;
delete deps [ entry . id ] ;
saveDependencies ( depsList , row , deps , function ( err ) {
if ( ! err ) {
row . fadeOut ( 200 , function ( ) {
depsList . editableList ( 'removeItem' , entry ) ;
} ) ;
} else {
console . log ( err ) ;
}
} ) ;
} ) ;
} else if ( ! entry . known ) {
$ ( '<a href="#" class="editor-button editor-button-small">add to project</a>' ) . appendTo ( buttons )
. click ( function ( evt ) {
evt . preventDefault ( ) ;
var deps = $ . extend ( true , { } , activeProject . dependencies ) ;
deps [ entry . id ] = modulesInUse [ entry . id ] . version ;
saveDependencies ( depsList , row , deps , function ( err ) {
if ( ! err ) {
buttons . remove ( ) ;
headerRow . removeClass ( "palette-module-unknown" ) ;
} else {
console . log ( err ) ;
}
} ) ;
} ) ;
}
2017-09-21 12:19:24 +02:00
}
}
} ,
sort : function ( A , B ) {
2018-01-22 14:46:11 +01:00
return A . id . localeCompare ( B . id ) ;
// if (A.index && B.index) {
// return A.index - B.index;
// }
// var Acategory = A.index?A.index:(A.known?(A.count>0?0:4):2);
// var Bcategory = B.index?B.index:(B.known?(B.count>0?0:4):2);
// if (Acategory === Bcategory) {
// return A.id.localeCompare(B.id);
// } else {
// return Acategory - Bcategory;
// }
2017-09-21 12:19:24 +02:00
}
} ) ;
updateProjectDependencies ( activeProject , depsList ) ;
return pane ;
}
2017-09-20 23:51:28 +02:00
2017-12-04 12:42:44 +01:00
function showProjectFileListing ( row , activeProject , current , filter , done ) {
2017-10-19 22:38:53 +02:00
var dialog ;
var dialogBody ;
var filesList ;
var selected ;
2017-11-22 00:31:41 +01:00
var container = $ ( '<div class="project-file-listing-container"></div>' , { style : "position: relative; min-height: 175px; height: 175px;" } ) . hide ( ) . appendTo ( row ) ;
var spinner = utils . addSpinnerOverlay ( container ) ;
2017-12-04 12:42:44 +01:00
$ . getJSON ( "projects/" + activeProject . name + "/files" , function ( result ) {
2017-10-19 22:38:53 +02:00
var fileNames = Object . keys ( result ) ;
2017-12-04 12:42:44 +01:00
fileNames = fileNames . filter ( function ( fn ) {
return ! result [ fn ] . status || ! /D/ . test ( result [ fn ] . status ) ;
} )
2017-10-19 22:38:53 +02:00
var files = { } ;
fileNames . sort ( ) ;
fileNames . forEach ( function ( file ) {
file . split ( "/" ) . reduce ( function ( r , v , i , arr ) { if ( v ) { if ( i < arr . length - 1 ) { r [ v ] = r [ v ] || { } ; } else { r [ v ] = true } return r [ v ] ; } } , files ) ;
2017-09-26 23:51:08 +02:00
} ) ;
2017-10-19 22:38:53 +02:00
var sortFiles = function ( key , value , fullPath ) {
var result = {
name : key || "/" ,
path : fullPath + ( fullPath ? "/" : "" ) + key ,
} ;
if ( value === true ) {
result . type = 'f' ;
return result ;
}
result . type = 'd' ;
result . children = [ ] ;
result . path = result . path ;
var files = Object . keys ( value ) ;
files . forEach ( function ( file ) {
result . children . push ( sortFiles ( file , value [ file ] , result . path ) ) ;
} )
result . children . sort ( function ( A , B ) {
if ( A . hasOwnProperty ( "children" ) && ! B . hasOwnProperty ( "children" ) ) {
return - 1 ;
} else if ( ! A . hasOwnProperty ( "children" ) && B . hasOwnProperty ( "children" ) ) {
return 1 ;
2017-09-26 23:51:08 +02:00
}
2017-10-19 22:38:53 +02:00
return A . name . localeCompare ( B . name ) ;
} )
return result ;
}
var files = sortFiles ( "" , files , "" ) ;
2017-12-04 12:42:44 +01:00
createFileSubList ( container , files . children , current , filter , done , "height: 175px" ) ;
2017-10-19 22:38:53 +02:00
spinner . remove ( ) ;
} ) ;
2017-11-22 00:31:41 +01:00
return container ;
2017-10-19 22:38:53 +02:00
}
2017-11-22 00:31:41 +01:00
2017-12-04 12:42:44 +01:00
function createFileSubList ( container , files , current , filter , onselect , style ) {
2017-10-19 22:38:53 +02:00
style = style || "" ;
var list = $ ( '<ol>' , { class : "projects-dialog-file-list" , style : style } ) . appendTo ( container ) . editableList ( {
addButton : false ,
scrollOnAdd : false ,
addItem : function ( row , index , entry ) {
var header = $ ( '<div></div>' , { class : "projects-dialog-file-list-entry" } ) . appendTo ( row ) ;
if ( entry . children ) {
$ ( '<span class="projects-dialog-file-list-entry-folder"><i class="fa fa-angle-right"></i> <i class="fa fa-folder-o"></i></span>' ) . appendTo ( header ) ;
if ( entry . children . length > 0 ) {
var children = $ ( '<div></div>' , { style : "padding-left: 20px;" } ) . appendTo ( row ) ;
if ( current . indexOf ( entry . path + "/" ) === 0 ) {
header . addClass ( "expanded" ) ;
} else {
children . hide ( ) ;
}
2017-12-04 12:42:44 +01:00
createFileSubList ( children , entry . children , current , filter , onselect ) ;
2017-10-19 22:38:53 +02:00
header . addClass ( "selectable" ) ;
header . click ( function ( e ) {
if ( $ ( this ) . hasClass ( "expanded" ) ) {
$ ( this ) . removeClass ( "expanded" ) ;
children . slideUp ( 200 ) ;
} else {
$ ( this ) . addClass ( "expanded" ) ;
children . slideDown ( 200 ) ;
}
2017-09-26 23:51:08 +02:00
2017-10-19 22:38:53 +02:00
} ) ;
2017-09-26 23:51:08 +02:00
2017-10-19 22:38:53 +02:00
}
} else {
var fileIcon = "fa-file-o" ;
var fileClass = "" ;
if ( /\.json$/i . test ( entry . name ) ) {
fileIcon = "fa-file-code-o"
} else if ( /\.md$/i . test ( entry . name ) ) {
fileIcon = "fa-book" ;
} else if ( /^\.git/i . test ( entry . name ) ) {
fileIcon = "fa-code-fork" ;
header . addClass ( "projects-dialog-file-list-entry-file-type-git" ) ;
}
$ ( '<span class="projects-dialog-file-list-entry-file"> <i class="fa ' + fileIcon + '"></i></span>' ) . appendTo ( header ) ;
2017-12-04 12:42:44 +01:00
if ( filter . test ( entry . name ) ) {
header . addClass ( "selectable" ) ;
if ( entry . path === current ) {
header . addClass ( "selected" ) ;
}
header . click ( function ( e ) {
$ ( ".projects-dialog-file-list-entry.selected" ) . removeClass ( "selected" ) ;
$ ( this ) . addClass ( "selected" ) ;
onselect ( entry . path ) ;
} )
header . dblclick ( function ( e ) {
e . preventDefault ( ) ;
onselect ( entry . path , true ) ;
} )
} else {
header . addClass ( "unselectable" ) ;
2017-10-19 22:38:53 +02:00
}
}
$ ( '<span class="projects-dialog-file-list-entry-name" style=""></span>' ) . text ( entry . name ) . appendTo ( header ) ;
}
} ) ;
if ( ! style ) {
list . parent ( ) . css ( "overflow-y" , "" ) ;
2017-09-26 23:51:08 +02:00
}
2017-10-19 22:38:53 +02:00
files . forEach ( function ( f ) {
list . editableList ( 'addItem' , f ) ;
} )
}
2017-09-26 23:51:08 +02:00
2017-10-19 22:38:53 +02:00
// function editFiles(activeProject, container,flowFile, flowFileLabel) {
// var editButton = container.children().first();
// editButton.hide();
//
// var flowFileInput = $('<input id="" type="text" style="width: calc(100% - 300px);">').val(flowFile).insertAfter(flowFileLabel);
//
// var flowFileInputSearch = $('<button class="editor-button" style="margin-left: 10px"><i class="fa fa-folder-open-o"></i></button>')
// .insertAfter(flowFileInput)
// .click(function(e) {
// showProjectFileListing(activeProject,'Select flow file',flowFileInput.val(),function(result) {
// flowFileInput.val(result);
// checkFiles();
// })
// })
//
// var checkFiles = function() {
// saveButton.toggleClass('disabled',flowFileInput.val()==="");
// saveButton.prop('disabled',flowFileInput.val()==="");
// }
// flowFileInput.on("change keyup paste",checkFiles);
// flowFileLabel.hide();
//
// var bg = $('<span class="button-group" style="position: relative; float: right; margin-right:0;"></span>').prependTo(container);
// $('<button class="editor-button">Cancel</button>')
// .appendTo(bg)
// .click(function(evt) {
// evt.preventDefault();
//
// flowFileLabel.show();
// flowFileInput.remove();
// flowFileInputSearch.remove();
// bg.remove();
// editButton.show();
// });
// var saveButton = $('<button class="editor-button">Save</button>')
// .appendTo(bg)
// .click(function(evt) {
// evt.preventDefault();
// var newFlowFile = flowFileInput.val();
// var newCredsFile = credentialsFileInput.val();
2017-11-22 00:31:41 +01:00
// var spinner = utils.addSpinnerOverlay(container);
2017-10-19 22:38:53 +02:00
// var done = function(err,res) {
// if (err) {
// spinner.remove();
// return;
// }
// activeProject.summary = v;
// spinner.remove();
// flowFileLabel.text(newFlowFile);
// flowFileLabel.show();
// flowFileInput.remove();
// flowFileInputSearch.remove();
// bg.remove();
// editButton.show();
// }
// // utils.sendRequest({
// // url: "projects/"+activeProject.name,
// // type: "PUT",
// // responses: {
// // 0: function(error) {
// // done(error,null);
// // },
// // 200: function(data) {
// // done(null,data);
// // },
// // 400: {
// // 'unexpected_error': function(error) {
// // done(error,null);
// // }
// // },
// // }
// // },{summary:v});
// });
//
//
// checkFiles();
//
// }
function createFilesSection ( activeProject , pane ) {
var title = $ ( '<h3></h3>' ) . text ( "Files" ) . appendTo ( pane ) ;
var filesContainer = $ ( '<div class="user-settings-section"></div>' ) . appendTo ( pane ) ;
2018-01-12 22:00:11 +01:00
if ( RED . user . hasPermission ( "projects.write" ) ) {
var editFilesButton = $ ( '<button class="editor-button editor-button-small" style="float: right;">edit</button>' )
. appendTo ( title )
. click ( function ( evt ) {
evt . preventDefault ( ) ;
formButtons . show ( ) ;
editFilesButton . hide ( ) ;
flowFileLabelText . hide ( ) ;
flowFileInput . show ( ) ;
flowFileInputSearch . show ( ) ;
credFileLabel . hide ( ) ;
credFileInput . show ( ) ;
flowFileInput . focus ( ) ;
// credentialStateLabel.parent().hide();
credentialStateLabel . addClass ( "uneditable-input" ) ;
$ ( ".user-settings-row-credentials" ) . show ( ) ;
credentialStateLabel . css ( 'height' , 'auto' ) ;
credentialFormRows . hide ( ) ;
credentialSecretButtons . show ( ) ;
} ) ;
}
2017-10-19 22:38:53 +02:00
var row ;
// Flow files
row = $ ( '<div class="user-settings-row"></div>' ) . appendTo ( filesContainer ) ;
$ ( '<label for=""></label>' ) . text ( 'Flow' ) . appendTo ( row ) ;
var flowFileLabel = $ ( '<div class="uneditable-input" style="padding:0">' ) . appendTo ( row ) ;
var flowFileLabelText = $ ( '<span style="display:inline-block; padding: 6px">' ) . text ( activeProject . files . flow ) . appendTo ( flowFileLabel ) ;
var flowFileInput = $ ( '<input id="" type="text" style="margin-bottom: 0;width: 100%; border: none;">' ) . val ( activeProject . files . flow ) . hide ( ) . appendTo ( flowFileLabel ) ;
2017-11-22 00:31:41 +01:00
var flowFileInputSearch = $ ( '<button class="editor-button" style="border-top-right-radius: 4px; border-bottom-right-radius: 4px; width: 36px; height: 34px; position: absolute; top: -1px; right: -1px;"><i class="fa fa-folder-open-o"></i></button>' )
2017-10-19 22:38:53 +02:00
. hide ( )
. appendTo ( flowFileLabel )
. click ( function ( e ) {
if ( $ ( this ) . hasClass ( 'selected' ) ) {
$ ( this ) . removeClass ( 'selected' ) ;
2017-11-22 00:31:41 +01:00
flowFileLabel . find ( '.project-file-listing-container' ) . slideUp ( 200 , function ( ) {
$ ( this ) . remove ( ) ;
flowFileLabel . css ( 'height' , '' ) ;
} ) ;
2017-10-19 22:38:53 +02:00
flowFileLabel . css ( 'color' , '' ) ;
} else {
$ ( this ) . addClass ( 'selected' ) ;
flowFileLabel . css ( 'color' , 'inherit' ) ;
2017-12-04 12:42:44 +01:00
var fileList = showProjectFileListing ( flowFileLabel , activeProject , flowFileInput . val ( ) , /.*\.json$/ , function ( result , isDblClick ) {
2017-10-19 22:38:53 +02:00
if ( result ) {
flowFileInput . val ( result ) ;
}
if ( isDblClick ) {
$ ( flowFileInputSearch ) . click ( ) ;
}
checkFiles ( ) ;
2017-11-22 00:31:41 +01:00
} ) ;
flowFileLabel . css ( 'height' , 'auto' ) ;
setTimeout ( function ( ) {
fileList . slideDown ( 200 ) ;
} , 50 ) ;
2017-10-19 22:38:53 +02:00
}
} )
row = $ ( '<div class="user-settings-row"></div>' ) . appendTo ( filesContainer ) ;
$ ( '<label for=""></label>' ) . text ( 'Credentials' ) . appendTo ( row ) ;
var credFileLabel = $ ( '<div class="uneditable-input">' ) . text ( activeProject . files . credentials ) . appendTo ( row ) ;
var credFileInput = $ ( '<div class="uneditable-input">' ) . text ( activeProject . files . credentials ) . hide ( ) . insertAfter ( credFileLabel ) ;
var checkFiles = function ( ) {
var saveDisabled ;
var currentFlowValue = flowFileInput . val ( ) ;
var m = /^(.+?)(\.[^.]*)?$/ . exec ( currentFlowValue ) ;
if ( m ) {
credFileInput . text ( m [ 1 ] + "_cred" + ( m [ 2 ] || ".json" ) ) ;
} else if ( currentFlowValue === "" ) {
credFileInput . text ( "" ) ;
}
var isFlowInvalid = currentFlowValue === "" ||
/\.\./ . test ( currentFlowValue ) ||
/\/$/ . test ( currentFlowValue ) ;
2017-09-26 23:51:08 +02:00
2017-10-19 22:38:53 +02:00
saveDisabled = isFlowInvalid || credFileInput . text ( ) === "" ;
2017-09-26 23:51:08 +02:00
2017-10-19 22:38:53 +02:00
if ( credentialSecretExistingInput . is ( ":visible" ) ) {
credentialSecretExistingInput . toggleClass ( "input-error" , credentialSecretExistingInput . val ( ) === "" ) ;
saveDisabled = saveDisabled || credentialSecretExistingInput . val ( ) === "" ;
2017-09-26 23:51:08 +02:00
}
2017-10-19 22:38:53 +02:00
if ( credentialSecretNewInput . is ( ":visible" ) ) {
credentialSecretNewInput . toggleClass ( "input-error" , credentialSecretNewInput . val ( ) === "" ) ;
saveDisabled = saveDisabled || credentialSecretNewInput . val ( ) === "" ;
2017-09-26 23:51:08 +02:00
}
2017-10-19 22:38:53 +02:00
flowFileInput . toggleClass ( "input-error" , isFlowInvalid ) ;
credFileInput . toggleClass ( "input-error" , credFileInput . text ( ) === "" ) ;
saveButton . toggleClass ( 'disabled' , saveDisabled ) ;
saveButton . prop ( 'disabled' , saveDisabled ) ;
2017-09-26 23:51:08 +02:00
}
2017-10-19 22:38:53 +02:00
flowFileInput . on ( "change keyup paste" , checkFiles ) ;
2017-09-26 23:51:08 +02:00
2017-10-19 22:38:53 +02:00
if ( ! activeProject . files . flow ) {
$ ( '<span class="form-warning"><i class="fa fa-warning"></i> Missing</span>' ) . appendTo ( flowFileLabelText ) ;
}
if ( ! activeProject . files . credentials ) {
$ ( '<span class="form-warning"><i class="fa fa-warning"></i> Missing</span>' ) . appendTo ( credFileLabel ) ;
2017-09-26 23:51:08 +02:00
}
2017-10-19 22:38:53 +02:00
row = $ ( '<div class="user-settings-row"></div>' ) . appendTo ( filesContainer ) ;
$ ( '<label></label>' ) . appendTo ( row ) ;
var credentialStateLabel = $ ( '<span><i class="user-settings-credentials-state-icon fa"></i> <span class="user-settings-credentials-state"></span></span>' ) . appendTo ( row ) ;
var credentialSecretButtons = $ ( '<span class="button-group" style="margin-left: -72px;">' ) . hide ( ) . appendTo ( row ) ;
credentialStateLabel . css ( 'color' , '#666' ) ;
credentialSecretButtons . css ( 'vertical-align' , 'top' ) ;
var credentialSecretResetButton = $ ( '<button class="editor-button" style="vertical-align: top; width: 36px; margin-bottom: 10px"><i class="fa fa-trash-o"></i></button>' )
. appendTo ( credentialSecretButtons )
. click ( function ( e ) {
e . preventDefault ( ) ;
if ( ! $ ( this ) . hasClass ( 'selected' ) ) {
credentialSecretNewInput . val ( "" ) ;
credentialSecretExistingRow . hide ( ) ;
credentialSecretNewRow . show ( ) ;
$ ( this ) . addClass ( "selected" ) ;
credentialSecretEditButton . removeClass ( "selected" ) ;
credentialResetLabel . show ( ) ;
credentialResetWarning . show ( ) ;
credentialSetLabel . hide ( ) ;
credentialChangeLabel . hide ( ) ;
credentialFormRows . show ( ) ;
} else {
$ ( this ) . removeClass ( "selected" ) ;
credentialFormRows . hide ( ) ;
}
checkFiles ( ) ;
} ) ;
2017-11-22 00:31:41 +01:00
var credentialSecretEditButton = $ ( '<button class="editor-button" style="border-top-right-radius: 4px; border-bottom-right-radius: 4px; vertical-align: top; width: 36px; margin-bottom: 10px"><i class="fa fa-pencil"></i></button>' )
2017-10-19 22:38:53 +02:00
. appendTo ( credentialSecretButtons )
. click ( function ( e ) {
e . preventDefault ( ) ;
if ( ! $ ( this ) . hasClass ( 'selected' ) ) {
credentialSecretExistingInput . val ( "" ) ;
credentialSecretNewInput . val ( "" ) ;
if ( activeProject . settings . credentialSecretInvalid || ! activeProject . settings . credentialsEncrypted ) {
credentialSetLabel . show ( ) ;
credentialChangeLabel . hide ( ) ;
credentialSecretExistingRow . hide ( ) ;
} else {
credentialSecretExistingRow . show ( ) ;
credentialSetLabel . hide ( ) ;
credentialChangeLabel . show ( ) ;
}
credentialSecretNewRow . show ( ) ;
credentialSecretEditButton . addClass ( "selected" ) ;
credentialSecretResetButton . removeClass ( "selected" ) ;
credentialResetLabel . hide ( ) ;
credentialResetWarning . hide ( ) ;
credentialFormRows . show ( ) ;
} else {
$ ( this ) . removeClass ( "selected" ) ;
credentialFormRows . hide ( ) ;
}
checkFiles ( ) ;
} )
row = $ ( '<div class="user-settings-row user-settings-row-credentials"></div>' ) . hide ( ) . appendTo ( filesContainer ) ;
var credentialFormRows = $ ( '<div>' , { style : "margin-top:10px" } ) . hide ( ) . appendTo ( credentialStateLabel ) ;
var credentialSetLabel = $ ( '<div style="margin: 20px 0 10px 5px;">Set the encryption key:</div>' ) . hide ( ) . appendTo ( credentialFormRows ) ;
var credentialChangeLabel = $ ( '<div style="margin: 20px 0 10px 5px;">Change the encryption key:</div>' ) . hide ( ) . appendTo ( credentialFormRows ) ;
var credentialResetLabel = $ ( '<div style="margin: 20px 0 10px 5px;">Reset the encryption key:</div>' ) . hide ( ) . appendTo ( credentialFormRows ) ;
var credentialSecretExistingRow = $ ( '<div class="user-settings-row user-settings-row-credentials"></div>' ) . appendTo ( credentialFormRows ) ;
$ ( '<label for=""></label>' ) . text ( 'Current key' ) . appendTo ( credentialSecretExistingRow ) ;
var credentialSecretExistingInput = $ ( '<input type="password">' ) . appendTo ( credentialSecretExistingRow )
. on ( "change keyup paste" , function ( ) {
2017-09-26 23:51:08 +02:00
if ( popover ) {
popover . close ( ) ;
popover = null ;
}
2017-10-19 22:38:53 +02:00
checkFiles ( ) ;
2017-09-26 23:51:08 +02:00
} ) ;
2017-10-19 22:38:53 +02:00
var credentialSecretNewRow = $ ( '<div class="user-settings-row user-settings-row-credentials"></div>' ) . appendTo ( credentialFormRows ) ;
$ ( '<label for=""></label>' ) . text ( 'New key' ) . appendTo ( credentialSecretNewRow ) ;
var credentialSecretNewInput = $ ( '<input type="password">' ) . appendTo ( credentialSecretNewRow ) . on ( "change keyup paste" , checkFiles ) ;
var credentialResetWarning = $ ( '<div class="form-tips form-warning" style="margin: 10px;"><i class="fa fa-warning"></i> This will delete all existing credentials</div>' ) . hide ( ) . appendTo ( credentialFormRows ) ;
var hideEditForm = function ( ) {
2017-11-22 00:31:41 +01:00
editFilesButton . show ( ) ;
2017-10-19 22:38:53 +02:00
formButtons . hide ( ) ;
flowFileLabelText . show ( ) ;
flowFileInput . hide ( ) ;
flowFileInputSearch . hide ( ) ;
credFileLabel . show ( ) ;
credFileInput . hide ( ) ;
// credentialStateLabel.parent().show();
credentialStateLabel . removeClass ( "uneditable-input" ) ;
credentialStateLabel . css ( 'height' , '' ) ;
2017-11-22 00:31:41 +01:00
flowFileInputSearch . removeClass ( 'selected' ) ;
flowFileLabel . find ( '.project-file-listing-container' ) . remove ( ) ;
flowFileLabel . css ( 'height' , '' ) ;
flowFileLabel . css ( 'color' , '' ) ;
2017-10-19 22:38:53 +02:00
$ ( ".user-settings-row-credentials" ) . hide ( ) ;
credentialFormRows . hide ( ) ;
credentialSecretButtons . hide ( ) ;
credentialSecretResetButton . removeClass ( "selected" ) ;
credentialSecretEditButton . removeClass ( "selected" ) ;
}
2017-12-20 15:37:34 +01:00
var formButtons = $ ( '<span class="button-row" style="position: relative; float: right; margin-right:0;"></span>' ) . hide ( ) . appendTo ( filesContainer ) ;
2017-11-22 00:31:41 +01:00
$ ( '<button class="editor-button">Cancel</button>' )
2017-10-19 22:38:53 +02:00
. appendTo ( formButtons )
2017-09-26 23:51:08 +02:00
. click ( function ( evt ) {
evt . preventDefault ( ) ;
2017-10-19 22:38:53 +02:00
hideEditForm ( ) ;
} ) ;
var saveButton = $ ( '<button class="editor-button">Save</button>' )
. appendTo ( formButtons )
. click ( function ( evt ) {
evt . preventDefault ( ) ;
2017-11-22 00:31:41 +01:00
var spinner = utils . addSpinnerOverlay ( filesContainer ) ;
2017-10-19 22:38:53 +02:00
var done = function ( err ) {
spinner . remove ( ) ;
if ( err ) {
2018-02-05 16:59:11 +01:00
utils . reportUnexpectedError ( err ) ;
2017-10-19 22:38:53 +02:00
return ;
}
flowFileLabelText . text ( flowFileInput . val ( ) ) ;
credFileLabel . text ( credFileInput . text ( ) ) ;
hideEditForm ( ) ;
2017-09-26 23:51:08 +02:00
}
var payload = {
2017-10-19 22:38:53 +02:00
files : {
flow : flowFileInput . val ( ) ,
credentials : credFileInput . text ( )
}
2017-09-26 23:51:08 +02:00
}
2017-10-19 22:38:53 +02:00
if ( credentialSecretResetButton . hasClass ( 'selected' ) ) {
payload . resetCredentialSecret = true ;
2017-09-26 23:51:08 +02:00
}
2017-10-19 22:38:53 +02:00
if ( credentialSecretResetButton . hasClass ( 'selected' ) || credentialSecretEditButton . hasClass ( 'selected' ) ) {
payload . credentialSecret = credentialSecretNewInput . val ( ) ;
if ( credentialSecretExistingInput . is ( ":visible" ) ) {
payload . currentCredentialSecret = credentialSecretExistingInput . val ( ) ;
2017-09-26 23:51:08 +02:00
}
}
2017-10-19 22:38:53 +02:00
// console.log(JSON.stringify(payload,null,4));
RED . deploy . setDeployInflight ( true ) ;
2017-09-26 23:51:08 +02:00
utils . sendRequest ( {
url : "projects/" + activeProject . name ,
type : "PUT" ,
responses : {
0 : function ( error ) {
2017-10-19 22:38:53 +02:00
done ( error ) ;
2017-09-26 23:51:08 +02:00
} ,
200 : function ( data ) {
2017-10-19 22:38:53 +02:00
activeProject = data ;
2017-12-11 18:05:12 +01:00
RED . sidebar . versionControl . refresh ( true ) ;
2017-10-19 22:38:53 +02:00
updateForm ( ) ;
done ( ) ;
2017-09-26 23:51:08 +02:00
} ,
400 : {
2017-10-19 22:38:53 +02:00
'credentials_load_failed' : function ( error ) {
done ( error ) ;
} ,
2017-09-26 23:51:08 +02:00
'missing_current_credential_key' : function ( error ) {
2017-10-19 22:38:53 +02:00
credentialSecretExistingInput . addClass ( "input-error" ) ;
2017-09-26 23:51:08 +02:00
popover = RED . popover . create ( {
2017-10-19 22:38:53 +02:00
target : credentialSecretExistingInput ,
2017-09-26 23:51:08 +02:00
direction : 'right' ,
size : 'small' ,
2017-10-19 22:38:53 +02:00
content : "Incorrect key" ,
autoClose : 3000
2017-09-26 23:51:08 +02:00
} ) . open ( ) ;
2017-10-19 22:38:53 +02:00
done ( error ) ;
2018-02-05 16:59:11 +01:00
} ,
'*' : function ( error ) {
done ( error ) ;
2017-09-26 23:51:08 +02:00
}
} ,
}
} , payload ) . always ( function ( ) {
2018-02-18 17:43:40 +01:00
setTimeout ( function ( ) {
RED . deploy . setDeployInflight ( false ) ;
} , 500 ) ;
2017-09-26 23:51:08 +02:00
} ) ;
2017-10-19 22:38:53 +02:00
} ) ;
var updateForm = function ( ) {
if ( activeProject . settings . credentialSecretInvalid ) {
credentialStateLabel . find ( ".user-settings-credentials-state-icon" ) . removeClass ( ) . addClass ( "user-settings-credentials-state-icon fa fa-warning" ) ;
credentialStateLabel . find ( ".user-settings-credentials-state" ) . text ( "Invalid encryption key" ) ;
} else if ( activeProject . settings . credentialsEncrypted ) {
credentialStateLabel . find ( ".user-settings-credentials-state-icon" ) . removeClass ( ) . addClass ( "user-settings-credentials-state-icon fa fa-lock" ) ;
credentialStateLabel . find ( ".user-settings-credentials-state" ) . text ( "Encryption enabled" ) ;
} else {
credentialStateLabel . find ( ".user-settings-credentials-state-icon" ) . removeClass ( ) . addClass ( "user-settings-credentials-state-icon fa fa-unlock" ) ;
credentialStateLabel . find ( ".user-settings-credentials-state" ) . text ( "Encryption disabled" ) ;
}
2018-01-18 23:17:48 +01:00
credentialSecretResetButton . toggleClass ( 'disabled' , ! activeProject . settings . credentialSecretInvalid && ! activeProject . settings . credentialsEncrypted ) ;
credentialSecretResetButton . prop ( 'disabled' , ! activeProject . settings . credentialSecretInvalid && ! activeProject . settings . credentialsEncrypted ) ;
2017-10-19 22:38:53 +02:00
}
2017-09-26 23:51:08 +02:00
2017-10-19 22:38:53 +02:00
checkFiles ( ) ;
updateForm ( ) ;
}
2017-11-22 00:31:41 +01:00
2017-12-08 17:31:42 +01:00
function createLocalBranchListSection ( activeProject , pane ) {
var localBranchContainer = $ ( '<div class="user-settings-section"></div>' ) . appendTo ( pane ) ;
$ ( '<h4></h4>' ) . text ( "Branches" ) . appendTo ( localBranchContainer ) ;
2017-12-20 15:37:34 +01:00
var row = $ ( '<div class="user-settings-row projects-dialog-list"></div>' ) . appendTo ( localBranchContainer ) ;
2017-12-08 17:31:42 +01:00
var branchList = $ ( '<ol>' ) . appendTo ( row ) . editableList ( {
2017-12-20 15:37:34 +01:00
height : 'auto' ,
2017-12-08 17:31:42 +01:00
addButton : false ,
scrollOnAdd : false ,
addItem : function ( row , index , entry ) {
2017-12-20 15:37:34 +01:00
var container = $ ( '<div class="projects-dialog-list-entry">' ) . appendTo ( row ) ;
2017-12-21 23:28:26 +01:00
if ( entry . empty ) {
container . addClass ( 'red-ui-search-empty' ) ;
container . text ( "No branches" ) ;
return ;
}
2017-12-20 15:37:34 +01:00
if ( entry . current ) {
container . addClass ( "current" ) ;
}
$ ( '<span class="entry-icon"><i class="fa fa-code-fork"></i></span>' ) . appendTo ( container ) ;
var content = $ ( '<span>' ) . appendTo ( container ) ;
var topRow = $ ( '<div>' ) . appendTo ( content ) ;
$ ( '<span class="entry-name">' ) . text ( entry . name ) . appendTo ( topRow ) ;
if ( entry . commit ) {
$ ( '<span class="entry-detail">' ) . text ( entry . commit . sha ) . appendTo ( topRow ) ;
}
2017-12-08 17:31:42 +01:00
if ( entry . remote ) {
2017-12-20 15:37:34 +01:00
var bottomRow = $ ( '<div>' ) . appendTo ( content ) ;
$ ( '<span class="entry-detail entry-remote-name">' ) . text ( entry . remote || "" ) . appendTo ( bottomRow ) ;
2017-12-08 17:31:42 +01:00
if ( entry . status . ahead + entry . status . behind > 0 ) {
2017-12-20 15:37:34 +01:00
$ ( '<span class="entry-detail">' +
2017-12-08 17:31:42 +01:00
'<i class="fa fa-long-arrow-up"></i> <span>' + entry . status . ahead + '</span> ' +
'<i class="fa fa-long-arrow-down"></i> <span>' + entry . status . behind + '</span>' +
2017-12-20 15:37:34 +01:00
'</span>' ) . appendTo ( bottomRow ) ;
2017-12-08 17:31:42 +01:00
}
}
2017-12-20 15:37:34 +01:00
if ( ! entry . current ) {
var tools = $ ( '<span class="entry-tools">' ) . appendTo ( container ) ;
$ ( '<button class="editor-button editor-button-small"><i class="fa fa-trash"></i></button>' )
2017-12-08 17:31:42 +01:00
. appendTo ( tools )
. click ( function ( e ) {
e . preventDefault ( ) ;
var spinner = utils . addSpinnerOverlay ( row ) . addClass ( 'projects-dialog-spinner-contain' ) ;
var notification = RED . notify ( "Are you sure you want to delete the local branch '" + entry . name + "'? This cannot be undone." , {
type : "warning" ,
modal : true ,
fixed : true ,
buttons : [
{
text : RED . _ ( "common.label.cancel" ) ,
click : function ( ) {
spinner . remove ( ) ;
notification . close ( ) ;
}
} , {
text : 'Delete branch' ,
click : function ( ) {
notification . close ( ) ;
var url = "projects/" + activeProject . name + "/branches/" + entry . name ;
var options = {
url : url ,
type : "DELETE" ,
responses : {
200 : function ( data ) {
row . fadeOut ( 200 , function ( ) {
branchList . editableList ( 'removeItem' , entry ) ;
spinner . remove ( ) ;
} ) ;
} ,
400 : {
'git_delete_branch_unmerged' : function ( error ) {
notification = RED . notify ( "The local branch '" + entry . name + "' has unmerged changes that will be lost. Are you sure you want to delete it?" , {
type : "warning" ,
modal : true ,
fixed : true ,
buttons : [
{
text : RED . _ ( "common.label.cancel" ) ,
click : function ( ) {
spinner . remove ( ) ;
notification . close ( ) ;
}
} , {
text : 'Delete unmerged branch' ,
click : function ( ) {
options . url += "?force=true" ;
notification . close ( ) ;
utils . sendRequest ( options ) ;
}
}
]
} ) ;
} ,
2018-02-05 16:59:11 +01:00
'*' : function ( error ) {
utils . reportUnexpectedError ( error ) ;
2017-12-08 17:31:42 +01:00
spinner . remove ( ) ;
}
} ,
}
}
utils . sendRequest ( options ) ;
}
}
]
} )
} )
}
}
} ) ;
2017-12-20 15:37:34 +01:00
2017-12-08 17:31:42 +01:00
$ . getJSON ( "projects/" + activeProject . name + "/branches" , function ( result ) {
if ( result . branches ) {
2017-12-21 23:28:26 +01:00
if ( result . branches . length > 0 ) {
result . branches . sort ( function ( A , B ) {
if ( A . current ) { return - 1 }
if ( B . current ) { return 1 }
return A . name . localeCompare ( B . name ) ;
} ) ;
result . branches . forEach ( function ( branch ) {
branchList . editableList ( 'addItem' , branch ) ;
} )
} else {
branchList . editableList ( 'addItem' , { empty : true } ) ;
}
2017-12-08 17:31:42 +01:00
}
} )
}
2017-12-20 15:37:34 +01:00
2017-12-04 12:42:44 +01:00
function createRemoteRepositorySection ( activeProject , pane ) {
2017-12-08 17:31:42 +01:00
$ ( '<h3></h3>' ) . text ( "Version Control" ) . appendTo ( pane ) ;
createLocalBranchListSection ( activeProject , pane ) ;
var repoContainer = $ ( '<div class="user-settings-section"></div>' ) . appendTo ( pane ) ;
var title = $ ( '<h4></h4>' ) . text ( "Git remotes" ) . appendTo ( repoContainer ) ;
2017-12-20 15:37:34 +01:00
var editRepoButton = $ ( '<button class="editor-button editor-button-small" style="float: right; margin-right: 10px;">add remote</button>' )
2017-11-22 00:31:41 +01:00
. appendTo ( title )
. click ( function ( evt ) {
2017-12-20 15:37:34 +01:00
editRepoButton . attr ( 'disabled' , true ) ;
2017-12-21 00:45:17 +01:00
addRemoteDialog . slideDown ( 200 , function ( ) {
addRemoteDialog [ 0 ] . scrollIntoView ( ) ;
2018-02-02 23:43:29 +01:00
if ( isEmpty ) {
remoteNameInput . val ( 'origin' ) ;
remoteURLInput . focus ( ) ;
} else {
remoteNameInput . focus ( ) ;
}
2018-01-31 23:34:18 +01:00
validateForm ( ) ;
2017-12-20 15:37:34 +01:00
} ) ;
2017-11-22 00:31:41 +01:00
} ) ;
2017-12-08 17:31:42 +01:00
2017-12-20 15:37:34 +01:00
var emptyItem = { empty : true } ;
2018-02-02 23:43:29 +01:00
var isEmpty = true ;
2017-12-21 00:45:17 +01:00
var row = $ ( '<div class="user-settings-row"></div>' ) . appendTo ( repoContainer ) ;
var addRemoteDialog = $ ( '<div class="projects-dialog-list-dialog"></div>' ) . hide ( ) . appendTo ( row ) ;
2017-12-20 15:37:34 +01:00
row = $ ( '<div class="user-settings-row projects-dialog-list"></div>' ) . appendTo ( repoContainer ) ;
2017-12-04 12:42:44 +01:00
var remotesList = $ ( '<ol>' ) . appendTo ( row ) ;
2017-11-22 00:31:41 +01:00
remotesList . editableList ( {
2017-12-20 15:37:34 +01:00
addButton : false ,
2017-12-04 12:42:44 +01:00
height : 'auto' ,
2017-12-20 15:37:34 +01:00
addItem : function ( row , index , entry ) {
var container = $ ( '<div class="projects-dialog-list-entry">' ) . appendTo ( row ) ;
if ( entry . empty ) {
container . addClass ( 'red-ui-search-empty' ) ;
container . text ( "No remotes" ) ;
return ;
} else {
$ ( '<span class="entry-icon"><i class="fa fa-globe"></i></span>' ) . appendTo ( container ) ;
var content = $ ( '<span>' ) . appendTo ( container ) ;
$ ( '<div class="entry-name">' ) . text ( entry . name ) . appendTo ( content ) ;
2017-12-04 12:42:44 +01:00
if ( entry . urls . fetch === entry . urls . push ) {
2017-12-20 15:37:34 +01:00
$ ( '<div class="entry-detail">' ) . text ( entry . urls . fetch ) . appendTo ( content ) ;
2017-12-04 12:42:44 +01:00
} else {
2017-12-20 15:37:34 +01:00
$ ( '<div class="entry-detail">' ) . text ( "fetch: " + entry . urls . fetch ) . appendTo ( content ) ;
$ ( '<div class="entry-detail">' ) . text ( "push: " + entry . urls . push ) . appendTo ( content ) ;
2017-12-04 12:42:44 +01:00
}
2017-12-20 15:37:34 +01:00
var tools = $ ( '<span class="entry-tools">' ) . appendTo ( container ) ;
$ ( '<button class="editor-button editor-button-small"><i class="fa fa-trash"></i></button>' )
. appendTo ( tools )
. click ( function ( e ) {
e . preventDefault ( ) ;
var spinner = utils . addSpinnerOverlay ( row ) . addClass ( 'projects-dialog-spinner-contain' ) ;
var notification = RED . notify ( "Are you sure you want to delete the remote '" + entry . name + "'?" , {
type : "warning" ,
modal : true ,
fixed : true ,
buttons : [
{
text : RED . _ ( "common.label.cancel" ) ,
click : function ( ) {
spinner . remove ( ) ;
notification . close ( ) ;
}
} , {
text : 'Delete remote' ,
click : function ( ) {
notification . close ( ) ;
2018-05-02 14:59:39 +02:00
if ( activeProject . git . branches . remote && activeProject . git . branches . remote . indexOf ( entry . name + "/" ) === 0 ) {
delete activeProject . git . branches . remote ;
}
if ( activeProject . git . branches . remoteAlt && activeProject . git . branches . remoteAlt . indexOf ( entry . name + "/" ) === 0 ) {
delete activeProject . git . branches . remoteAlt ;
}
2017-12-20 15:37:34 +01:00
var url = "projects/" + activeProject . name + "/remotes/" + entry . name ;
var options = {
url : url ,
type : "DELETE" ,
responses : {
200 : function ( data ) {
row . fadeOut ( 200 , function ( ) {
remotesList . editableList ( 'removeItem' , entry ) ;
setTimeout ( spinner . remove , 100 ) ;
if ( data . remotes . length === 0 ) {
2018-02-01 12:25:56 +01:00
delete activeProject . git . remotes ;
2018-02-02 23:43:29 +01:00
isEmpty = true ;
2017-12-20 15:37:34 +01:00
remotesList . editableList ( 'addItem' , emptyItem ) ;
2018-02-01 12:25:56 +01:00
} else {
activeProject . git . remotes = { } ;
data . remotes . forEach ( function ( remote ) {
var name = remote . name ;
delete remote . name ;
activeProject . git . remotes [ name ] = remote ;
} ) ;
2017-12-20 15:37:34 +01:00
}
2018-05-02 14:59:39 +02:00
delete activeProject . git . branches . remoteAlt ;
2018-02-05 11:10:26 +01:00
RED . sidebar . versionControl . refresh ( ) ;
2017-12-20 15:37:34 +01:00
} ) ;
} ,
400 : {
2018-02-05 16:59:11 +01:00
'*' : function ( error ) {
utils . reportUnexpectedError ( error ) ;
2017-12-20 15:37:34 +01:00
spinner . remove ( ) ;
}
} ,
}
}
utils . sendRequest ( options ) ;
}
}
2017-11-22 00:31:41 +01:00
2017-12-20 15:37:34 +01:00
]
} )
} ) ;
2017-12-04 12:42:44 +01:00
}
2017-12-20 15:37:34 +01:00
2017-12-04 12:42:44 +01:00
}
} ) ;
2017-11-22 00:31:41 +01:00
2017-12-20 15:37:34 +01:00
var validateForm = function ( ) {
var validName = /^[a-zA-Z0-9\-_]+$/ . test ( remoteNameInput . val ( ) ) ;
2018-02-02 23:43:29 +01:00
var repo = remoteURLInput . val ( ) ;
2018-02-01 11:42:04 +01:00
// var validRepo = /^(?:file|git|ssh|https?|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?[\w\.@:\/~_-]+(?:\.git)?(?:\/?|\#[\d\w\.\-_]+?)$/.test(remoteURLInput.val());
2018-02-02 23:43:29 +01:00
var validRepo = repo . length > 0 && ! /\s/ . test ( repo ) ;
if ( /^https?:\/\/[^/]+@/i . test ( repo ) ) {
remoteURLLabel . text ( "Do not include the username/password in the url" ) ;
validRepo = false ;
} else {
remoteURLLabel . text ( "https://, ssh:// or file://" ) ;
}
2017-12-20 15:37:34 +01:00
saveButton . attr ( 'disabled' , ( ! validName || ! validRepo ) )
remoteNameInput . toggleClass ( 'input-error' , remoteNameInputChanged && ! validName ) ;
2018-02-02 23:43:29 +01:00
remoteURLInput . toggleClass ( 'input-error' , remoteURLInputChanged && ! validRepo ) ;
2017-12-20 15:37:34 +01:00
if ( popover ) {
popover . close ( ) ;
popover = null ;
}
} ;
var popover ;
2018-02-02 23:43:29 +01:00
var remoteNameInputChanged = false ;
var remoteURLInputChanged = false ;
2017-12-21 00:45:17 +01:00
2017-12-20 15:37:34 +01:00
$ ( '<div class="projects-dialog-list-dialog-header">' ) . text ( 'Add remote' ) . appendTo ( addRemoteDialog ) ;
row = $ ( '<div class="user-settings-row"></div>' ) . appendTo ( addRemoteDialog ) ;
$ ( '<label for=""></label>' ) . text ( 'Remote name' ) . appendTo ( row ) ;
var remoteNameInput = $ ( '<input type="text">' ) . appendTo ( row ) . on ( "change keyup paste" , function ( ) {
remoteNameInputChanged = true ;
validateForm ( ) ;
} ) ;
$ ( '<label class="projects-edit-form-sublabel"><small>Must contain only A-Z 0-9 _ -</small></label>' ) . appendTo ( row ) . find ( "small" ) ;
row = $ ( '<div class="user-settings-row"></div>' ) . appendTo ( addRemoteDialog ) ;
2017-12-21 00:45:17 +01:00
$ ( '<label for=""></label>' ) . text ( 'URL' ) . appendTo ( row ) ;
2018-02-02 23:43:29 +01:00
var remoteURLInput = $ ( '<input type="text">' ) . appendTo ( row ) . on ( "change keyup paste" , function ( ) {
remoteURLInputChanged = true ;
validateForm ( )
} ) ;
var remoteURLLabel = $ ( '<label class="projects-edit-form-sublabel"><small>https://, ssh:// or file://</small></label>' ) . appendTo ( row ) . find ( "small" ) ;
2017-11-22 00:31:41 +01:00
var hideEditForm = function ( ) {
2017-12-20 15:37:34 +01:00
editRepoButton . attr ( 'disabled' , false ) ;
addRemoteDialog . hide ( ) ;
remoteNameInput . val ( "" ) ;
remoteURLInput . val ( "" ) ;
if ( popover ) {
popover . close ( ) ;
popover = null ;
}
2017-11-22 00:31:41 +01:00
}
2017-12-20 15:37:34 +01:00
var formButtons = $ ( '<span class="button-row" style="position: relative; float: right; margin: 10px;"></span>' )
. appendTo ( addRemoteDialog ) ;
2017-11-22 00:31:41 +01:00
$ ( '<button class="editor-button">Cancel</button>' )
. appendTo ( formButtons )
. click ( function ( evt ) {
evt . preventDefault ( ) ;
hideEditForm ( ) ;
} ) ;
2017-12-20 15:37:34 +01:00
var saveButton = $ ( '<button class="editor-button">Add remote</button>' )
2017-11-22 00:31:41 +01:00
. appendTo ( formButtons )
. click ( function ( evt ) {
evt . preventDefault ( ) ;
2017-12-20 15:37:34 +01:00
var spinner = utils . addSpinnerOverlay ( addRemoteDialog ) . addClass ( 'projects-dialog-spinner-contain' ) ;
2017-12-04 12:42:44 +01:00
2017-12-20 15:37:34 +01:00
var payload = {
name : remoteNameInput . val ( ) ,
url : remoteURLInput . val ( )
2017-12-04 12:42:44 +01:00
}
var done = function ( err ) {
spinner . remove ( ) ;
if ( err ) {
return ;
}
hideEditForm ( ) ;
}
// console.log(JSON.stringify(payload,null,4));
RED . deploy . setDeployInflight ( true ) ;
utils . sendRequest ( {
2017-12-20 15:37:34 +01:00
url : "projects/" + activeProject . name + "/remotes" ,
type : "POST" ,
2017-12-04 12:42:44 +01:00
responses : {
0 : function ( error ) {
done ( error ) ;
} ,
200 : function ( data ) {
2017-12-20 15:37:34 +01:00
activeProject . git . remotes = { } ;
data . remotes . forEach ( function ( remote ) {
var name = remote . name ;
delete remote . name ;
activeProject . git . remotes [ name ] = remote ;
} ) ;
2017-12-04 12:42:44 +01:00
updateForm ( ) ;
2018-02-05 11:10:26 +01:00
RED . sidebar . versionControl . refresh ( ) ;
2017-12-04 12:42:44 +01:00
done ( ) ;
} ,
400 : {
2017-12-20 15:37:34 +01:00
'git_remote_already_exists' : function ( error ) {
popover = RED . popover . create ( {
target : remoteNameInput ,
direction : 'right' ,
size : 'small' ,
content : "Remote already exists" ,
autoClose : 6000
} ) . open ( ) ;
remoteNameInput . addClass ( 'input-error' ) ;
done ( error ) ;
} ,
2018-02-05 16:59:11 +01:00
'*' : function ( error ) {
utils . reportUnexpectedError ( error ) ;
2017-12-04 12:42:44 +01:00
done ( error ) ;
}
} ,
}
} , payload ) ;
2017-11-22 00:31:41 +01:00
} ) ;
2018-02-02 23:43:29 +01:00
2017-12-04 12:42:44 +01:00
var updateForm = function ( ) {
remotesList . editableList ( 'empty' ) ;
2017-12-20 15:37:34 +01:00
var count = 0 ;
2017-12-04 12:42:44 +01:00
if ( activeProject . git . hasOwnProperty ( 'remotes' ) ) {
for ( var name in activeProject . git . remotes ) {
if ( activeProject . git . remotes . hasOwnProperty ( name ) ) {
2017-12-20 15:37:34 +01:00
count ++ ;
2017-12-04 12:42:44 +01:00
remotesList . editableList ( 'addItem' , { name : name , urls : activeProject . git . remotes [ name ] } ) ;
}
}
}
2018-02-02 23:43:29 +01:00
isEmpty = ( count === 0 ) ;
if ( isEmpty ) {
2017-12-20 15:37:34 +01:00
remotesList . editableList ( 'addItem' , emptyItem ) ;
}
2017-12-04 12:42:44 +01:00
}
updateForm ( ) ;
2017-11-22 00:31:41 +01:00
}
2017-10-19 22:38:53 +02:00
function createSettingsPane ( activeProject ) {
var pane = $ ( '<div id="project-settings-tab-settings" class="project-settings-tab-pane node-help"></div>' ) ;
createFilesSection ( activeProject , pane ) ;
2017-11-22 00:31:41 +01:00
// createLocalRepositorySection(activeProject,pane);
createRemoteRepositorySection ( activeProject , pane ) ;
2017-09-26 23:51:08 +02:00
return pane ;
}
2018-01-22 14:46:11 +01:00
function refreshModuleInUseCounts ( ) {
modulesInUse = { } ;
RED . nodes . eachNode ( _updateModulesInUse ) ;
RED . nodes . eachConfig ( _updateModulesInUse ) ;
}
function _updateModulesInUse ( n ) {
if ( ! /^subflow:/ . test ( n . type ) ) {
var module = RED . nodes . registry . getNodeSetForType ( n . type ) . module ;
if ( module !== 'node-red' ) {
if ( ! modulesInUse . hasOwnProperty ( module ) ) {
modulesInUse [ module ] = {
module : module ,
version : RED . nodes . registry . getModule ( module ) . version ,
count : 0 ,
known : false
}
}
modulesInUse [ module ] . count ++ ;
}
}
}
2017-09-26 23:51:08 +02:00
2018-01-22 14:46:11 +01:00
var popover ;
2017-09-20 23:51:28 +02:00
var utils ;
2017-09-21 12:19:24 +02:00
var modulesInUse = { } ;
2017-09-20 23:51:28 +02:00
function init ( _utils ) {
utils = _utils ;
addPane ( {
id : 'main' ,
2017-09-21 12:19:24 +02:00
title : "Project" , // TODO: nls
get : createMainPane ,
2017-09-20 23:51:28 +02:00
close : function ( ) { }
2017-09-21 12:19:24 +02:00
} ) ;
addPane ( {
id : 'deps' ,
title : "Dependencies" , // TODO: nls
get : createDependenciesPane ,
close : function ( ) { }
} ) ;
2017-09-26 23:51:08 +02:00
addPane ( {
id : 'settings' ,
title : "Settings" , // TODO: nls
get : createSettingsPane ,
close : function ( ) {
if ( popover ) {
popover . close ( ) ;
popover = null ;
}
}
} ) ;
2017-09-21 12:19:24 +02:00
2018-01-22 14:46:11 +01:00
RED . events . on ( 'nodes:add' , _updateModulesInUse ) ;
2017-09-21 12:19:24 +02:00
RED . events . on ( 'nodes:remove' , function ( n ) {
if ( ! /^subflow:/ . test ( n . type ) ) {
var module = RED . nodes . registry . getNodeSetForType ( n . type ) . module ;
if ( module !== 'node-red' && modulesInUse . hasOwnProperty ( module ) ) {
modulesInUse [ module ] . count -- ;
if ( modulesInUse [ module ] . count === 0 ) {
if ( ! modulesInUse [ module ] . known ) {
delete modulesInUse [ module ] ;
}
}
}
}
} )
2017-09-20 23:51:28 +02:00
}
return {
init : init ,
2017-09-26 23:51:08 +02:00
show : show ,
switchProject : function ( name ) {
// TODO: not ideal way to trigger this; should there be an editor-wide event?
modulesInUse = { } ;
}
2017-09-20 23:51:28 +02:00
} ;
} ) ( ) ;