Add record comment

This commit is contained in:
Khanh Ngo 2019-12-09 17:50:48 +07:00
parent c1fae6f3dd
commit bca3c45e37
No known key found for this signature in database
GPG Key ID: D5FAA6A16150E49E
10 changed files with 158 additions and 64 deletions

View File

@ -0,0 +1,30 @@
"""Add comment column in domain template record table
Revision ID: 856bb94b7040
Revises: 0fb6d23a4863
Create Date: 2019-12-09 17:17:46.257906
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '856bb94b7040'
down_revision = '0fb6d23a4863'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('domain_template_record',
sa.Column('comment', sa.Text(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('domain_template_record') as batch_op:
batch_op.drop_column('comment')
# ### end Alembic commands ###

View File

@ -8,6 +8,7 @@ class DomainTemplateRecord(db.Model):
type = db.Column(db.String(64))
ttl = db.Column(db.Integer)
data = db.Column(db.Text)
comment = db.Column(db.Text)
status = db.Column(db.Boolean)
template_id = db.Column(db.Integer, db.ForeignKey('domain_template.id'))
template = db.relationship('DomainTemplate', back_populates='records')
@ -21,12 +22,14 @@ class DomainTemplateRecord(db.Model):
type=None,
ttl=None,
data=None,
comment=None,
status=None):
self.id = id
self.name = name
self.type = type
self.ttl = ttl
self.data = data
self.comment = comment
self.status = status
def apply(self):

View File

@ -20,12 +20,19 @@ class Record(object):
This is not a model, it's just an object
which be assigned data from PowerDNS API
"""
def __init__(self, name=None, type=None, status=None, ttl=None, data=None):
def __init__(self,
name=None,
type=None,
status=None,
ttl=None,
data=None,
comment_data=None):
self.name = name
self.type = type
self.status = status
self.ttl = ttl
self.data = data
self.comment_data = comment_data
# PDNS configs
self.PDNS_STATS_URL = Setting().get('pdns_api_url')
self.PDNS_API_KEY = Setting().get('pdns_api_key')
@ -48,7 +55,8 @@ class Record(object):
jdata = utils.fetch_json(urljoin(
self.PDNS_STATS_URL, self.API_EXTENDED_URL +
'/servers/localhost/zones/{0}'.format(domain)),
timeout=int(Setting().get('pdns_api_timeout')),
timeout=int(
Setting().get('pdns_api_timeout')),
headers=headers)
except Exception as e:
current_app.logger.error(
@ -59,16 +67,24 @@ class Record(object):
if self.NEW_SCHEMA:
rrsets = jdata['rrsets']
for rrset in rrsets:
r_name = rrset['name'].rstrip('.')
if self.PRETTY_IPV6_PTR: # only if activated
if rrset['type'] == 'PTR': # only ptr
if 'ip6.arpa' in r_name: # only if v6-ptr
r_name = dns.reversename.to_address(
dns.name.from_text(r_name))
if rrset['records']:
r_name = rrset['name'].rstrip('.')
if self.PRETTY_IPV6_PTR: # only if activated
if rrset['type'] == 'PTR': # only ptr
if 'ip6.arpa' in r_name: # only if v6-ptr
r_name = dns.reversename.to_address(
dns.name.from_text(r_name))
rrset['name'] = r_name
rrset['content'] = rrset['records'][0]['content']
rrset['disabled'] = rrset['records'][0]['disabled']
rrset['name'] = r_name
rrset['content'] = rrset['records'][0]['content']
rrset['disabled'] = rrset['records'][0]['disabled']
# Get the record's comment. PDNS support multiple comments
# per record. However, we are only interested in the 1st
# one, for now.
rrset['comment_data'] = {"content": "", "account": ""}
if rrset['comments']:
rrset['comment_data'] = rrset['comments'][0]
return {'records': rrsets}
return jdata
@ -108,7 +124,8 @@ class Record(object):
"records": [{
"content": self.data,
"disabled": self.status,
}]
}],
"comments": [self.comment_data]
}]
}
else:
@ -126,7 +143,8 @@ class Record(object):
"name": self.name,
"ttl": self.ttl,
"type": self.type
}]
}],
"comments": [self.comment_data]
}]
}
@ -135,7 +153,8 @@ class Record(object):
self.PDNS_STATS_URL, self.API_EXTENDED_URL +
'/servers/localhost/zones/{0}'.format(domain)),
headers=headers,
timeout=int(Setting().get('pdns_api_timeout')),
timeout=int(
Setting().get('pdns_api_timeout')),
method='PATCH',
data=data)
current_app.logger.debug(jdata)
@ -208,6 +227,7 @@ class Record(object):
"disabled":
True if r['record_status'] == 'Disabled' else False,
"ttl": int(r['record_ttl']) if r['record_ttl'] else 3600,
"comment_data": r['comment_data']
}
records.append(record)
@ -256,8 +276,10 @@ class Record(object):
r['ttl'],
"records": [{
"content": r['content'],
"disabled": r['disabled'],
}]
"disabled": r['disabled']
}],
"comments":
r['comment_data']
}
else:
record = {
@ -275,7 +297,9 @@ class Record(object):
"type": r['type'],
"priority":
10, # priority field for pdns 3.4.1. https://doc.powerdns.com/md/authoritative/upgrading/
}]
}],
"comments":
r['comment_data']
}
records.append(record)
@ -320,6 +344,7 @@ class Record(object):
"content": temp_content,
"disabled": temp_disabled
})
new_record['comments'] = item['comments']
final_records.append(new_record)
else:
@ -357,14 +382,14 @@ class Record(object):
headers=headers,
method='PATCH',
data=postdata_for_delete)
jdata2 = utils.fetch_json(
urljoin(
self.PDNS_STATS_URL, self.API_EXTENDED_URL +
'/servers/localhost/zones/{0}'.format(domain)),
headers=headers,
timeout=int(Setting().get('pdns_api_timeout')),
method='PATCH',
data=postdata_for_new)
jdata2 = utils.fetch_json(urljoin(
self.PDNS_STATS_URL, self.API_EXTENDED_URL +
'/servers/localhost/zones/{0}'.format(domain)),
headers=headers,
timeout=int(
Setting().get('pdns_api_timeout')),
method='PATCH',
data=postdata_for_new)
if 'error' in jdata1.keys():
current_app.logger.error('Cannot apply record changes.')
@ -471,7 +496,8 @@ class Record(object):
self.PDNS_STATS_URL, self.API_EXTENDED_URL +
'/servers/localhost/zones/{0}'.format(domain)),
headers=headers,
timeout=int(Setting().get('pdns_api_timeout')),
timeout=int(
Setting().get('pdns_api_timeout')),
method='PATCH',
data=data)
current_app.logger.debug(jdata)
@ -587,7 +613,8 @@ class Record(object):
self.PDNS_STATS_URL, self.API_EXTENDED_URL +
'/servers/localhost/zones/{0}'.format(domain)),
headers=headers,
timeout=int(Setting().get('pdns_api_timeout')),
timeout=int(
Setting().get('pdns_api_timeout')),
method='GET')
serial = jdata['serial']

View File

@ -9,12 +9,14 @@ class RecordEntry(object):
status=None,
ttl=None,
data=None,
comment=None,
is_allowed_edit=False):
self.name = name
self.type = type
self.status = status
self.ttl = ttl
self.data = data
self.comment = comment
self._is_allowed_edit = is_allowed_edit
self._is_allowed_delete = is_allowed_edit and self.type != 'SOA'

View File

@ -851,7 +851,8 @@ def create_template_from_zone():
status=True
if subrecord['disabled'] else False,
ttl=jr['ttl'],
data=subrecord['content'])
data=subrecord['content'],
comment=jr['comment_data']['content'])
records.append(record)
else:
for jr in jrecords:
@ -863,7 +864,8 @@ def create_template_from_zone():
type=jr['type'],
status=True if jr['disabled'] else False,
ttl=jr['ttl'],
data=jr['content'])
data=jr['content'],
comment=jr['comment_data']['content'])
records.append(record)
result_records = t.replace_records(records)
@ -918,7 +920,8 @@ def edit_template(template):
type=jr.type,
status='Disabled' if jr.status else 'Active',
ttl=jr.ttl,
data=jr.data)
data=jr.data,
comment=jr.comment if jr.comment else '')
records.append(record)
return render_template('template_edit.html',
@ -948,12 +951,14 @@ def apply_records(template):
name = '@' if j['record_name'] in ['@', ''] else j['record_name']
type = j['record_type']
data = j['record_data']
comment = j['record_comment']
disabled = True if j['record_status'] == 'Disabled' else False
ttl = int(j['record_ttl']) if j['record_ttl'] else 3600
dtr = DomainTemplateRecord(name=name,
type=type,
data=data,
comment=comment,
status=disabled,
ttl=ttl)
records.append(dtr)

View File

@ -59,6 +59,7 @@ def domain(domain_name):
subrecord['disabled'] else 'Active',
ttl=jr['ttl'],
data=subrecord['content'],
comment=jr['comment_data']['content'],
is_allowed_edit=True)
records.append(record)
if not re.search('ip6\.arpa|in-addr\.arpa$', domain_name):
@ -80,6 +81,7 @@ def domain(domain_name):
status='Disabled' if jr['disabled'] else 'Active',
ttl=jr['ttl'],
data=jr['content'],
comment=jr['comment_data']['content'],
is_allowed_edit=True)
records.append(record)
if not re.search('ip6\.arpa|in-addr\.arpa$', domain_name):
@ -108,8 +110,9 @@ def add():
account_id = request.form.getlist('accountid')[0]
if ' ' in domain_name or not domain_name or not domain_type:
return render_template('errors/400.html',
msg="Please enter a valid domain name"), 400
return render_template(
'errors/400.html',
msg="Please enter a valid domain name"), 400
if domain_type == 'slave':
if request.form.getlist('domain_master_address'):
@ -140,8 +143,7 @@ def add():
history.add()
# grant user access to the domain
Domain(name=domain_name).grant_privileges(
[current_user.id])
Domain(name=domain_name).grant_privileges([current_user.id])
# apply template if needed
if domain_template != '0':
@ -157,7 +159,8 @@ def add():
'record_name': template_record.name,
'record_status': template_record.status,
'record_ttl': template_record.ttl,
'record_type': template_record.type
'record_type': template_record.type,
'comment_data': [{'content': template_record.comment, 'account': ''}]
}
record_data.append(record_row)
r = Record()
@ -341,6 +344,18 @@ def record_apply(domain_name):
'Domain name {0} does not exist'.format(domain_name)
}), 404)
# Modify the record's comment data. We append
# the "current_user" into account field as it
# a field with user-defined meaning
for sr in submitted_record:
if sr.get('record_comment'):
sr['comment_data'] = [{
'content': sr['record_comment'],
'account': current_user.username
}]
else:
sr['comment_data'] = []
r = Record()
result = r.apply(domain_name, submitted_record)
if result['status'] == 'ok':

View File

@ -75,6 +75,7 @@ function getTableData(table) {
record["record_status"] = r[2].trim();
record["record_ttl"] = r[3].trim();
record["record_data"] = r[4].trim();
record["record_comment"] = r[5].trim();
records.push(record);
});
return records
@ -95,13 +96,14 @@ function saveRow(oTable, nRow) {
oTable.cell(nRow,2).data(status);
oTable.cell(nRow,3).data(jqSelect[2].value);
oTable.cell(nRow,4).data(jqInputs[1].value);
oTable.cell(nRow,5).data(jqInputs[2].value);
var record = jqInputs[0].value;
var button_edit = "<button type=\"button\" class=\"btn btn-flat btn-warning button_edit\" id=\"" + record + "\">Edit&nbsp;<i class=\"fa fa-edit\"></i></button>"
var button_delete = "<button type=\"button\" class=\"btn btn-flat btn-danger button_delete\" id=\"" + record + "\">Delete&nbsp;<i class=\"fa fa-trash\"></i></button>"
oTable.cell(nRow,5).data(button_edit);
oTable.cell(nRow,6).data(button_delete);
oTable.cell(nRow,6).data(button_edit);
oTable.cell(nRow,7).data(button_delete);
oTable.draw();
}
@ -145,8 +147,9 @@ function editRow(oTable, nRow) {
jqTds[2].innerHTML = '<select class="form-control" id="record_status" name="record_status" value="' + aData[2] + '"><option value="false">Active</option><option value="true">Disabled</option></select>';
jqTds[3].innerHTML = '<select class="form-control" id="record_ttl" name="record_ttl" value="' + aData[3] + '">' + ttl_opts + '</select>';
jqTds[4].innerHTML = '<input type="text" style="display:table-cell; width:100% !important" id="current_edit_record_data" name="current_edit_record_data" class="form-control input-small advance-data" value="' + aData[4].replace(/\"/g,"&quot;") + '">';
jqTds[5].innerHTML = '<button type="button" class="btn btn-flat btn-primary button_save">Save</button>';
jqTds[6].innerHTML = '<button type="button" class="btn btn-flat btn-primary button_cancel">Cancel</button>';
jqTds[5].innerHTML = '<input type="text" style="display:table-cell; width:100% !important" id="record_comment" name="record_comment" class="form-control input-small advance-data" value="' + aData[5].replace(/\"/g, "&quot;") + '">';
jqTds[6].innerHTML = '<button type="button" class="btn btn-flat btn-primary button_save">Save</button>';
jqTds[7].innerHTML = '<button type="button" class="btn btn-flat btn-primary button_cancel">Cancel</button>';
// set current value of dropdown column
if (aData[2] == 'Active'){

View File

@ -5193,17 +5193,17 @@ function applyRecordChanges(data,domain){$.ajax({type:"POST",url:$SCRIPT_ROOT+'/
var modal=$("#modal_success");modal.find('.modal-body p').text("Applied changes successfully");modal.modal('show');},error:function(jqXHR,status){console.log(jqXHR);var modal=$("#modal_error");var responseJson=jQuery.parseJSON(jqXHR.responseText);modal.find('.modal-body p').text(responseJson['msg']);modal.modal('show');}});}
function getTableData(table){var records=[]
table.rows().every(function(){var r=this.data();var record={}
record["record_name"]=r[0].trim();record["record_type"]=r[1].trim();record["record_status"]=r[2].trim();record["record_ttl"]=r[3].trim();record["record_data"]=r[4].trim();records.push(record);});return records}
record["record_name"]=r[0].trim();record["record_type"]=r[1].trim();record["record_status"]=r[2].trim();record["record_ttl"]=r[3].trim();record["record_data"]=r[4].trim();record["record_comment"]=r[5].trim();records.push(record);});return records}
function saveRow(oTable,nRow){var status='Disabled';var jqInputs=$(oTable.row(nRow).node()).find("input");var jqSelect=$(oTable.row(nRow).node()).find("select");if(jqSelect[1].value=='false'){status='Active';}
oTable.cell(nRow,0).data(jqInputs[0].value);oTable.cell(nRow,1).data(jqSelect[0].value);oTable.cell(nRow,2).data(status);oTable.cell(nRow,3).data(jqSelect[2].value);oTable.cell(nRow,4).data(jqInputs[1].value);var record=jqInputs[0].value;var button_edit="<button type=\"button\" class=\"btn btn-flat btn-warning button_edit\" id=\""+record+"\">Edit&nbsp;<i class=\"fa fa-edit\"></i></button>"
oTable.cell(nRow,0).data(jqInputs[0].value);oTable.cell(nRow,1).data(jqSelect[0].value);oTable.cell(nRow,2).data(status);oTable.cell(nRow,3).data(jqSelect[2].value);oTable.cell(nRow,4).data(jqInputs[1].value);oTable.cell(nRow,5).data(jqInputs[2].value);var record=jqInputs[0].value;var button_edit="<button type=\"button\" class=\"btn btn-flat btn-warning button_edit\" id=\""+record+"\">Edit&nbsp;<i class=\"fa fa-edit\"></i></button>"
var button_delete="<button type=\"button\" class=\"btn btn-flat btn-danger button_delete\" id=\""+record+"\">Delete&nbsp;<i class=\"fa fa-trash\"></i></button>"
oTable.cell(nRow,5).data(button_edit);oTable.cell(nRow,6).data(button_delete);oTable.draw();}
oTable.cell(nRow,6).data(button_edit);oTable.cell(nRow,7).data(button_delete);oTable.draw();}
function restoreRow(oTable,nRow){var aData=oTable.row(nRow).data();oTable.row(nRow).data(aData);oTable.draw();}
function sec2str(t){var d=Math.floor(t/86400),h=Math.floor(t/3600)%24,m=Math.floor(t/60)%60,s=t%60;return(d>0?d+' days ':'')+(h>0?h+' hours ':'')+(m>0?m+' minutes ':'')+(s>0?s+' seconds':'');}
function editRow(oTable,nRow){var isDisabled='true';var aData=oTable.row(nRow).data();var jqTds=oTable.cells(nRow,'').nodes();var record_types="";var ttl_opts="";var ttl_not_found=true;for(var i=0;i<records_allow_edit.length;i++){var record_type=records_allow_edit[i];record_types+="<option value=\""+record_type+"\">"+record_type+"</option>";}
for(var i=0;i<ttl_options.length;i++){ttl_opts+="<option value=\""+ttl_options[i][0]+"\">"+ttl_options[i][1]+"</option>";if(ttl_options[i][0]==aData[3]){ttl_not_found=false;}}
if(ttl_not_found){ttl_opts+="<option value=\""+aData[3]+"\">"+sec2str(aData[3])+"</option>";}
jqTds[0].innerHTML='<input type="text" id="edit-row-focus" class="form-control input-small" value="'+aData[0]+'">';jqTds[1].innerHTML='<select class="form-control" id="record_type" name="record_type" value="'+aData[1]+'">'+record_types+'</select>';jqTds[2].innerHTML='<select class="form-control" id="record_status" name="record_status" value="'+aData[2]+'"><option value="false">Active</option><option value="true">Disabled</option></select>';jqTds[3].innerHTML='<select class="form-control" id="record_ttl" name="record_ttl" value="'+aData[3]+'">'+ttl_opts+'</select>';jqTds[4].innerHTML='<input type="text" style="display:table-cell; width:100% !important" id="current_edit_record_data" name="current_edit_record_data" class="form-control input-small advance-data" value="'+aData[4].replace(/\"/g,"&quot;")+'">';jqTds[5].innerHTML='<button type="button" class="btn btn-flat btn-primary button_save">Save</button>';jqTds[6].innerHTML='<button type="button" class="btn btn-flat btn-primary button_cancel">Cancel</button>';if(aData[2]=='Active'){isDisabled='false';}
jqTds[0].innerHTML='<input type="text" id="edit-row-focus" class="form-control input-small" value="'+aData[0]+'">';jqTds[1].innerHTML='<select class="form-control" id="record_type" name="record_type" value="'+aData[1]+'">'+record_types+'</select>';jqTds[2].innerHTML='<select class="form-control" id="record_status" name="record_status" value="'+aData[2]+'"><option value="false">Active</option><option value="true">Disabled</option></select>';jqTds[3].innerHTML='<select class="form-control" id="record_ttl" name="record_ttl" value="'+aData[3]+'">'+ttl_opts+'</select>';jqTds[4].innerHTML='<input type="text" style="display:table-cell; width:100% !important" id="current_edit_record_data" name="current_edit_record_data" class="form-control input-small advance-data" value="'+aData[4].replace(/\"/g,"&quot;")+'">';jqTds[5].innerHTML='<input type="text" style="display:table-cell; width:100% !important" id="record_comment" name="record_comment" class="form-control input-small advance-data" value="'+aData[5].replace(/\"/g,"&quot;")+'">';jqTds[6].innerHTML='<button type="button" class="btn btn-flat btn-primary button_save">Save</button>';jqTds[7].innerHTML='<button type="button" class="btn btn-flat btn-primary button_cancel">Cancel</button>';if(aData[2]=='Active'){isDisabled='false';}
SelectElement('record_type',aData[1]);SelectElement('record_status',isDisabled);SelectElement('record_ttl',aData[3]);}
function SelectElement(elementID,valueToSelect)
{var element=document.getElementById(elementID);element.value=valueToSelect;}

View File

@ -43,6 +43,7 @@
<th>Status</th>
<th>TTL</th>
<th>Data</th>
<th>Comment</th>
<th>Edit</th>
<th>Delete</th>
</tr>
@ -65,24 +66,28 @@
<td>
{{ record.data }}
</td>
<td>
{{ record.comment }}
</td>
{% if domain.type != 'Slave' %}
<td width="6%">
{% if record.is_allowed_edit() %}
<button type="button" class="btn btn-flat btn-warning button_edit">Edit&nbsp;<i class="fa fa-edit"></i></button>
{% else %}
<button type="button" class="btn btn-flat btn-warning"">&nbsp;&nbsp;<i class="fa fa-exclamation-circle"></i>&nbsp;&nbsp;</button>
{% endif %}
{% if record.is_allowed_edit() %}
<button type="button" class="btn btn-flat btn-warning button_edit">Edit&nbsp;<i class="fa fa-edit"></i></button>
{% else %}
<button type="button" class="btn btn-flat btn-warning"">&nbsp;&nbsp;<i class="fa fa-exclamation-circle"></i>&nbsp;&nbsp;</button>
{% endif %}
</td>
<td width="6%">
{% if record.is_allowed_delete() %}
<button type="button" class="btn btn-flat btn-danger button_delete">Delete&nbsp;<i class="fa fa-trash"></i></button>
{% endif %}
{% if record.is_allowed_delete() %}
<button type="button" class="btn btn-flat btn-danger button_delete">Delete&nbsp;<i class="fa fa-trash"></i></button>
{% endif %}
</td>
{% else %}
<td width="6%">
<button type="button" class="btn btn-flat btn-warning">&nbsp;&nbsp;<i class="fa fa-exclamation-circle"></i>&nbsp;&nbsp;</button>
</td>
<td width="6%">
<button type="button" class="btn btn-flat btn-warning"">&nbsp;&nbsp;<i class="fa fa-exclamation-circle"></i>&nbsp;&nbsp;</button>
<button type="button" class="btn btn-flat btn-warning">&nbsp;&nbsp;<i class="fa fa-exclamation-circle"></i>&nbsp;&nbsp;</button>
</td>
{% endif %}
</td>
@ -133,20 +138,20 @@
"columnDefs": [
{
type: 'natural',
targets: [0, 4]
targets: [0, 5]
},
{
// hidden column so that we can add new records on top
// regardless of whatever sorting is done
// regardless of whatever sorting is done. See orderFixed
visible: false,
targets: [ 7 ]
targets: [ 8 ]
},
{
className: "length-break",
targets: [ 4 ]
targets: [ 5 ]
}
],
"orderFixed": [[7, 'asc']]
"orderFixed": [[8, 'asc']]
});
// handle delete button
@ -238,7 +243,7 @@
// add new row
var default_type = records_allow_edit[0]
var nRow = jQuery('#tbl_records').dataTable().fnAddData(['', default_type, 'Active', 3600, '', '', '', '0']);
var nRow = jQuery('#tbl_records').dataTable().fnAddData(['', default_type, 'Active', 3600, '', '', '', '', '0']);
editRow($("#tbl_records").DataTable(), nRow);
document.getElementById("edit-row-focus").focus();
nEditing = nRow;

View File

@ -41,6 +41,7 @@
<th>Status</th>
<th>TTL</th>
<th>Data</th>
<th>Comment</th>
<th>Edit</th>
<th>Delete</th>
<th>ID</th>
@ -64,6 +65,9 @@
<td class="length-break">
{{ record.data }}
</td>
<td class="length-break">
{{ record.comment }}
</td>
<td width="6%">
<button type="button" class="btn btn-flat btn-warning button_edit">
Edit&nbsp;<i class="fa fa-edit"></i>
@ -119,20 +123,20 @@
"columnDefs": [
{
type: 'natural',
targets: [0, 4]
targets: [0, 5]
},
{
// hidden column so that we can add new records on top
// regardless of whatever sorting is done
// regardless of whatever sorting is done. See orderFixed
visible: false,
targets: [ 7 ]
targets: [ 8 ]
},
{
className: "length-break",
targets: [ 4 ]
targets: [ 5 ]
}
],
"orderFixed": [[7, 'asc']]
"orderFixed": [[8, 'asc']]
});
// handle delete button
@ -223,7 +227,7 @@
// add new row
var default_type = records_allow_edit[0]
var nRow = jQuery('#tbl_records').dataTable().fnAddData(['', default_type, 'Active', 3600, '', '', '', '0']);
var nRow = jQuery('#tbl_records').dataTable().fnAddData(['', default_type, 'Active', 3600, '', '', '', '', '0']);
editRow($("#tbl_records").DataTable(), nRow);
document.getElementById("edit-row-focus").focus();
nEditing = nRow;