Enhance CSRF protection and logging in various components

- Added CSRF token handling in BaseView and CSRFSchema.
- Updated routes to include CSRF flag for user actions.
- Improved logging for CSRF token and request parameters in DataTables.
- Refactored templates to support CSRF token integration.
1 parent b8c3dea9
...@@ -14,7 +14,7 @@ from pyramid.config import Configurator ...@@ -14,7 +14,7 @@ from pyramid.config import Configurator
from pyramid.events import NewRequest, BeforeRender, subscriber from pyramid.events import NewRequest, BeforeRender, subscriber
from pyramid_mailer import mailer_factory_from_settings from pyramid_mailer import mailer_factory_from_settings
from sqlalchemy import engine_from_config from sqlalchemy import engine_from_config
from pyramid.csrf import get_csrf_token
from opensipkd.tools import get_settings, DefaultTimeZone, dmy, dmyhms, get_ext from opensipkd.tools import get_settings, DefaultTimeZone, dmy, dmyhms, get_ext
from .security import MySecurityPolicy, get_user from .security import MySecurityPolicy, get_user
from .models.base import DBSession from .models.base import DBSession
...@@ -100,7 +100,7 @@ def add_cors_headers_response_callback(event): ...@@ -100,7 +100,7 @@ def add_cors_headers_response_callback(event):
# _logging.debug(f"Headers: {headers}") # _logging.debug(f"Headers: {headers}")
response.headers.update(headers) response.headers.update(headers)
_logging.warning(response.headers) _logging.debug(response.headers)
event.request.add_response_callback(cors_headers) event.request.add_response_callback(cors_headers)
...@@ -246,8 +246,11 @@ def get_config(settings): ...@@ -246,8 +246,11 @@ def get_config(settings):
config = Configurator(settings=settings, config = Configurator(settings=settings,
root_factory='opensipkd.base.models.users.RootFactory', root_factory='opensipkd.base.models.users.RootFactory',
session_factory=session_factory) session_factory=session_factory)
config.set_default_csrf_options(require_csrf=False) require_csrf = settings.get("csrf")
require_csrf = require_csrf and str(require_csrf).lower() in ['1', 'true', 'yes'] or False
config.set_default_csrf_options(require_csrf=require_csrf)
config.set_security_policy(MySecurityPolicy(settings["session.secret"])) config.set_security_policy(MySecurityPolicy(settings["session.secret"]))
config.add_subscriber(add_cors_headers_response_callback, NewRequest) config.add_subscriber(add_cors_headers_response_callback, NewRequest)
config.add_request_method(get_app_name, 'app_name', reify=True) config.add_request_method(get_app_name, 'app_name', reify=True)
config.add_request_method(get_menus, 'menus', reify=True) config.add_request_method(get_menus, 'menus', reify=True)
...@@ -275,7 +278,7 @@ def get_config(settings): ...@@ -275,7 +278,7 @@ def get_config(settings):
# reify=True) # reify=True)
# config.add_request_method(_get_ini, 'get_ini', reify=True) # config.add_request_method(_get_ini, 'get_ini', reify=True)
# config.add_request_method(get_params, 'get_params', reify=True) # config.add_request_method(get_params, 'get_params', reify=True)
# config.add_request_method(get_csrf_token, 'get_csrf_token', reify=True) config.add_request_method(get_csrf_token, 'get_csrf_token', reify=True)
# # Penambahan Module Auto Generate Menu # # Penambahan Module Auto Generate Menu
# # config.add_request_method(get_module_menus, 'get_module_menus', reify=True) # # config.add_request_method(get_module_menus, 'get_module_menus', reify=True)
...@@ -372,6 +375,7 @@ def _add_route(config, route): ...@@ -372,6 +375,7 @@ def _add_route(config, route):
def _add_view_config(config, paket, route): def _add_view_config(config, paket, route):
_logging.debug(f"Add View Config for Route: {route}")
_add_route(config, route) _add_route(config, route)
if not route.get("func_name"): if not route.get("func_name"):
func_name = "".join(route.get("kode").split('-')[-1:]) func_name = "".join(route.get("kode").split('-')[-1:])
...@@ -415,7 +419,7 @@ def _add_view_config(config, paket, route): ...@@ -415,7 +419,7 @@ def _add_view_config(config, paket, route):
if route.get("permission"): if route.get("permission"):
params["permission"] = route.get("permission") params["permission"] = route.get("permission")
if route.get("crsf"): if route.get("csrf"):
params["require_csrf"] = True params["require_csrf"] = True
if route.get("request_method"): if route.get("request_method"):
params["request_method"] = route.get("request_method") params["request_method"] = route.get("request_method")
...@@ -424,6 +428,7 @@ def _add_view_config(config, paket, route): ...@@ -424,6 +428,7 @@ def _add_view_config(config, paket, route):
params.pop("renderer", None) params.pop("renderer", None)
config.add_view(views.as_view(), **params) config.add_view(views.as_view(), **params)
else: else:
# _logging.debug(f"Add View Config : {params}")
config.add_view(views, **params) config.add_view(views, **params)
except Exception as e: except Exception as e:
...@@ -646,6 +651,7 @@ def add_global(event): ...@@ -646,6 +651,7 @@ def add_global(event):
event['has_modules'] = has_modules event['has_modules'] = has_modules
event['get_params'] = get_params_ event['get_params'] = get_params_
# event['urlencode'] = urlencode # event['urlencode'] = urlencode
# event['quote_plus'] = quote_plus # event['quote_plus'] = quote_plus
# event['quote'] = quote # event['quote'] = quote
......
...@@ -10,11 +10,11 @@ base-register,/register,base,register,,view_register,,,,Register,1,0,,0,form8.pt ...@@ -10,11 +10,11 @@ base-register,/register,base,register,,view_register,,,,Register,1,0,,0,form8.pt
base-recreate-api-key,/recreate-api-key,base,register,ViewPassword,recreate-api-key,,,,Get Api Key,1,0,,0,recreate-api-key.pt, base-recreate-api-key,/recreate-api-key,base,register,ViewPassword,recreate-api-key,,,,Get Api Key,1,0,,0,recreate-api-key.pt,
base-admin,#,base,,,,,view,,Administrator,1,0,,1,, base-admin,#,base,,,,,view,,Administrator,1,0,,1,,
base-user,/user,base,user,,view_list,,user-view,base-admin,User,1,0,,1,form.pt, base-user,/user,base,user,,view_list,,user-view,base-admin,User,1,0,,1,form.pt,
base-user-act,/user/{act}/act,base,user,,,,user-view,base-user,User Action,1,0,,,json, base-user-act,/user/{act}/act,base,user,,,,user-view,base-user,User Action,1,0,,,json,1
base-user-add,/user/add,base,user,,,,user-edit,base-user,User Add,1,0,,,form6.pt, base-user-add,/user/add,base,user,,,,user-edit,base-user,User Add,1,0,,,form6.pt,1
base-user-edit,/user/{id}/edit,base,user,,,,user-edit,base-user,User Edit,1,0,,,form6.pt, base-user-edit,/user/{id}/edit,base,user,,,,user-edit,base-user,User Edit,1,0,,,form6.pt,1
base-user-view,/user/{id}/view,base,user,,,,user-view,base-user,User View,1,0,,,form6.pt, base-user-view,/user/{id}/view,base,user,,,,user-view,base-user,User View,1,0,,,form6.pt,1
base-user-delete,/user/{id}/delete,base,user,,,,user-edit,base-user,User Delete,1,0,,,form6.pt, base-user-delete,/user/{id}/delete,base,user,,,,user-edit,base-user,User Delete,1,0,,,form6.pt,1
base-partner,/partner,base,partner,,view_list,,admin,base-admin,Partner,1,0,,1,form.pt, base-partner,/partner,base,partner,,view_list,,admin,base-admin,Partner,1,0,,1,form.pt,
base-partner-act,/partner/{act}/act,base,partner,,,,admin,base-partner,Partner Action,1,0,,,json, base-partner-act,/partner/{act}/act,base,partner,,,,admin,base-partner,Partner Action,1,0,,,json,
base-partner-add,/partner/add,base,partner,,,,admin,base-partner,Partner Add,1,0,,,form6.pt, base-partner-add,/partner/add,base,partner,,,,admin,base-partner,Partner Add,1,0,,,form6.pt,
......
...@@ -10,7 +10,7 @@ from pyramid.view import view_config ...@@ -10,7 +10,7 @@ from pyramid.view import view_config
from opensipkd.base import get_params, get_home from opensipkd.base import get_params, get_home
from pyramid.renderers import render_to_response from pyramid.renderers import render_to_response
#, get_urls #, get_urls
from .base_views import BaseView from .base_views import BaseView, CSRFSchema
#, DataTables #, DataTables
from datetime import timedelta from datetime import timedelta
from opensipkd.detable import * from opensipkd.detable import *
......
...@@ -49,7 +49,8 @@ class CSRFSchema(colander.Schema): ...@@ -49,7 +49,8 @@ class CSRFSchema(colander.Schema):
self["csrf_token"] = colander.SchemaNode( self["csrf_token"] = colander.SchemaNode(
colander.String(), widget=widget.HiddenWidget(), colander.String(), widget=widget.HiddenWidget(),
default=csrf_token default=csrf_token,
missing=colander.drop
) )
...@@ -440,6 +441,7 @@ class BaseView(object): ...@@ -440,6 +441,7 @@ class BaseView(object):
scroll_y=scroll_y, scroll_y=scroll_y,
scroll_x=scroll_x, scroll_x=scroll_x,
html_buttons=html_buttons, html_buttons=html_buttons,
csrf_token=get_csrf_token(self.req),
**kwargs **kwargs
) )
resources = table.get_widget_resources() resources = table.get_widget_resources()
...@@ -541,7 +543,10 @@ class BaseView(object): ...@@ -541,7 +543,10 @@ class BaseView(object):
# log.debug(str(columns)) # log.debug(str(columns))
# qry = query.add_columns(*[c.sqla_expr for c in columns]) # qry = query.add_columns(*[c.sqla_expr for c in columns])
# log.debug(str(qry)) # log.debug(str(qry))
row_table = DataTables(self.req.GET, query, columns) if self.req.POST:
row_table = DataTables(self.req.POST, query, columns)
else:
row_table = DataTables(self.req.GET, query, columns)
result = row_table.output_result() result = row_table.output_result()
data = result and result.get("data") or {} data = result and result.get("data") or {}
for res in data: for res in data:
......
...@@ -45,6 +45,7 @@ class DataTables(BaseDataTables): ...@@ -45,6 +45,7 @@ class DataTables(BaseDataTables):
def output_result(self): def output_result(self):
"""Output results in the format needed by DataTables.""" """Output results in the format needed by DataTables."""
output = {} output = {}
log.debug(self.params)
output['draw'] = str(int(self.params['draw'])) output['draw'] = str(int(self.params['draw']))
output['recordsTotal'] = str(self.cardinality) output['recordsTotal'] = str(self.cardinality)
output['recordsFiltered'] = str(self.cardinality_filtered) output['recordsFiltered'] = str(self.cardinality_filtered)
......
...@@ -9,7 +9,7 @@ from pyramid.view import view_config ...@@ -9,7 +9,7 @@ from pyramid.view import view_config
from sqlalchemy import (func, ) from sqlalchemy import (func, )
from ziggurat_foundations.models.services.user import UserService from ziggurat_foundations.models.services.user import UserService
from . import BaseView from . import BaseView, CSRFSchema
# from .company import company_widget # from .company import company_widget
from .user_login import ( from .user_login import (
regenerate_security_code, send_email_security_code, generate_api_key, ) regenerate_security_code, send_email_security_code, generate_api_key, )
...@@ -296,7 +296,7 @@ def save_user(values, user, row=None): ...@@ -296,7 +296,7 @@ def save_user(values, user, row=None):
return row return row
class AddSchema(colander.Schema): class AddSchema(CSRFSchema):
email = colander.SchemaNode( email = colander.SchemaNode(
colander.String(), title=_('Email'), colander.String(), title=_('Email'),
validator=email_validator) validator=email_validator)
...@@ -314,6 +314,8 @@ class AddSchema(colander.Schema): ...@@ -314,6 +314,8 @@ class AddSchema(colander.Schema):
# colander.Integer(), widget=company_widget, # colander.Integer(), widget=company_widget,
# title="Company", # title="Company",
# missing=colander.drop) # missing=colander.drop)
def after_bind(self, schema, kwargs):
super().after_bind(schema, kwargs)
class EditSchema(AddSchema): class EditSchema(AddSchema):
......
...@@ -67,12 +67,6 @@ class Login(CSRFSchema): ...@@ -67,12 +67,6 @@ class Login(CSRFSchema):
def after_bind(self, schema, kwargs): def after_bind(self, schema, kwargs):
super().after_bind(schema, kwargs) super().after_bind(schema, kwargs)
request = kwargs["request"] request = kwargs["request"]
# csrf_token = new_csrf_token(request)
# log.debug(csrf_token)
# self["csrf_token"] = colander.SchemaNode(
# colander.String(), widget=widget.HiddenWidget(),
# default=csrf_token
# )
if BASE_CLASS.login_captcha: if BASE_CLASS.login_captcha:
self["captcha"] = colander.SchemaNode( self["captcha"] = colander.SchemaNode(
colander.String(), colander.String(),
......
...@@ -138,6 +138,7 @@ class DeTable(field.Field): ...@@ -138,6 +138,7 @@ class DeTable(field.Field):
self.filter_columns = filter_columns self.filter_columns = filter_columns
self.scroll_x = json.dumps(scroll_x) self.scroll_x = json.dumps(scroll_x)
self.scroll_y = json.dumps(scroll_y) self.scroll_y = json.dumps(scroll_y)
self.csrf_token = kw.get("csrf_token", "")
# self.widget = None # self.widget = None
# Button yang dikirim sebagai tambahan # Button yang dikirim sebagai tambahan
......
...@@ -27,14 +27,15 @@ ...@@ -27,14 +27,15 @@
filter_scripts filter_scripts|field.filter_scripts; filter_scripts filter_scripts|field.filter_scripts;
filter_form filter_form|field.filter_form; filter_form filter_form|field.filter_form;
edit_buttons edit_buttons|field.edit_buttons; edit_buttons edit_buttons|field.edit_buttons;
csrf_token csrf_token|field.csrf_token;
" tal:attributes="style style; class css_class; attributes|field.widget.attributes|{};" i18n:domain="detable"> " tal:attributes="style style; class css_class; attributes|field.widget.attributes|{};" i18n:domain="detable">
<header role="heading" class="txt-color-grayDark"> <header role="heading" class="txt-color-grayDark">
<h2 tal:condition="title"><i class="fa fa-fw fa-table"></i>${title}</h2> <h2 tal:condition="title"><i class="fa fa-fw fa-table"></i>${title}</h2>
<div class="alert alert-danger " role="alert" id="emptyID" style="opacity:0.7;display: none;"> <div class="alert alert-danger " role="alert" id="emptyID" style="opacity:0.7;display: none;">
<strong>Pilih data terlebih dahulu!!</strong> <strong>Pilih data terlebih dahulu!!</strong>
<!--? <button type="button" class="close" data-dismiss="alert" aria-label="Close">--> <!--? <button type="button" class="close" data-dismiss="alert" aria-label="Close">-->
<!--? <span aria-hidden="true">&times;</span>--> <!--? <span aria-hidden="true">&times;</span>-->
<!--? </button>--> <!--? </button>-->
</div> </div>
<div role="content"> <div role="content">
<div class="widget-body"> <div class="widget-body">
...@@ -49,7 +50,7 @@ ...@@ -49,7 +50,7 @@
<!-- <div class="row"> <!-- <div class="row">
<div class="input-group col-md-2"> <div class="input-group col-md-2">
<span class="input-group-addon"> <span class="input-group-addon">
<input type="checkbox" class="${tableid}checkAll"/> <input type="checkbox" class="${tableid}checkAll" />
</span> </span>
<label for="${tableid}checkAll">Pilih Semua</label> <label for="${tableid}checkAll">Pilih Semua</label>
</div> </div>
...@@ -70,17 +71,17 @@ ...@@ -70,17 +71,17 @@
<table id="${tableid}" class="table table-bordered table-hover table-condensed dataTable no-footer"> <table id="${tableid}" class="table table-bordered table-hover table-condensed dataTable no-footer">
<thead> <thead>
<tr> <tr>
<tr> <tr>
<tal:block tal:repeat="child field"> <tal:block tal:repeat="child field">
<tal:block tal:condition="python:not hasattr(child, 'visible') or getattr(child, 'visible')==True"> <tal:block tal:condition="python:not hasattr(child, 'visible') or getattr(child, 'visible')==True">
<th <th
tal:condition="python:hasattr(child, 'action') and not getattr(child.condition)==False and False or True"> tal:condition="python:hasattr(child, 'action') and not getattr(child.condition)==False and False or True">
${child.title} </th> ${child.title} </th>
</tal:block>
</tal:block> </tal:block>
</tal:block> </tr>
</tr>
</thead> </thead>
<tbody></tbody> <tbody></tbody>
...@@ -105,7 +106,7 @@ ...@@ -105,7 +106,7 @@
var o${tableid}Url = o${tableid}Uri + "${url_suffix}"; var o${tableid}Url = o${tableid}Uri + "${url_suffix}";
var m${tableid}ID; var m${tableid}ID;
var m${tableid}CheckList = []; var m${tableid}CheckList = [];
var check_field = ${ check_field }; var check_field = ${check_field};
deform.addCallback('${tableid}', function (oid) { deform.addCallback('${tableid}', function (oid) {
function displayEmptyID() { function displayEmptyID() {
$("#emptyID").show(); $("#emptyID").show();
...@@ -118,136 +119,140 @@ ...@@ -118,136 +119,140 @@
} }
let tb_array = [ let tb_array = [
'<div class="btn-group pull-left">', '<div class="btn-group pull-left">',
'${structure:buttons}', '${structure:buttons}',
' &nbsp;', ' &nbsp;',
'</div>', '</div>',
] ]
let ${tableid}Language = { let ${tableid}Language = {
"search": "Cari: ", "search": "Cari: ",
"paginate": { "paginate": {
"first": '<span class="glyphicon glyphicon-step-backward"></span> ', "first": '<span class="glyphicon glyphicon-step-backward"></span> ',
"last": '<span class="glyphicon glyphicon glyphicon-step-forward"></span> ', "last": '<span class="glyphicon glyphicon glyphicon-step-forward"></span> ',
"previous": '<span class="glyphicon glyphicon-backward"></span> ', "previous": '<span class="glyphicon glyphicon-backward"></span> ',
"next": '<span class="glyphicon glyphicon-forward"></span> ', "next": '<span class="glyphicon glyphicon-forward"></span> ',
}, },
"infoEmpty": "Menampilkan 0 sampai 0 dari 0", "infoEmpty": "Menampilkan 0 sampai 0 dari 0",
"info": "Menampilkan _START_ sampai _END_ dari _TOTAL_", "info": "Menampilkan _START_ sampai _END_ dari _TOTAL_",
"infoFiltered": "(disaring dari _MAX_ total keseluruhan)", "infoFiltered": "(disaring dari _MAX_ total keseluruhan)",
"lengthMenu": " _MENU_ baris ", "lengthMenu": " _MENU_ baris ",
"loadingRecords": "Sedang memuat...", "loadingRecords": "Sedang memuat...",
"processing": "Sedang memproses...", "processing": "Sedang memproses...",
"emptyTable": "Tidak ada data yang tersedia pada tabel ini", "emptyTable": "Tidak ada data yang tersedia pada tabel ini",
"zeroRecords": "Tidak ditemukan data yang sesuai", "zeroRecords": "Tidak ditemukan data yang sesuai",
}; };
let ${tableid}Columns = ${ structure: columns }; let ${tableid}Columns = ${structure: columns };
function render_checkbox(value) { function render_checkbox(value) {
if (value === true) if (value === true)
return '<i class="fas fa-check-square" aria-hidden="true">'; return '<i class="fas fa-check-square" aria-hidden="true">';
if (value === false) if (value === false)
return '<i class="fas fa-minus-square" aria-hidden="true">'; return '<i class="fas fa-minus-square" aria-hidden="true">';
return value; return value;
} }
function render_checklist(value) { function render_checklist(value) {
return '<input type="checkbox" class="${tableid}_check" checked="' + { value } + '"></input>'; return '<input type="checkbox" class="${tableid}_check" checked="' + { value } + '"></input>';
} }
for (let co in ${tableid}Columns) { for (let co in ${tableid}Columns) {
if (${tableid}Columns[co].wg_checkbox === true) { if (${tableid}Columns[co].wg_checkbox === true) {
${tableid}Columns[co].render = function (value) { ${tableid}Columns[co].render = function (value) {
if (typeof value === "string" && value.length > 0) { if (typeof value === "string" && value.length > 0) {
return render_checkbox(value) return render_checkbox(value)
}
if (["", false, 0, null].indexOf(value) === -1) {
return render_checkbox(true);
}
return render_checkbox(false);
} }
if (["", false, 0, null].indexOf(value) === -1) {
return render_checkbox(true);
} }
else if (${tableid}Columns[co].wg_select === true) { return render_checkbox(false);
${tableid}Columns[co].render = function (value) { }
if (value != null) }
return ${tableid}Columns[co].wg_select_val[value]; else if (${tableid}Columns[co].wg_select === true) {
return ""; ${tableid}Columns[co].render = function (value) {
} if (value != null)
}else if (${tableid}Columns[co].hasOwnProperty("url")) { return ${tableid}Columns[co].wg_select_val[value];
return "";
}
}else if (${tableid}Columns[co].hasOwnProperty("url")) {
let url = ${tableid}Columns[co].url; let url = ${tableid}Columns[co].url;
${tableid}Columns[co].render = function (data) { ${tableid}Columns[co].render = function (data) {
let result = "No Data"; let result = "No Data";
if (data != null) { if (data != null) {
result = '<a href="' + url + data + '" target="_blank">Link</a>&nbsp;'; result = '<a href="' + url + data + '" target="_blank">Link</a>&nbsp;';
}
return result;
} }
}else if (${tableid}Columns[co].data === "id") { return result;
}
}else if (${tableid}Columns[co].data === "id") {
${tableid}Columns[co].render = function (id, typ, data, setting) { ${tableid}Columns[co].render = function (id, typ, data, setting) {
if (${tableid}Columns[co].action === false) return "" if (${tableid}Columns[co].action === false) return ""
let result = ""; let result = "";
if (${ allow_check }) { if (${allow_check}) {
var checked = ""; var checked = "";
if (check_field !== "") { if (check_field !== "") {
checked = data[check_field] !== null ? "checked" : ""; checked = data[check_field] !== null ? "checked" : "";
m${tableid}CheckList.push(id); m${tableid}CheckList.push(id);
}
var mtableId = "${tableid}";
result = '<input type="checkbox" class="' + mtableId + '_check" value="' + id + '" ' + checked + ' name="' + mtableId + '_check"/>&nbsp;';
} }
if (${ allow_view }) var mtableId = "${tableid}";
result += '<a href="${url}/' + id + '/view"><i class="fas fa-eye" aria-hidden="true" title="View"></i></a>&nbsp;'; result = '<input type="checkbox" class="' + mtableId + '_check" value="' + id + '" ' + checked + ' name="' + mtableId + '_check"/>&nbsp;';
}
if (${allow_view})
result += '<a href="${url}/' + id + '/view"><i class="fas fa-eye" aria-hidden="true" title="View"></i></a>&nbsp;';
if (${ allow_edit }) if (${allow_edit})
result += '<a href="${url}/' + id + '/edit"><i class="fas fa-edit" aria-hidden="true" title="Edit"></i></a>&nbsp;' result += '<a href="${url}/' + id + '/edit"><i class="fas fa-edit" aria-hidden="true" title="Edit"></i></a>&nbsp;'
if (${ allow_delete }) if (${allow_delete})
result += '<a href="${url}/' + id + '/delete"><i class="fas fa-trash" aria-hidden="true" title="Delete"></i></a>'; result += '<a href="${url}/' + id + '/delete"><i class="fas fa-trash" aria-hidden="true" title="Delete"></i></a>';
if (${ allow_post }) if (${allow_post})
result += '<a href="${url}/' + id + '/post"><i class="fas fa-signs-post" aria-hidden="true" title="Post"></i></a>'; result += '<a href="${url}/' + id + '/post"><i class="fas fa-signs-post" aria-hidden="true" title="Post"></i></a>';
if (${ allow_unpost }) if (${allow_unpost})
result += '<a href="${url}/' + id + '/unpost"><i class="fas fa-delete-left" aria-hidden="true" title="Unpost"></i></a>'; result += '<a href="${url}/' + id + '/unpost"><i class="fas fa-delete-left" aria-hidden="true" title="Unpost"></i></a>';
return result; return result;
}
} }
} }
}
let ${tableid}Params = {}; let ${tableid}Params = { };
var param_ajax = ""; var param_ajax = "";
var param_data = []; var param_data = [];
if (!${ server_side }) param_data = ${ data }; if (!${server_side}) param_data = ${data};
else param_ajax = o${tableid}Url; else param_ajax = {url: o${tableid}Url,
type: 'POST',
o${tableid} = $('#${tableid}').DataTable({ data: function (d) {
scrollX: ${ field.scroll_x }, d.csrf_token = '${csrf_token}';
//scrollY: ${field.scroll_y}, }};
//dom: '<"row"<"col-md-8"<"toolbar">Bl><"col-md-4"fr>>tip', console.log("DataTable Init:", param_ajax);
o${tableid} = $('#${tableid}').DataTable({
scrollX: ${field.scroll_x},
//scrollY: ${field.scroll_y},
//dom: '<"row"<"col-md-8"<"toolbar">Bl><"col-md-4"fr>>tip',
dom: '<"row"<"col-md-8"<"toolbar">>><"row"<"col-md-8"Bl><"col-md-4"fr>>t<"row dt-footer"<"col-md-8 sub-foot" i><"col-md-4"p>>', dom: '<"row"<"col-md-8"<"toolbar">>><"row"<"col-md-8"Bl><"col-md-4"fr>>t<"row dt-footer"<"col-md-8 sub-foot" i><"col-md-4"p>>',
processing: true, processing: true,
serverSide: ${ server_side }, serverSide: ${server_side},
data: param_data, data: param_data,
ajax: param_ajax, ajax: param_ajax,
stateSave: ${ state_save }, stateSave: ${state_save},
scrollCollapse: true, scrollCollapse: true,
sort: ${ sorts }, sort: ${sorts},
info: true, info: true,
filter: ${ filters }, filter: ${filters},
autoWidth: false, autoWidth: false,
paginate: ${ paginates }, paginate: ${paginates},
paginationType: "full_numbers", paginationType: "full_numbers",
order: [], order: [],
lengthMenu: [ lengthMenu: [
[10, 25, 50, 100], [10, 25, 50, 100],
[10, 25, 50, 100] [10, 25, 50, 100]
], ],
pageLength: 25, pageLength: 25,
columns: ${tableid}Columns, columns: ${tableid}Columns,
language: ${tableid}Language, language: ${tableid}Language,
...@@ -256,124 +261,124 @@ ...@@ -256,124 +261,124 @@
.bind('keyup', function (e) { .bind('keyup', function (e) {
var code = e.keyCode || e.which; var code = e.keyCode || e.which;
if (code === 13) { if (code === 13) {
o${tableid}.search(this.value).draw(); o${ tableid }.search(this.value).draw();
} }
}); });
} }
}); });
let tb = tb_array.join(' '); let tb = tb_array.join(' ');
$("div.toolbar").html(tb); $("div.toolbar").html(tb);
$("div.toolbar").attr('style', 'display:block; float: left; margin-bottom:6px; line-height:16px;'); $("div.toolbar").attr('style', 'display:block; float: left; margin-bottom:6px; line-height:16px;');
$("div.dt-footer").attr('style', 'margin-left: -7px;'); $("div.dt-footer").attr('style', 'margin-left: -7px;');
$("div.sub-foot").attr('style', 'position:unset;'); $("div.sub-foot").attr('style', 'position:unset;');
$(".dataTables_scrollBody").attr('style', 'margin-top: -10px;'); $(".dataTables_scrollBody").attr('style', 'margin-top: -10px;');
$('#${tableid} tbody').on('click', ':checkbox', function () { $('#${tableid} tbody').on('click', ':checkbox', function () {
if (this.checked) m${tableid}CheckList.push($(this).val()); if (this.checked) m${tableid}CheckList.push($(this).val());
else m${tableid}CheckList.splice(m${tableid}CheckList.indexOf($(this).val()), 1); else m${tableid}CheckList.splice(m${tableid}CheckList.indexOf($(this).val()), 1);
}); });
$('#${tableid} tbody').on('click', 'tr', function () { $('#${tableid} tbody').on('click', 'tr', function () {
if ($(this).hasClass('selected')) { if ($(this).hasClass('selected')) {
$(this).removeClass('selected'); $(this).removeClass('selected');
m${tableid}ID = null; m${tableid}ID = null;
} else { } else {
let aData = o${tableid}.row(this).data(); let aData = o${tableid}.row(this).data();
o${tableid}.$('tr.selected').removeClass('selected'); o${tableid}.$('tr.selected').removeClass('selected');
$(this).addClass('selected'); $(this).addClass('selected');
m${tableid}ID = aData.id; m${tableid}ID = aData.id;
o${tableid}.$('tr.row_selected').removeClass('row_selected'); o${tableid}.$('tr.row_selected').removeClass('row_selected');
$(this).addClass('row_selected'); $(this).addClass('row_selected');
} }
}); });
<tal:block tal:condition="filter_columns"> <tal:block tal:condition="filter_columns">
$(".${tableid}-control-filter").on('keyup', function (e) { $(".${tableid}-control-filter").on('keyup', function (e) {
var code = e.keyCode || e.which; var code = e.keyCode || e.which;
if (code === 13) filter_table(); if (code === 13) filter_table();
}); });
$("#${tableid}_length").append('<label>&emsp;${structure:edit_buttons}</label>'); $("#${tableid}_length").append('<label>&emsp;${structure:edit_buttons}</label>');
function filter_table() { function filter_table() {
$(".${tableid}-control-filter").each(function (e) { $(".${tableid}-control-filter").each(function (e) {
var col_id = $(this).attr("id"); var col_id = $(this).attr("id");
var value; var value;
if ($(this).attr("type") === 'radio') { if ($(this).attr("type") === 'radio') {
col_id = $(this).attr('id').split("-"); col_id = $(this).attr('id').split("-");
col_id.length = 2; col_id.length = 2;
col_id = col_id.join("-"); col_id = col_id.join("-");
value = this.value; value = this.value;
if (this.checked) { if (this.checked) {
//console.log(col_id, $(this).attr('id'), value, this.checked); //console.log(col_id, $(this).attr('id'), value, this.checked);
localStorage.setItem(col_id, value); localStorage.setItem(col_id, value);
o${tableid}.column($(this).data('index')).search(this.value) o${ tableid }.column($(this).data('index')).search(this.value)
} }
} }
else if ($(this).attr("type") === 'date') { else if ($(this).attr("type") === 'date') {
value = this.value; value = this.value;
localStorage.setItem(col_id, value); localStorage.setItem(col_id, value);
var splitted = col_id.split('-'); var splitted = col_id.split('-');
if (splitted[splitted.length - 1] !== "min" && splitted[splitted.length - 1] !== "max"){ if (splitted[splitted.length - 1] !== "min" && splitted[splitted.length - 1] !== "max"){
o${tableid}.column($(this).data('index')).search(value) o${tableid}.column($(this).data('index')).search(value)
} }
else{ else{
var min_val = undefined; var min_val = undefined;
var max_val = undefined; var max_val = undefined;
if (splitted[splitted.length - 1] === "min") { if (splitted[splitted.length - 1] === "min") {
min_val = this.value; min_val = this.value;
splitted.length = splitted.length - 1; splitted.length = splitted.length - 1;
col_id = splitted.join("-"); col_id = splitted.join("-");
max_val = $("#" + col_id + '-max').val() max_val = $("#" + col_id + '-max').val()
} }
else { else {
max_val = this.value; max_val = this.value;
splitted.length = splitted.length - 1; splitted.length = splitted.length - 1;
col_id = splitted.join("-"); col_id = splitted.join("-");
min_val = $("#" + col_id + '-min').val(); min_val = $("#" + col_id + '-min').val();
} }
if (min_val === undefined && max_val !== undefined) min_val = max_val; if (min_val === undefined && max_val !== undefined) min_val = max_val;
if (max_val === undefined && min_val !== undefined) max_val = min_val; if (max_val === undefined && min_val !== undefined) max_val = min_val;
if (max_val !== undefined && min_val !== undefined && min_val !== null && max_val !== null) if (max_val !== undefined && min_val !== undefined && min_val !== null && max_val !== null)
o${tableid} o${tableid}
.column($(this).data('index')) .column($(this).data('index'))
.search(min_val + '-yadcf_delim-' + max_val, true); .search(min_val + '-yadcf_delim-' + max_val, true);
//&& min_val !== "" && max_val !== "" //&& min_val !== "" && max_val !== ""
} }
} }
else { else {
col_id = this.id; col_id = this.id;
value = this.value; value = this.value;
if (value === undefined || value === null) value = ""; if (value === undefined || value === null) value = "";
console.log(col_id, $(this).attr('id'), value, $(this).data('index')); console.log(col_id, $(this).attr('id'), value, $(this).data('index'));
localStorage.setItem(col_id, value); localStorage.setItem(col_id, value);
o${tableid} o${tableid}
.column($(this).data('index')) .column($(this).data('index'))
.search(value); .search(value);
} }
}); });
o${tableid}.draw(); o${tableid}.draw();
} }
$(".${tableid}-control-filter").on('click', function (e) { $(".${tableid}-control-filter").on('click', function (e) {
//console.log("Write Data Click"); //console.log("Write Data Click");
var typ = $(this).attr("type"); var typ = $(this).attr("type");
if (typ === 'radio') { if (typ === 'radio') {
filter_table(); filter_table();
} }
}); });
$(".${tableid}-control-filter").on('change', function (e) { $(".${tableid}-control-filter").on('change', function (e) {
var typ = $(this).prop("nodeName").toLowerCase(); var typ = $(this).prop("nodeName").toLowerCase();
if (typ === "select") filter_table(); if (typ === "select") filter_table();
var typ = $(this).attr("type").toLowerCase(); var typ = $(this).attr("type").toLowerCase();
if (typ === "date") filter_table(); if (typ === "date") filter_table();
}); });
console.log("Read Data"); console.log("Read Data");
$(".${tableid}-control-filter").each(function (e) { $(".${tableid}-control-filter").each(function (e) {
var col_id = $(this).attr("id") var col_id = $(this).attr("id")
var value; var value;
if ($(this).attr("type") === 'radio') { if ($(this).attr("type") === 'radio') {
col_id = $(this).attr('id').split("-"); col_id = $(this).attr('id').split("-");
col_id.length = 2; col_id.length = 2;
col_id = col_id.join("-"); col_id = col_id.join("-");
value = localStorage.getItem(col_id); value = localStorage.getItem(col_id);
...@@ -381,17 +386,17 @@ ...@@ -381,17 +386,17 @@
if ($(this).val() === value) $(this).attr("checked", true); if ($(this).val() === value) $(this).attr("checked", true);
else $(this).attr("checked", false); else $(this).attr("checked", false);
} }
else { else {
value = localStorage.getItem(col_id); value = localStorage.getItem(col_id);
if (value !== undefined && value !== null) if (value !== undefined && value !== null)
$(this).val(value); $(this).val(value);
} }
}); });
filter_table(); filter_table();
</tal:block> </tal:block>
${structure:btnscripts} ${structure: btnscripts }
${structure:filter_scripts} ${structure: filter_scripts }
}); });
</script> </script>
</div> </div>
\ No newline at end of file \ No newline at end of file
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!