mirror of https://github.com/gophish/gophish
- Cleanup up flash messages for templates. Will fix for everything else later.
- Also fixed #5 with the help from CKEditor devs (duplicate ticket found)pull/24/head
parent
759f86447d
commit
73e395cfe0
|
@ -49,7 +49,7 @@ func API_Reset(w http.ResponseWriter, r *http.Request) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "Error setting API Key", http.StatusInternalServerError)
|
http.Error(w, "Error setting API Key", http.StatusInternalServerError)
|
||||||
} else {
|
} else {
|
||||||
JSONResponse(w, models.Response{Success: true, Message: "API Key Successfully Reset", Data: u.ApiKey}, http.StatusOK)
|
JSONResponse(w, models.Response{Success: true, Message: "API Key successfully reset!", Data: u.ApiKey}, http.StatusOK)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,7 @@ func API_Campaigns_Id(w http.ResponseWriter, r *http.Request) {
|
||||||
if checkError(err, w, "Error deleting campaign", http.StatusInternalServerError) {
|
if checkError(err, w, "Error deleting campaign", http.StatusInternalServerError) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
JSONResponse(w, models.Response{Success: true, Message: "Campaign Deleted Successfully!"}, http.StatusOK)
|
JSONResponse(w, models.Response{Success: true, Message: "Campaign deleted successfully!"}, http.StatusOK)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,7 +162,7 @@ func API_Groups_Id(w http.ResponseWriter, r *http.Request) {
|
||||||
if checkError(err, w, "Error deleting group", http.StatusInternalServerError) {
|
if checkError(err, w, "Error deleting group", http.StatusInternalServerError) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
JSONResponse(w, models.Response{Success: true, Message: "Group Deleted Successfully"}, http.StatusOK)
|
JSONResponse(w, models.Response{Success: true, Message: "Group deleted successfully!"}, http.StatusOK)
|
||||||
case r.Method == "PUT":
|
case r.Method == "PUT":
|
||||||
// Change this to get from URL and uid (don't bother with id in r.Body)
|
// Change this to get from URL and uid (don't bother with id in r.Body)
|
||||||
g = models.Group{}
|
g = models.Group{}
|
||||||
|
@ -203,6 +203,7 @@ func API_Templates(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, err = models.GetTemplateByName(t.Name, ctx.Get(r, "user_id").(int64))
|
_, err = models.GetTemplateByName(t.Name, ctx.Get(r, "user_id").(int64))
|
||||||
|
fmt.Println(err)
|
||||||
if err != gorm.RecordNotFound {
|
if err != gorm.RecordNotFound {
|
||||||
JSONResponse(w, models.Response{Success: false, Message: "Template name already in use"}, http.StatusConflict)
|
JSONResponse(w, models.Response{Success: false, Message: "Template name already in use"}, http.StatusConflict)
|
||||||
return
|
return
|
||||||
|
@ -210,6 +211,14 @@ func API_Templates(w http.ResponseWriter, r *http.Request) {
|
||||||
t.ModifiedDate = time.Now()
|
t.ModifiedDate = time.Now()
|
||||||
t.UserId = ctx.Get(r, "user_id").(int64)
|
t.UserId = ctx.Get(r, "user_id").(int64)
|
||||||
err = models.PostTemplate(&t)
|
err = models.PostTemplate(&t)
|
||||||
|
if err == models.ErrTemplateNameNotSpecified {
|
||||||
|
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err == models.ErrTemplateMissingParameter {
|
||||||
|
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
if checkError(err, w, "Error inserting template", http.StatusInternalServerError) {
|
if checkError(err, w, "Error inserting template", http.StatusInternalServerError) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -233,7 +242,7 @@ func API_Templates_Id(w http.ResponseWriter, r *http.Request) {
|
||||||
if checkError(err, w, "Error deleting template", http.StatusInternalServerError) {
|
if checkError(err, w, "Error deleting template", http.StatusInternalServerError) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
JSONResponse(w, models.Response{Success: true, Message: "Template Deleted Successfully"}, http.StatusOK)
|
JSONResponse(w, models.Response{Success: true, Message: "Template deleted successfully!"}, http.StatusOK)
|
||||||
case r.Method == "PUT":
|
case r.Method == "PUT":
|
||||||
t = models.Template{}
|
t = models.Template{}
|
||||||
err = json.NewDecoder(r.Body).Decode(&t)
|
err = json.NewDecoder(r.Body).Decode(&t)
|
||||||
|
|
|
@ -23,7 +23,7 @@ type Template struct {
|
||||||
var ErrTemplateNameNotSpecified = errors.New("Template Name not specified")
|
var ErrTemplateNameNotSpecified = errors.New("Template Name not specified")
|
||||||
|
|
||||||
// ErrTemplateMissingParameter is thrown when a needed parameter is not provided
|
// ErrTemplateMissingParameter is thrown when a needed parameter is not provided
|
||||||
var ErrTemplateMissingParameter = errors.New("Need to specify at least plaintext or HTML format")
|
var ErrTemplateMissingParameter = errors.New("Need to specify at least plaintext or HTML content")
|
||||||
|
|
||||||
// Validate checks the given template to make sure values are appropriate and complete
|
// Validate checks the given template to make sure values are appropriate and complete
|
||||||
func (t *Template) Validate() error {
|
func (t *Template) Validate() error {
|
||||||
|
@ -89,7 +89,10 @@ func GetTemplateByName(n string, uid int64) (Template, error) {
|
||||||
// PostTemplate creates a new template in the database.
|
// PostTemplate creates a new template in the database.
|
||||||
func PostTemplate(t *Template) error {
|
func PostTemplate(t *Template) error {
|
||||||
// Insert into the DB
|
// Insert into the DB
|
||||||
err := db.Save(t).Error
|
if err := t.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = db.Save(t).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Logger.Println(err)
|
Logger.Println(err)
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -559,8 +559,8 @@ var GroupModalCtrl = function($scope, $modalInstance, $upload) {
|
||||||
|
|
||||||
app.controller('TemplateCtrl', function($scope, $modal, TemplateService, ngTableParams) {
|
app.controller('TemplateCtrl', function($scope, $modal, TemplateService, ngTableParams) {
|
||||||
$scope.errorFlash = function(message) {
|
$scope.errorFlash = function(message) {
|
||||||
$scope.flashes = [];
|
$scope.flashes = {"main" : [], "modal" : []};
|
||||||
$scope.flashes.push({
|
$scope.flashes.main.push({
|
||||||
"type": "danger",
|
"type": "danger",
|
||||||
"message": message,
|
"message": message,
|
||||||
"icon": "fa-exclamation-circle"
|
"icon": "fa-exclamation-circle"
|
||||||
|
@ -568,8 +568,8 @@ app.controller('TemplateCtrl', function($scope, $modal, TemplateService, ngTable
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.successFlash = function(message) {
|
$scope.successFlash = function(message) {
|
||||||
$scope.flashes = [];
|
$scope.flashes = {"main" : [], "modal" : []};;
|
||||||
$scope.flashes.push({
|
$scope.flashes.main.push({
|
||||||
"type": "success",
|
"type": "success",
|
||||||
"message": message,
|
"message": message,
|
||||||
"icon": "fa-check-circle"
|
"icon": "fa-check-circle"
|
||||||
|
@ -613,31 +613,22 @@ app.controller('TemplateCtrl', function($scope, $modal, TemplateService, ngTable
|
||||||
scope: $scope
|
scope: $scope
|
||||||
});
|
});
|
||||||
|
|
||||||
modalInstance.result.then(function(selectedItem) {
|
modalInstance.result.then(function(message) {
|
||||||
$scope.selected = selectedItem;
|
$scope.successFlash(message)
|
||||||
|
$scope.template = {
|
||||||
|
name: '',
|
||||||
|
html: '',
|
||||||
|
text: '',
|
||||||
|
};
|
||||||
}, function() {
|
}, function() {
|
||||||
console.log('closed')
|
$scope.template = {
|
||||||
|
name: '',
|
||||||
|
html: '',
|
||||||
|
text: '',
|
||||||
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.saveTemplate = function(template) {
|
|
||||||
var newTemplate = new TemplateService(template);
|
|
||||||
if ($scope.newTemplate) {
|
|
||||||
newTemplate.$save({}, function() {
|
|
||||||
$scope.templates.push(newTemplate);
|
|
||||||
$scope.mainTableParams.reload()
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
newTemplate.$update({
|
|
||||||
id: newTemplate.id
|
|
||||||
})
|
|
||||||
}
|
|
||||||
$scope.template = {
|
|
||||||
name: '',
|
|
||||||
html: '',
|
|
||||||
text: '',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
$scope.deleteTemplate = function(template) {
|
$scope.deleteTemplate = function(template) {
|
||||||
var deleteTemplate = new TemplateService(template);
|
var deleteTemplate = new TemplateService(template);
|
||||||
deleteTemplate.$delete({
|
deleteTemplate.$delete({
|
||||||
|
@ -653,10 +644,27 @@ app.controller('TemplateCtrl', function($scope, $modal, TemplateService, ngTable
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
var TemplateModalCtrl = function($scope, $upload, $modalInstance, $modal) {
|
var TemplateModalCtrl = function($scope, TemplateService, $upload, $modalInstance, $modal) {
|
||||||
$scope.editorOptions = {
|
$scope.editorOptions = {
|
||||||
fullPage: true,
|
fullPage: true,
|
||||||
allowedContent: true
|
allowedContent: true,
|
||||||
|
}
|
||||||
|
$scope.errorFlash = function(message) {
|
||||||
|
$scope.flashes = {"main" : [], "modal" : []};
|
||||||
|
$scope.flashes.modal.push({
|
||||||
|
"type": "danger",
|
||||||
|
"message": message,
|
||||||
|
"icon": "fa-exclamation-circle"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.successFlash = function(message) {
|
||||||
|
$scope.flashes = {"main" : [], "modal" : []};;
|
||||||
|
$scope.flashes.modal.push({
|
||||||
|
"type": "success",
|
||||||
|
"message": message,
|
||||||
|
"icon": "fa-check-circle"
|
||||||
|
})
|
||||||
}
|
}
|
||||||
$scope.onFileSelect = function($files) {
|
$scope.onFileSelect = function($files) {
|
||||||
angular.forEach($files, function(file, key) {
|
angular.forEach($files, function(file, key) {
|
||||||
|
@ -676,11 +684,34 @@ var TemplateModalCtrl = function($scope, $upload, $modalInstance, $modal) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
$scope.cancel = function() {
|
$scope.cancel = function() {
|
||||||
$modalInstance.dismiss('cancel');
|
$modalInstance.dismiss();
|
||||||
};
|
};
|
||||||
$scope.ok = function(template) {
|
$scope.ok = function(template) {
|
||||||
$modalInstance.dismiss('')
|
var newTemplate = new TemplateService(template);
|
||||||
$scope.saveTemplate(template)
|
// If it's a new template
|
||||||
|
if ($scope.newTemplate) {
|
||||||
|
// POST the template to /api/templates
|
||||||
|
newTemplate.$save({}, function() {
|
||||||
|
// If successful, push the template into the list
|
||||||
|
$scope.templates.push(newTemplate);
|
||||||
|
$scope.mainTableParams.reload()
|
||||||
|
// Close the dialog, returning the template
|
||||||
|
$modalInstance.close("Template created successfully!")
|
||||||
|
}, function(error){
|
||||||
|
// Otherwise, leave the dialog open, showing the error
|
||||||
|
console.log(error.data)
|
||||||
|
$scope.errorFlash(error.data.message)
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
newTemplate.$update({
|
||||||
|
id: newTemplate.id
|
||||||
|
}, function(){
|
||||||
|
$modalInstance.close("Template updated successfully!")
|
||||||
|
}, function(error){
|
||||||
|
console.log(error.data)
|
||||||
|
$scope.errorFlash(error.data.message)
|
||||||
|
})
|
||||||
|
}
|
||||||
};
|
};
|
||||||
$scope.removeFile = function(file) {
|
$scope.removeFile = function(file) {
|
||||||
$scope.template.attachments.splice($scope.template.attachments.indexOf(file), 1);
|
$scope.template.attachments.splice($scope.template.attachments.indexOf(file), 1);
|
||||||
|
|
|
@ -5,6 +5,11 @@
|
||||||
<h4 class="modal-title" ng-show="newTemplate" id="groupModalLabel">New Template</h4>
|
<h4 class="modal-title" ng-show="newTemplate" id="groupModalLabel">New Template</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
|
<div class="row">
|
||||||
|
<div ng-repeat="flash in flashes.modal" style="text-align:center" class="alert alert-{{flash.type}}">
|
||||||
|
<i class="fa {{flash.icon}}"></i> {{flash.message}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<label class="control-label" for="name">Name:</label>
|
<label class="control-label" for="name">Name:</label>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="text" class="form-control" ng-model="template.name" placeholder="Template name" id="name" autofocus/>
|
<input type="text" class="form-control" ng-model="template.name" placeholder="Template name" id="name" autofocus/>
|
||||||
|
|
|
@ -24,6 +24,11 @@
|
||||||
<h1 class="page-header">
|
<h1 class="page-header">
|
||||||
Email Templates
|
Email Templates
|
||||||
</h1>
|
</h1>
|
||||||
|
<div class="row">
|
||||||
|
<div ng-repeat="flash in flashes.main" style="text-align:center" class="alert alert-{{flash.type}}">
|
||||||
|
<i class="fa {{flash.icon}}"></i> {{flash.message}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<button type="button" class="btn btn-primary" ng-click="editTemplate('new')"><i class="fa fa-plus"></i> New Template</button>
|
<button type="button" class="btn btn-primary" ng-click="editTemplate('new')"><i class="fa fa-plus"></i> New Template</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -49,7 +54,7 @@
|
||||||
<li><a ng-click="editTemplate(template)">Edit</a>
|
<li><a ng-click="editTemplate(template)">Edit</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="divider"></li>
|
<li class="divider"></li>
|
||||||
<li><a ng-click="deleteTemplate(template)" ng-href="#">Delete</a>
|
<li><a ng-click="deleteTemplate(template)">Delete</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -34,5 +34,5 @@ CKEDITOR.editorConfig = function( config ) {
|
||||||
config.format_tags = 'p;h1;h2;h3;pre';
|
config.format_tags = 'p;h1;h2;h3;pre';
|
||||||
|
|
||||||
// Simplify the dialog windows.
|
// Simplify the dialog windows.
|
||||||
config.removeDialogTabs = 'image:advanced;link:advanced';
|
config.extraPlugins = 'dialogadvtab';
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,196 @@
|
||||||
|
/**
|
||||||
|
* @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
( function() {
|
||||||
|
|
||||||
|
function setupAdvParams( element ) {
|
||||||
|
var attrName = this.att;
|
||||||
|
|
||||||
|
var value = element && element.hasAttribute( attrName ) && element.getAttribute( attrName ) || '';
|
||||||
|
|
||||||
|
if ( value !== undefined )
|
||||||
|
this.setValue( value );
|
||||||
|
}
|
||||||
|
|
||||||
|
function commitAdvParams() {
|
||||||
|
// Dialogs may use different parameters in the commit list, so, by
|
||||||
|
// definition, we take the first CKEDITOR.dom.element available.
|
||||||
|
var element;
|
||||||
|
|
||||||
|
for ( var i = 0; i < arguments.length; i++ ) {
|
||||||
|
if ( arguments[ i ] instanceof CKEDITOR.dom.element ) {
|
||||||
|
element = arguments[ i ];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( element ) {
|
||||||
|
var attrName = this.att,
|
||||||
|
value = this.getValue();
|
||||||
|
|
||||||
|
if ( value )
|
||||||
|
element.setAttribute( attrName, value );
|
||||||
|
else
|
||||||
|
element.removeAttribute( attrName, value );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultTabConfig = { id: 1, dir: 1, classes: 1, styles: 1 };
|
||||||
|
|
||||||
|
CKEDITOR.plugins.add( 'dialogadvtab', {
|
||||||
|
requires: 'dialog',
|
||||||
|
|
||||||
|
// Returns allowed content rule for the content created by this plugin.
|
||||||
|
allowedContent: function( tabConfig ) {
|
||||||
|
if ( !tabConfig )
|
||||||
|
tabConfig = defaultTabConfig;
|
||||||
|
|
||||||
|
var allowedAttrs = [];
|
||||||
|
if ( tabConfig.id )
|
||||||
|
allowedAttrs.push( 'id' );
|
||||||
|
if ( tabConfig.dir )
|
||||||
|
allowedAttrs.push( 'dir' );
|
||||||
|
|
||||||
|
var allowed = '';
|
||||||
|
|
||||||
|
if ( allowedAttrs.length )
|
||||||
|
allowed += '[' + allowedAttrs.join( ',' ) + ']';
|
||||||
|
|
||||||
|
if ( tabConfig.classes )
|
||||||
|
allowed += '(*)';
|
||||||
|
if ( tabConfig.styles )
|
||||||
|
allowed += '{*}';
|
||||||
|
|
||||||
|
return allowed;
|
||||||
|
},
|
||||||
|
|
||||||
|
// @param tabConfig
|
||||||
|
// id, dir, classes, styles
|
||||||
|
createAdvancedTab: function( editor, tabConfig, element ) {
|
||||||
|
if ( !tabConfig )
|
||||||
|
tabConfig = defaultTabConfig;
|
||||||
|
|
||||||
|
var lang = editor.lang.common;
|
||||||
|
|
||||||
|
var result = {
|
||||||
|
id: 'advanced',
|
||||||
|
label: lang.advancedTab,
|
||||||
|
title: lang.advancedTab,
|
||||||
|
elements: [ {
|
||||||
|
type: 'vbox',
|
||||||
|
padding: 1,
|
||||||
|
children: []
|
||||||
|
} ]
|
||||||
|
};
|
||||||
|
|
||||||
|
var contents = [];
|
||||||
|
|
||||||
|
if ( tabConfig.id || tabConfig.dir ) {
|
||||||
|
if ( tabConfig.id ) {
|
||||||
|
contents.push( {
|
||||||
|
id: 'advId',
|
||||||
|
att: 'id',
|
||||||
|
type: 'text',
|
||||||
|
requiredContent: element ? element + '[id]' : null,
|
||||||
|
label: lang.id,
|
||||||
|
setup: setupAdvParams,
|
||||||
|
commit: commitAdvParams
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( tabConfig.dir ) {
|
||||||
|
contents.push( {
|
||||||
|
id: 'advLangDir',
|
||||||
|
att: 'dir',
|
||||||
|
type: 'select',
|
||||||
|
requiredContent: element ? element + '[dir]' : null,
|
||||||
|
label: lang.langDir,
|
||||||
|
'default': '',
|
||||||
|
style: 'width:100%',
|
||||||
|
items: [
|
||||||
|
[ lang.notSet, '' ],
|
||||||
|
[ lang.langDirLTR, 'ltr' ],
|
||||||
|
[ lang.langDirRTL, 'rtl' ]
|
||||||
|
],
|
||||||
|
setup: setupAdvParams,
|
||||||
|
commit: commitAdvParams
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
result.elements[ 0 ].children.push( {
|
||||||
|
type: 'hbox',
|
||||||
|
widths: [ '50%', '50%' ],
|
||||||
|
children: [].concat( contents )
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( tabConfig.styles || tabConfig.classes ) {
|
||||||
|
contents = [];
|
||||||
|
|
||||||
|
if ( tabConfig.styles ) {
|
||||||
|
contents.push( {
|
||||||
|
id: 'advStyles',
|
||||||
|
att: 'style',
|
||||||
|
type: 'text',
|
||||||
|
requiredContent: element ? element + '{cke-xyz}' : null,
|
||||||
|
label: lang.styles,
|
||||||
|
'default': '',
|
||||||
|
|
||||||
|
validate: CKEDITOR.dialog.validate.inlineStyle( lang.invalidInlineStyle ),
|
||||||
|
onChange: function() {},
|
||||||
|
|
||||||
|
getStyle: function( name, defaultValue ) {
|
||||||
|
var match = this.getValue().match( new RegExp( '(?:^|;)\\s*' + name + '\\s*:\\s*([^;]*)', 'i' ) );
|
||||||
|
return match ? match[ 1 ] : defaultValue;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateStyle: function( name, value ) {
|
||||||
|
var styles = this.getValue();
|
||||||
|
|
||||||
|
var tmp = editor.document.createElement( 'span' );
|
||||||
|
tmp.setAttribute( 'style', styles );
|
||||||
|
tmp.setStyle( name, value );
|
||||||
|
styles = CKEDITOR.tools.normalizeCssText( tmp.getAttribute( 'style' ) );
|
||||||
|
|
||||||
|
this.setValue( styles, 1 );
|
||||||
|
},
|
||||||
|
|
||||||
|
setup: setupAdvParams,
|
||||||
|
|
||||||
|
commit: commitAdvParams
|
||||||
|
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( tabConfig.classes ) {
|
||||||
|
contents.push( {
|
||||||
|
type: 'hbox',
|
||||||
|
widths: [ '45%', '55%' ],
|
||||||
|
children: [ {
|
||||||
|
id: 'advCSSClasses',
|
||||||
|
att: 'class',
|
||||||
|
type: 'text',
|
||||||
|
requiredContent: element ? element + '(cke-xyz)' : null,
|
||||||
|
label: lang.cssClasses,
|
||||||
|
'default': '',
|
||||||
|
setup: setupAdvParams,
|
||||||
|
commit: commitAdvParams
|
||||||
|
|
||||||
|
} ]
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
result.elements[ 0 ].children.push( {
|
||||||
|
type: 'hbox',
|
||||||
|
widths: [ '50%', '50%' ],
|
||||||
|
children: [].concat( contents )
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
} )();
|
Loading…
Reference in New Issue