New directive to edit xml files

This commit is contained in:
JuanCarlos 2018-12-13 15:29:55 +01:00
parent a2f1cb9fbc
commit 635af9d7fa
10 changed files with 316 additions and 77 deletions

View File

@ -11,7 +11,6 @@
*/
import beautifier from '../../utils/json-beautifier';
import * as FileSaver from '../../services/file-saver';
import CodeMirror from '../../utils/codemirror/lib/codemirror';
export function GroupsController(
$scope,
@ -22,8 +21,7 @@ export function GroupsController(
appState,
shareAgent,
$document,
wzTableFilter,
$timeout
wzTableFilter
) {
$scope.$on('groupsIsReloaded', () => {
$scope.currentGroup = false;
@ -61,19 +59,6 @@ export function GroupsController(
const globalAgent = shareAgent.getAgent();
const load = async () => {
$scope.xmlCodeBox = CodeMirror.fromTextArea(
$document[0].getElementById('xml_box'),
{
lineNumbers: true,
matchClosing: true,
matchBrackets: true,
mode: 'text/xml',
theme: 'ttcn',
foldGutter: true,
styleSelectedText: true,
gutters: ['CodeMirror-foldgutter']
}
);
try {
// If come from agents
if (globalAgent) {
@ -197,33 +182,10 @@ export function GroupsController(
return;
};
const autoFormat = () => {
var totalLines = $scope.xmlCodeBox.lineCount();
var totalChars = $scope.xmlCodeBox.getTextArea().value.length;
$scope.xmlCodeBox.autoFormatRange({ line: 0, ch: 0 }, { line: totalLines, ch: totalChars });
}
$scope.editGroupAgentConfig = (group, params) => {
$timeout(function () { $scope.xmlCodeBox.refresh() });
$scope.editingGroupAgentConfig = true;
$scope.editingGroupAgentConfigItem = params.group;
try {
const xml = "<bookstore>\n<book>\n" +
"<title>Everyday Italian</title>\n" +
"<author>Giada De Laurentiis</author>\n" +
"<year>2005</year>\n" +
"</book>\n</bookstore>";
$scope.xmlCodeBox.setValue(xml);
autoFormat();
} catch (error) {
return false;
}
};
$scope.$on('editGroupAgentConfig', (group, params) => $scope.editGroupAgentConfig(group, params));
// Resetting the factory configuration
$scope.$on('$destroy', () => { });
$scope.$watch('lookingGroup', value => {
$scope.$watch('lookingGroup', value => {
if (!value) {
$scope.file = false;
$scope.filename = false;

View File

@ -22,3 +22,5 @@ import './wz-config-item/wz-config-item.less';
import './wz-tag-filter/wz-tag-filter';
import './wz-tag-filter/wz-tag-filter.less';
import './wz-config-viewer/wz-config-viewer';
import './wz-xml-file-editor/wz-xml-file-editor';
import './wz-xml-file-editor/wz-xml-file-editor.less';

View File

@ -38,6 +38,7 @@ app.directive('wzTable', function () {
extraLimit: '=extraLimit'
},
controller(
$rootScope,
$scope,
apiReq,
$timeout,
@ -255,7 +256,7 @@ app.directive('wzTable', function () {
};
$scope.editGroupAgentConfig = (ev, group) => {
$scope.$emit('editGroupAgentConfig', { group });
$rootScope.$emit('editXmlFile', { 'target' : group });
};
$scope.showConfirm = function (ev, agent) {

View File

@ -0,0 +1,15 @@
<div class='wzEmbedDialog' ng-show='!lookingGroup && editingFile'>
<div class='wzEmbedDialogHeader'>
<span>Edit <b>{{fileName}}</b> of <b>{{targetName}}</b></span>
<i aria-hidden='true' class='fa fa-fw fa-times cursor-pointer' ng-click='editingFile = false'></i>
</div>
<div ng-show='!loadingFile'>
<textarea id='xml_box'></textarea>
</div>
<div class='wzEmbedDialogFooter'>
<span ng-disabled='xmlHasErrors' ng-click='saveFile()' class='btn btn-primary'><i aria-hidden='true' class='fa fa-fw fa-save'></i>
Save file</span>
<span ng-show='xmlHasErrors' class='btn-danger'><i aria-hidden='true' class='fa fa-fw fa-exclamation-triangle'></i>
XML format error</span>
</div>
</div>

View File

@ -0,0 +1,128 @@
/*
* Wazuh app - Wazuh XML file editor
* Copyright (C) 2018 Wazuh, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Find more information about this on the LICENSE file.
*/
import template from './wz-xml-file-editor.html';
import CodeMirror from '../../utils/codemirror/lib/codemirror';
import { uiModules } from 'ui/modules';
const app = uiModules.get('app/wazuh', []);
app.directive('wzXmlFileEditor', function () {
return {
restrict: 'E',
scope: {
loadPath: '=loadPath',
updatePath: '=updatePath',
fileName: '@fileName'
},
controller(
$rootScope,
$scope,
$timeout,
apiReq,
$document,
errorHandler
) {
$(document).ready(function () {
$scope.xmlCodeBox = CodeMirror.fromTextArea(
$document[0].getElementById('xml_box'),
{
lineNumbers: true,
matchClosing: true,
matchBrackets: true,
mode: 'text/xml',
theme: 'ttcn',
foldGutter: true,
styleSelectedText: true,
gutters: ['CodeMirror-foldgutter']
}
);
$scope.xmlHasErrors = false;
$scope.xmlCodeBox.on('change', () => {
checkXmlParseError();
});
const checkXmlParseError = () => {
try {
var parser = new DOMParser();
var xml = $scope.xmlCodeBox.getValue();
var xmlDoc = parser.parseFromString(xml, "text/xml");
$timeout(function () {
$scope.xmlHasErrors = xmlDoc.getElementsByTagName("parsererror").length > 0 ? true : false
}, 50);
} catch (error) {
errorHandler.handle(error, 'Error validating XML');
}
}
});
const autoFormat = () => {
var totalLines = $scope.xmlCodeBox.lineCount();
$scope.xmlCodeBox.autoFormatRange({ 'line': 0, 'ch': 0 }, { line: totalLines - 1 });
$scope.xmlCodeBox.setCursor(0);
}
const updateFile = async () => {
try {
/*const response = await this.apiReq.request(
'PUT',
$scope.updatePath,
{}
);*/
const response = "";
return response;
} catch (error) {
this.apiInputBox.model = [];
}
}
$scope.saveFile = async () => {
const response = await updateFile();
console.log(response);
}
const fetchFile = async () => {
try {
/*const xml = = await this.apiReq.request(
'GET',
$scope.loadPath,
{}
);*/
const xml = "<agent_config>" +
"\n" +
"<!-- Shared agent configuration here -->\n" +
"\n" +
"</agent_config>";
return xml;
} catch (error) {
errorHandler.handle(error, 'Fetch file error');
}
}
$scope.editXmlFile = async (item, params) => {
$scope.editingFile = true;
$scope.loadingFile = true;
$scope.targetName = params.target.name;
try {
const xml = await fetchFile();
$scope.xmlCodeBox.setValue(xml);
autoFormat();
$scope.loadingFile = false;
$timeout(function () { $scope.xmlCodeBox.refresh() }, 100);
} catch (error) {
errorHandler.handle(error, 'Fetch file error');
}
};
$rootScope.$on('editXmlFile', (item, params) => $scope.editXmlFile(item, params));
},
template
};
});

View File

@ -0,0 +1,45 @@
/*
* Wazuh app - Wazuh XML file editor stylesheet
* Copyright (C) 2018 Wazuh, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Find more information about this on the LICENSE file.
*/
/* -------------------------------------------------------------------------- */
/* ------------------ Wazuh XML file editor stylesheet ---------------------- */
/* -------------------------------------------------------------------------- */
.wzEmbedDialog{
width: 50vw;
height: 400px;
position: fixed;
bottom: 0;
border: 1px solid #e0e0e0;
min-width: 500px;
margin: 5px;
border-radius: 3px;
right: 0;
box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.1) !important;
}
.wzEmbedDialogHeader{
padding: 15px;
}
.wzEmbedDialogHeader b{
font-weight: 600;
}
.wzEmbedDialogHeader i{
float: right;
}
.wzEmbedDialogFooter{
padding: 10px;
border-top: 1px solid #dddddd;
}

View File

@ -507,29 +507,4 @@ md-sidenav {
.visualization {
overflow: hidden !important;
}
.wzEmbedDialog{
width: 50vw;
height: 400px;
position: fixed;
bottom: 0;
border: 1px solid #e0e0e0;
min-width: 500px;
margin: 5px;
border-radius: 3px;
right: 0;
box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.1) !important;
}
.wzEmbedDialogHeader{
padding: 15px;
}
.wzEmbedDialogHeader i{
float: right;
}
.wzEmbedDialogFooter{
padding: 10px;
border-top: 1px solid #dddddd;
}

View File

@ -90,18 +90,10 @@
<!-- End CSV Download button section for groups -->
<!-- XML editor for group agents -->
<div class="wzEmbedDialog" ng-show="!lookingGroup && editingGroupAgentConfig" >
<div class="wzEmbedDialogHeader">
<span>Edit <b>agent.conf</b> of {{editingGroupAgentConfigItem.name}}</span>
<i aria-hidden="true" class="fa fa-fw fa-times cursor-pointer" ng-click="editingGroupAgentConfig = false"></i>
</div>
<textarea id="xml_box"></textarea>
<div class="wzEmbedDialogFooter">
<span ng-click="saveGroupAgentConfig()" class="btn btn-primary"><i aria-hidden="true" class="fa fa-fw fa-save"></i> Save file</span>
</div>
</div>
<wz-xml-file-editor file-name='agents.conf' load-path='hola' update-path='adios'>
</wz-xml-file-editor>
<!-- XML editor for group agents -->
<!-- Group agents table -->
<div layout="row" ng-if="lookingGroup && groupsSelectedTab==='agents' && currentGroup" class="md-padding">
<wz-table flex path="'/agents/groups/' + currentGroup.name" keys="['id','name','ip','status','os.name','os.version','version']"

View File

@ -0,0 +1,118 @@
(function(mod) {
if (typeof exports == 'object' && typeof module == 'object')
// CommonJS
mod(require('./lib/codemirror'), require('./foldcode'));
else if (typeof define == 'function' && define.amd)
// AMD
define(['./lib/codemirror', './foldcode'], mod);
// Plain browser env
else mod(CodeMirror);
})(function(CodeMirror) {
'use strict';
CodeMirror.extendMode("css", {
commentStart: "/*",
commentEnd: "*/",
newlineAfterToken: function(type, content) {
return /^[;{}]$/.test(content);
}
});
CodeMirror.extendMode("javascript", {
commentStart: "/*",
commentEnd: "*/",
// FIXME semicolons inside of for
newlineAfterToken: function(type, content, textAfter, state) {
if (this.jsonMode) {
return /^[\[,{]$/.test(content) || /^}/.test(textAfter);
} else {
if (content == ";" && state.lexical && state.lexical.type == ")") return false;
return /^[;{}]$/.test(content) && !/^;/.test(textAfter);
}
}
});
CodeMirror.extendMode("xml", {
commentStart: "<!--",
commentEnd: "-->",
newlineAfterToken: function(type, content, textAfter) {
return type == "tag" && />$/.test(content) || /^</.test(textAfter);
}
});
// Comment/uncomment the specified range
CodeMirror.defineExtension("commentRange", function (isComment, from, to) {
var cm = this, curMode = CodeMirror.innerMode(cm.getMode(), cm.getTokenAt(from).state).mode;
cm.operation(function() {
if (isComment) { // Comment range
cm.replaceRange(curMode.commentEnd, to);
cm.replaceRange(curMode.commentStart, from);
if (from.line == to.line && from.ch == to.ch) // An empty comment inserted - put cursor inside
cm.setCursor(from.line, from.ch + curMode.commentStart.length);
} else { // Uncomment range
var selText = cm.getRange(from, to);
var startIndex = selText.indexOf(curMode.commentStart);
var endIndex = selText.lastIndexOf(curMode.commentEnd);
if (startIndex > -1 && endIndex > -1 && endIndex > startIndex) {
// Take string till comment start
selText = selText.substr(0, startIndex)
// From comment start till comment end
+ selText.substring(startIndex + curMode.commentStart.length, endIndex)
// From comment end till string end
+ selText.substr(endIndex + curMode.commentEnd.length);
}
cm.replaceRange(selText, from, to);
}
});
});
// Applies automatic mode-aware indentation to the specified range
CodeMirror.defineExtension("autoIndentRange", function (from, to) {
var cmInstance = this;
this.operation(function () {
for (var i = from.line; i <= to.line; i++) {
cmInstance.indentLine(i, "smart");
}
});
});
// Applies automatic formatting to the specified range
CodeMirror.defineExtension("autoFormatRange", function (from, to) {
var cm = this;
var outer = cm.getMode(), text = cm.getRange(from, to).split("\n");
var state = CodeMirror.copyState(outer, cm.getTokenAt(from).state);
var tabSize = cm.getOption("tabSize");
var out = "", lines = 0, atSol = from.ch == 0;
function newline() {
out += "\n";
atSol = true;
++lines;
}
for (var i = 0; i < text.length; ++i) {
var stream = new CodeMirror.StringStream(text[i], tabSize);
while (!stream.eol()) {
var inner = CodeMirror.innerMode(outer, state);
var style = outer.token(stream, state), cur = stream.current();
stream.start = stream.pos;
if (!atSol || /\S/.test(cur)) {
out += cur;
atSol = false;
}
if (!atSol && inner.mode.newlineAfterToken &&
inner.mode.newlineAfterToken(style, cur, stream.string.slice(stream.pos) || text[i+1] || "", inner.state))
newline();
}
if (!stream.pos && outer.blankLine) outer.blankLine(state);
if (!atSol) newline();
}
cm.operation(function () {
cm.replaceRange(out, from, to);
for (var cur = from.line + 1, end = from.line + lines; cur <= end; ++cur)
cm.indentLine(cur, "smart");
cm.setSelection(from, cm.getCursor(false));
});
});
});

View File

@ -6,6 +6,7 @@ import './foldgutter.css';
import './ttcn.css';
import './javascript.js';
import './xml.js';
import './formatting.js';
import './brace-fold.js';
import './foldcode.js';
import './foldgutter.js';