Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support managing source component in the UI #1624

Merged
merged 1 commit into from
Aug 6, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/user_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -1505,8 +1505,9 @@ optional arguments:
-i COMPONENT_FILE, --import COMPONENT_FILE
Path to the source component file which contains
multiple file paths. Each file path start with a '+'
(path should be filtered) or '-' (path should not be
filtered) sign. E.g.:
(results from this path should be listed) or '-'
(results from this path should not be listed) sign.
E.g.:
+/a/b/x.cpp
-/a/b/
Please see the User guide for more information.
Expand Down
7 changes: 4 additions & 3 deletions libcodechecker/libhandlers/cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -742,9 +742,10 @@ def __register_add(parser):
required=True,
help="Path to the source component file which "
"contains multiple file paths. Each file "
"path start with a '+' (path should be "
"filtered) or '-' (path should not be "
"filtered) sign. E.g.: \n"
"path start with a '+' (results from this "
"path should be listed) or '-' (results from "
"this path should not be listed) sign. "
"E.g.: \n"
" +/a/b/x.cpp\n"
" -/a/b/\n"
"Please see the User guide for more "
Expand Down
365 changes: 365 additions & 0 deletions www/scripts/codecheckerviewer/SourceComponentManager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,365 @@
// -------------------------------------------------------------------------
// The CodeChecker Infrastructure
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// -------------------------------------------------------------------------

define([
'dojo/dom-construct',
'dojo/dom-attr',
'dojo/dom-class',
'dojo/_base/declare',
'dojo/data/ItemFileWriteStore',
'dojox/grid/DataGrid',
'dijit/ConfirmDialog',
'dijit/form/Button',
'dijit/form/SimpleTextarea',
'dijit/form/TextBox',
'dijit/layout/BorderContainer',
'dijit/layout/ContentPane'],
function (dom, domAttr, domClass, declare, ItemFileWriteStore, DataGrid,
ConfirmDialog, Button, SimpleTextarea, TextBox, BorderContainer,
ContentPane) {

var DeleteComponentDialog = declare(ConfirmDialog, {
constructor : function () {
this._confirmLabel = new ContentPane({
class : 'delete-confirm-text',
innerHTML : '<span class="warningHeader">You have selected to ' +
'delete a source component!</span>'
});
},

postCreate : function () {
this.inherited(arguments);
this.addChild(this._confirmLabel);
},

onExecute : function () {
CC_SERVICE.removeSourceComponent(this.componentName);
this.listSourceComponent.refreshGrid();
}
});

var EditSourceComponent = declare(ContentPane, {
postCreate : function () {
var that = this;

this._errorMessage = dom.create('div', {class : 'mbox mbox-error hide'});
dom.place(this._errorMessage, this.domNode);

this._componentName = new TextBox({
class : 'component-name',
placeHolder : 'Name of the source component...'
});
this._placeFormElement(this._componentName, 'name', 'Name*');

this._componentValue = new SimpleTextarea({
class : 'component-value',
rows : 10,
placeholder : 'Value of the source component. Each line must start '
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would recommend a small rephrasing in the placeholder:

with a "+" (results from this path should be listed) or "-" (results from this path should not be listed) sign. E.g.: '+/a/b/x.cpp or -/a/b/'

I would do this rephrasing in the command line and in the user guide too.

+ 'with a "+" (results from this path should be listed) or '
+ '"-" (results from this path should not be listed) sign. '
+ 'E.g.: +/a/b/x.cpp or -/a/b/'
});
this._placeFormElement(this._componentValue, 'value', 'Value*');

this._componentDescription = new TextBox({
class : 'component-description',
placeHolder : 'Description of the source component...'
});
this._placeFormElement(this._componentDescription, 'description',
'Description');

this._btnCreate = new Button({
class : 'btn-save',
label : 'Save',
onClick : function () {
var componentName = that._componentName.get('value');
if (!componentName) {
return that.showError('Component name can not be empty!');
}

var componentValue = that._componentValue.get('value');
if (!componentValue) {
return that.showError('Component value can not be empty!');
}

if (!that.isValidComponentValue(componentValue)) {
return that.showError('Component value format is invalid! Every'
+ 'line should start with + or - sign followed by one or more '
+ 'character.');
}

var componentDescription = that._componentDescription.get('value');

// Remove the original component because the user would like to change
// the name.
var origComponentName = that._origComponent
? that._origComponent.name[0]
: null;

if (origComponentName && origComponentName !== componentName) {
CC_SERVICE.removeSourceComponent(origComponentName);
}

CC_SERVICE.addSourceComponent(componentName, componentValue,
componentDescription, function (success) {
if (success) {
that.showSuccess('The component has been successfully ' +
'created/edited!');
that.sourceComponentManager.updateNeeded = true;

// Reset items of the filter tooltip.
that.sourceComponentFilter._filterTooltip.reset();
} else {
that.showError('Failed to create/edit component!');
}
}).fail(function (jsReq, status, exc) {
if (status === 'parsererror') {
that.showError(exc.message);
}
});
}
});
this.addChild(this._btnCreate);
},

isValidComponentValue : function (value) {
var lines = value.split(/\r|\n/);
for (var i = 0; i < lines.length; ++i) {
if (!lines[i].startsWith('+') && !lines[i].startsWith('-') ||
lines[i].trim().length < 2
) {
return false;
}
}
return true;
},

hideError : function () {
domClass.add(this._errorMessage, 'hide');
},

showSuccess : function (msg) {
this._errorMessage.innerHTML = msg;
domClass.remove(this._errorMessage, 'hide');
domClass.remove(this._errorMessage, 'mbox-error');
domClass.add(this._errorMessage, 'mbox-success');
},

showError : function (msg) {
this._errorMessage.innerHTML = msg;
domClass.remove(this._errorMessage, 'hide');
domClass.remove(this._errorMessage, 'mbox-success');
domClass.add(this._errorMessage, 'mbox-error');
},

_placeFormElement : function (element, key, label) {
this.addChild(element);

var container = dom.create('div', {
class : 'formElement'
}, this.containerNode);

if (label) {
var labelNode = dom.create('label', {
class : 'formLabel bold',
innerHTML : label + ': '
}, container);

if (key) {
domAttr.set(labelNode, 'for', key);
}
}

dom.place(element.domNode, container);
},

init : function (component) {
if (component) {
this._componentName.set('value', component.name[0]);
this._componentValue.set('value', component.value[0]);
this._componentDescription.set('value', component.description[0]);
this._origComponent = component;
} else {
this._componentName.set('value', null);
this._componentValue.set('value', null);
this._componentDescription.set('value', null);
this._origComponent = null;
}

this.hideError();
}
});

function componentValueFormatter(value) {
return value.split(/\r|\n/).map(function (line) {
if (line.startsWith('+')) {
return '<span style="color:green">' + line + '</span>';
} else {
return '<span style="color:red">' + line + '</span>';
}
}).join('<br/>');
}

var ListSourceComponent = declare(DataGrid, {
constructor : function () {
this.store = new ItemFileWriteStore({
data : { identifier : 'id', items : [] }
});

this.structure = [
{ name : 'Name', field : 'name', styles : 'text-align: left;', width : '100%' },
{ name : 'Value', field : 'value', styles : 'text-align: left;', width : '100%', formatter: componentValueFormatter },
{ name : 'Description', field : 'description', styles : 'text-align: left;', width : '100%' },
{ name : '&nbsp;', field : 'editIcon', cellClasses : 'status', width : '20px', noresize : true },
{ name : '&nbsp;', field : 'deleteIcon', cellClasses : 'status', width : '20px', noresize : true }
];

this.focused = true;
this.selectable = true;
this.keepSelection = true;
this.escapeHTMLInData = false;
this.autoHeight = false;
},

postCreate : function () {
this.inherited(arguments);

this._confirmDeleteDialog = new DeleteComponentDialog({
title : 'Confirm deletion of component',
listSourceComponent : this
});
},

onRowClick : function (evt) {
var item = this.getItem(evt.rowIndex);
switch (evt.cell.field) {
case 'editIcon':
this.editComponent(item);
break;
case 'deleteIcon':
this.removeComponent(item.name[0]);
break;
}
},

removeComponent : function (name) {
this._confirmDeleteDialog.componentName = name;
this._confirmDeleteDialog.show();
},

editComponent : function (component) {
this.sourceComponentManager.showNewComponentPage(component);
},

refreshGrid : function () {
var that = this;

this.store.fetch({
onComplete : function (sourceComponents) {
sourceComponents.forEach(function (component) {
that.store.deleteItem(component);
});
that.store.save();
}
});

CC_SERVICE.getSourceComponents(null, function (sourceComponents) {
sourceComponents.forEach(function (item) {
that._addSourceComponent(item);
});
});
},

_addSourceComponent : function (component) {
this.store.newItem({
id : component.name,
name : component.name,
value : component.value,
description : component.description,
editIcon : '<span class="customIcon edit"></span>',
deleteIcon : '<span class="customIcon delete"></span>'
});
},

onShow : function () {
if (this.sourceComponentManager.updateNeeded) {
this.refreshGrid();
this.sourceComponentManager.updateNeeded = false;
}
}
});

return declare(ContentPane, {
updateNeeded : true,

postCreate : function () {
var that = this;

this._wrapper = new BorderContainer({
class : 'component-manager-wrapper'
});

this.addChild(this._wrapper);

this._btnNew = new Button({
class : 'btn-new',
region : 'top',
label : 'New',
onClick : function () {
that.showNewComponentPage();
}
});

this._btnBack = new Button({
class : 'btn-back',
region : 'top',
label : 'Back',
onClick : function () {
that.showListOfComponentPage();
}
});

this._editSourceComponent = new EditSourceComponent({
region : 'center',
sourceComponentManager : this,
sourceComponentFilter : this.sourceComponentFilter
});

this._listSourceComponent = new ListSourceComponent({
region : 'center',
editSourceComponent : this._editSourceComponent,
sourceComponentManager : this
});
},

showNewComponentPage : function (component) {
this._clearDom();

this._editSourceComponent.init(component);

this._wrapper.addChild(this._btnBack);
this._wrapper.addChild(this._editSourceComponent);
},

showListOfComponentPage : function () {
this._clearDom();

this._wrapper.addChild(this._btnNew);
this._wrapper.addChild(this._listSourceComponent);
this._listSourceComponent.onShow();
},

refreshGrid : function () {
this._listSourceComponent.refreshGrid();
},

_clearDom : function () {
this._wrapper.getChildren().forEach(function (child) {
this.removeChild(child);
}, this);
}
});
});
Loading