Commit bfe5ab1f by taufikyu

Merge branch 'latest' of https://git.opensipkd.com/aa.gusti/opensipkd-base into latest

2 parents 15a40281 d625f5a7
Showing 43 changed files with 877 additions and 83 deletions
......@@ -86,13 +86,31 @@ titles = {}
def add_cors_headers_response_callback(event):
def cors_headers(request, response):
response.headers.update({
'Access-Control-Allow-Origin': '*',
origin = request.headers.get("Origin", None)
allowed_origin = get_params("allowed_origin", None)
if allowed_origin:
if origin not in allowed_origin.split('\n'):
origin = "null"
headers = {
'Access-Control-Allow-Methods': 'POST,GET,DELETE,PUT,OPTIONS',
'Access-Control-Allow-Headers': 'Origin, Content-Type, Accept, Authorization',
'Access-Control-Allow-Credentials': 'true',
'Access-Control-Max-Age': '1728000',
})
}
log.info(f"{origin} {request.is_xhr}")
# response.headers.update(
# {'Access-Control-Allow-Credential': 'true',
# 'Access-Control-Allow-Origin': "*"}
# )
if origin:
headers['Access-Control-Allow-Origin'] = origin
else:
headers['Access-Control-Allow-Origin'] = "*"
if 'Access-Control-Allow-Credentials' not in headers:
headers['Access-Control-Allow-Credentials'] = 'true'
log.info(f"Headers: {headers}")
response.headers.update(headers)
event.request.add_response_callback(cors_headers)
......
"""menus add valu and action
Revision ID: 671617e55c56
Revises: f95c10a7ae98
Create Date: 2022-12-27 17:59:55.766347
"""
# revision identifiers, used by Alembic.
revision = '671617e55c56'
down_revision = 'f95c10a7ae98'
branch_labels = None
depends_on = None
from alembic import op
import sqlalchemy as sa
def upgrade():
context = op.get_context()
helpers = context.opts['helpers']
if not helpers.table_has_column('menus', 'valu'):
op.add_column('menus',
sa.Column('valu', sa.String(256)))
if not helpers.table_has_column('menus', 'meth'):
op.add_column('menus',
sa.Column('meth', sa.String(256)))
if not helpers.table_has_column('menus', 'page_typ'):
op.add_column('menus',
sa.Column('page_typ', sa.String(256)))
def downgrade():
pass
No preview for this file type
kode,nama,dati2_id/res_dati2.kode
32.76.01,Pancoran Mas,ID-JB.25
32.76.02,Cimanggis,ID-JB.25
32.76.03,Sawangan,ID-JB.25
32.76.04,Limo,ID-JB.25
32.76.05,Sukmajaya,ID-JB.25
32.76.06,Beji,ID-JB.25
32.76.07,Cipayung,ID-JB.25
32.76.08,Cilodong,ID-JB.25
32.76.09,Cinere,ID-JB.25
32.76.10,Tapos,ID-JB.25
32.76.11,Bojongsari,ID-JB.25
kode,nama,kecamatan_id/res_kecamatan.kode,,,,,,,,,,,
32.76.01.01,Depok,32.76.01,,,,,,,,,,,
32.76.01.02,Depok Jaya,32.76.01,,,,,,,,,,,
32.76.01.03,Mampang,32.76.01,,,,,,,,,,,
32.76.01.04,Pancoran Mas,32.76.01,,,,,,,,,,,
32.76.01.05,Rangkapan Jaya,32.76.01,,,,,,,,,,,
32.76.01.06,Rangkapan Jaya Baru,32.76.01,,,,,,,,,,,
32.76.02.01,Cisalak Pasar,32.76.02,,,,,,,,,,,
32.76.02.02,Curug,32.76.02,,,,,,,,,,,
32.76.02.03,Harjamukti,32.76.02,,,,,,,,,,,
32.76.02.04,Mekarsari,32.76.02,,,,,,,,,,,
32.76.02.05,Pasir Gunung Selatan,32.76.02,,,,,,,,,,,
32.76.02.06,Tugu,32.76.02,,,,,,,,,,,
32.76.03.01,Bedahan,32.76.03,,,,,,,,,,,
32.76.03.02,Cinangka,32.76.03,,,,,,,,,,,
32.76.03.03,Kedaung,32.76.03,,,,,,,,,,,
32.76.03.04,Pasir Putih,32.76.03,,,,,,,,,,,
32.76.03.05,Pengasinan,32.76.03,,,,,,,,,,,
32.76.03.06,Sawangan Baru,32.76.03,,,,,,,,,,,
32.76.03.07,Sawangan Lama,32.76.03,,,,,,,,,,,
32.76.04.01,Grogol,32.76.04,,,,,,,,,,,
32.76.04.02,Krukut,32.76.04,,,,,,,,,,,
32.76.04.03,Limo,32.76.04,,,,,,,,,,,
32.76.04.04,Meruyung,32.76.04,,,,,,,,,,,
32.76.05.01,Abadijaya,32.76.05,,,,,,,,,,,
32.76.05.02,Baktijaya,32.76.05,,,,,,,,,,,
32.76.05.03,Cisalak,32.76.05,,,,,,,,,,,
32.76.05.04,Mekarjaya,32.76.05,,,,,,,,,,,
32.76.05.05,Sukmajaya,32.76.05,,,,,,,,,,,
32.76.05.06,Tirtajaya,32.76.05,,,,,,,,,,,
32.76.06.01,Beji,32.76.06,,,,,,,,,,,
32.76.06.02,Beji Timur,32.76.06,,,,,,,,,,,
32.76.06.03,Kemirimuka,32.76.06,,,,,,,,,,,
32.76.06.04,Kukusan,32.76.06,,,,,,,,,,,
32.76.06.05,Pondokcina,32.76.06,,,,,,,,,,,
32.76.06.06,Tanahbaru,32.76.06,,,,,,,,,,,
32.76.07.01,Cipayung,32.76.07,,,,,,,,,,,
32.76.07.02,Bojong Pondok Terong,32.76.07,,,,,,,,,,,
32.76.07.03,Cipayung,32.76.07,,,,,,,,,,,
32.76.07.04,Cipayung Jaya,32.76.07,,,,,,,,,,,
32.76.07.05,Pondok Jaya,32.76.07,,,,,,,,,,,
32.76.07.06,Ratujaya,32.76.07,,,,,,,,,,,
32.76.08.01,Cilodong,32.76.08,,,,,,,,,,,
32.76.08.02,Jatimulya,32.76.08,,,,,,,,,,,
32.76.08.03,Kalibaru,32.76.08,,,,,,,,,,,
32.76.08.04,Kalimulya,32.76.08,,,,,,,,,,,
32.76.08.05,Sukamaju,32.76.08,,,,,,,,,,,
32.76.09.01,Cinere,32.76.09,,,,,,,,,,,
32.76.09.02,Gandul,32.76.09,,,,,,,,,,,
32.76.09.03,Pangkalan Jati,32.76.09,,,,,,,,,,,
32.76.09.04,Pangkalan Jati Baru,32.76.09,,,,,,,,,,,
32.76.10.01,Cilangkap,32.76.10,,,,,,,,,,,
32.76.10.02,Cimpaeun,32.76.10,,,,,,,,,,,
32.76.10.03,Jatijajar,32.76.10,,,,,,,,,,,
32.76.10.04,Leuwinanggung,32.76.10,,,,,,,,,,,
32.76.10.05,Sukamaju Baru,32.76.10,,,,,,,,,,,
32.76.10.06,Sukatani,32.76.10,,,,,,,,,,,
32.76.10.07,Tapos,32.76.10,,,,,,,,,,,
32.76.11.01,Bojongsari Lama,32.76.11,,,,,,,,,,,
32.76.11.02,Bojongsaribaru,32.76.11,,,,,,,,,,,
32.76.11.03,Curug,32.76.11,,,,,,,,,,,
32.76.11.04,Durenmekar,32.76.11,,,,,,,,,,,
32.76.11.05,Durenseribu,32.76.11,,,,,,,,,,,
32.76.11.06,Pondokpetir,32.76.11,,,,,,,,,,,
32.76.11.07,Serua,32.76.11,,,,,,,,,,,
\ No newline at end of file
......@@ -160,6 +160,7 @@ dati2-view,/dati2/{id}/view,Kabupaten/Kota View,1,0
dati2-delete,/dati2/{id}/delete,Kabupaten/Kota Hapus,1,0
dati2-act,/dati2/{act}/act,Kabupaten/Kota Act,1,0
dati2-rpt,/dati2/{rpt}/rpt,Kabupaten/Kota Report,1,0
dati2-upload,/dati2/upload,Kabupaten/Kota Upload,1,0
kecamatan,/kecamatan,Kecamatan,1,0
kecamatan-add,/kecamatan/add,Kecamatan Add,1,0
kecamatan-edit,/kecamatan/{id}/edit,Kecamatan Edit,1,0
......@@ -167,6 +168,7 @@ kecamatan-view,/kecamatan/{id}/view,Kecamatan View,1,0
kecamatan-delete,/kecamatan/{id}/delete,Kecamatan Hapus,1,0
kecamatan-act,/kecamatan/{act}/act,Kecamatan Act,1,0
kecamatan-rpt,/kecamatan/{rpt}/rpt,Kecamatan Report,1,0
kecamatan-upload,/kecamatan/upload,Kecamatan Upload,1,0
desa,/desa,Desa/Kelurahan,1,0
desa-add,/desa/add,Desa/Kelurahan Add,1,0
desa-edit,/desa/{id}/edit,Desa/Kelurahan Edit,1,0
......@@ -174,6 +176,7 @@ desa-view,/desa/{id}/view,Desa/Kelurahan View,1,0
desa-delete,/desa/{id}/delete,Desa/Kelurahan Hapus,1,0
desa-act,/desa/{act}/act,Desa/Kelurahan Act,1,0
desa-rpt,/desa/{rpt}/rpt,Desa/Kelurahan Report,1,0
desa-upload,/desa/upload,Desa/Kelurahan Upload,1,0
company,/company,Pemda,1,0
company-add,/company/add,Pemda Add,1,0
company-edit,/company/{id}/edit,Pemda Edit,1,0
......
import logging
import os
import sys
import csv
......@@ -20,6 +21,7 @@ from opensipkd.models import (
from sqlalchemy.dialects import oracle
from sqlalchemy import text
log = logging.getLogger(__name__)
# , mssql
# from .tools import mkdir
......@@ -144,6 +146,7 @@ def append_csv(table, filename, keys, get_file_func=get_file,
is_first = True
fmap = dict()
for cf in reader:
log.info(cf)
if is_first:
is_first = False
for fname in cf.keys():
......
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
......@@ -5,6 +5,7 @@ from datetime import datetime
from datatables import ColumnDT
from dateutil.relativedelta import relativedelta
from opensipkd.base.views.upload import tmpstore
from opensipkd.tools.captcha import get_captcha
from pyramid.httpexceptions import HTTPFound
......@@ -14,7 +15,7 @@ from .. import DBSession, get_params
from opensipkd.tools import dmy, date_from_str, get_settings, get_ext, \
date_from_str
import colander
from deform import (widget, Form, ValidationFailure, Button, )
from deform import (widget, Form, ValidationFailure, Button, FileData, )
from email.utils import parseaddr
from opensipkd.tools.buttons import btn_save, btn_cancel, btn_close, btn_delete, \
......@@ -22,12 +23,21 @@ from opensipkd.tools.buttons import btn_save, btn_cancel, btn_close, btn_delete,
btn_pdf
from opensipkd.models import User, Menus
from ..scripts.initializedb import append_csv
from ..tools.api import auth_from_rpc
from ...detable import DeTable
log = logging.getLogger(__name__)
class UploadSchema(colander.Schema):
upload = colander.SchemaNode(
FileData(),
widget=widget.FileUploadWidget(tmpstore),
title='Unggah')
class BaseView(object):
def __init__(self, request):
self.req = request
......@@ -140,6 +150,7 @@ class BaseView(object):
});"""
self.edit_schema = ""
self.add_schema = ""
self.upload_schema = UploadSchema
self.table = ""
self.home = self.req.route_url('home')[:-1]
self.buttons = None
......@@ -147,6 +158,7 @@ class BaseView(object):
self.bindings = {}
self.autocomplete = 'on'
self.action_suffix = "/grid/act"
self.upload_keys = ["kode"]
def delete_msg(self, row):
return f'Data ID {row.id} sudah dihapus.'
......@@ -241,9 +253,62 @@ class BaseView(object):
return self.route_list("Nilai Data tidak ditemukan", "error")
form.set_appstruct(values)
table = self.get_item_table(row)
resources = form.get_widget_resources()
return dict(form=form.render(readonly=True),
table=table and table.render() or None,
scripts=self.form_scripts)
scripts=self.form_scripts,
css=resources["css"],
js=resources["js"]
)
def view_upload(self, exts=('.png', '.ico')):
bindings = self.get_bindings()
form = self.get_form(self.upload_schema, bindings=bindings)
resources = form.get_widget_resources()
if self.req.POST:
if 'save' in self.req.POST:
input_file = self.req.POST['upload'].file
filename = self.req.POST['upload'].filename.lower()
ext = get_ext(filename).lower()
if ext.lower() not in exts:
ext = ", ".join([ex for ex in exts])
self.req.session.flash(f'File harus format {ext}', 'error')
return dict(form=form.render(),
scripts=self.form_scripts, css=resources["css"],
js=resources["js"])
_here = get_params('tmp', '/tmp')
folder = os.path.join(_here, 'upload')
if not os.path.exists(folder):
os.makedirs(folder)
fullpath = os.path.join(folder, filename)
output_file = open(fullpath, 'wb')
input_file.seek(0)
while True:
data = input_file.read(2 << 16)
if not data:
break
output_file.write(data)
output_file.close()
self.save_upload(fullpath)
elif "cancel" in self.req.POST or 'batal' in self.req.POST or "close" in self.req.POST:
self.cancel_act()
else:
return self.next_add(form, resources=resources)
return self.route_list()
return dict(form=form.render(),
scripts=self.form_scripts, css=resources["css"],
js=resources["js"])
def get_file(self, filename):
return open(filename)
def save_upload(self, file_name):
return append_csv(self.table, file_name, self.upload_keys,
get_file_func=self.get_file, update_exist=True)
def before_add(self):
return {}
......@@ -502,13 +567,16 @@ username_re = re.compile('^[a-z0-9_]{6,16}$', re.IGNORECASE)
def user_name_validator(node, value):
if not username_re.match(value):
raise colander.Invalid(node,
'Value must be between 6 and 16 characters and can only contain uppercase and lowercase alphanumeric characters or an underscore')
raise colander.Invalid(
node,
'Value must be between 6 and 16 characters and can only contain ' +
'uppercase and lowercase alphanumeric characters or an underscore')
def need_captcha():
is_captcha = get_params("reg_captcha")
return is_captcha == '1' or is_captcha == "True" or is_captcha == "true" or is_captcha == True
return is_captcha == '1' or is_captcha == "True" or is_captcha == "true" \
or is_captcha == True
def need_verify():
......
......@@ -7,6 +7,7 @@ from . import widget_os
from .provinsi import provinsi_widget
from opensipkd.models import DBSession, ResDati2, kategori_dati2, ResProvinsi
from ..views import BaseView
_ = TranslationStringFactory("opensipkd")
SESS_ADD_FAILED = 'Tambah dati2 gagal'
......@@ -29,12 +30,15 @@ def dati2_widget(node, kw):
class AddSchema(colander.Schema):
provinsi_id = colander.SchemaNode(colander.String(),
widget=provinsi_widget,
validator=colander.Length(max=32), oid="kode")
validator=colander.Length(max=32),
oid="kode")
kode = colander.SchemaNode(colander.String(),
validator=colander.Length(max=32), oid="kode")
kategori = colander.SchemaNode(colander.String(),
widget=widget.SelectWidget(values=kategori_dati2),
validator=colander.Length(max=32), oid="kode")
widget=widget.SelectWidget(
values=kategori_dati2),
validator=colander.Length(max=32),
oid="kode")
nama = colander.SchemaNode(colander.String(), oid="nama")
......@@ -103,12 +107,12 @@ class ViewDati2(BaseView):
@view_config(route_name='dati2',
renderer='templates/table.pt',
permission='dati2')
permission='wilayah')
def view_list(self):
return super(ViewDati2, self).view_list()
@view_config(route_name='dati2-view',
renderer='templates/form.pt', permission='dati2')
renderer='templates/form.pt', permission='wilayah')
def view_view(self): # row = query_id(request).first()
return super(ViewDati2, self).view_view()
......@@ -129,16 +133,21 @@ class ViewDati2(BaseView):
return result
@view_config(route_name='dati2-add',
renderer='templates/form.pt', permission='dati2')
renderer='templates/form.pt', permission='wilayah')
def view_add(self):
return super(ViewDati2, self).view_add()
@view_config(route_name='dati2-edit',
renderer='templates/form.pt', permission='dati2')
renderer='templates/form.pt', permission='wilayah')
def view_edt(self):
return super(ViewDati2, self).view_edit()
@view_config(route_name='dati2-delete',
renderer='templates/form.pt', permission='dati2')
renderer='templates/form.pt', permission='wilayah')
def view_delete(self):
return super(ViewDati2, self).view_delete()
@view_config(route_name='dati2-upload',
renderer='templates/form.pt', permission='wilayah')
def view_upload(self):
return super(ViewDati2, self).view_upload(exts=(".csv",))
import colander
from deform import (widget, )
from opensipkd.tools.buttons import btn_upload, btn_close, btn_add
from pyramid.i18n import TranslationStringFactory
from pyramid.view import (view_config, )
......@@ -76,6 +77,7 @@ class ViewDesa(BaseView):
self.edit_schema = EditSchema
self.table = ResDesa
self.list_schema = ListSchema
self.list_buttons = (btn_add, btn_close, btn_upload)
def form_validator(self, form, value):
def err_kode():
......@@ -132,13 +134,13 @@ class ViewDesa(BaseView):
return d
@view_config(route_name='desa-view',
renderer='templates/form.pt', permission='desa')
renderer='templates/form.pt', permission='wilayah')
def view_view(self):
return super().view_view()
@view_config(route_name='desa',
renderer='templates/table.pt',
permission='desa')
permission='wilayah')
def view_list(self):
return super(ViewDesa, self).view_list()
......@@ -160,16 +162,21 @@ class ViewDesa(BaseView):
return result
@view_config(route_name='desa-add',
renderer='templates/form.pt', permission='desa')
renderer='templates/form.pt', permission='wilayah')
def view_add(self):
return super(ViewDesa, self).view_add()
@view_config(route_name='desa-edit',
renderer='templates/form.pt', permission='desa')
renderer='templates/form.pt', permission='wilayah')
def view_edt(self):
return super(ViewDesa, self).view_edit()
@view_config(route_name='desa-delete',
renderer='templates/form.pt', permission='desa')
renderer='templates/form.pt', permission='wilayah')
def view_delete(self):
return super(ViewDesa, self).view_delete()
@view_config(route_name='desa-upload',
renderer='templates/form.pt', permission='wilayah')
def view_upload(self):
return super(ViewDesa, self).view_upload(exts=('.csv',))
import colander
from deform import (widget, )
from opensipkd.tools.buttons import btn_upload, btn_close, btn_add
from pyramid.i18n import TranslationStringFactory
from opensipkd.base.views.provinsi import provinsi_widget
......@@ -72,6 +73,7 @@ class Views(BaseView):
self.edit_schema = EditSchema
self.table = ResKecamatan
self.list_schema = ListSchema
self.list_buttons = (btn_add, btn_close, btn_upload)
def form_validator(self, form, value):
def err_kode():
......@@ -108,13 +110,13 @@ class Views(BaseView):
err_nama()
@view_config(route_name='kecamatan-view',
renderer='templates/form.pt', permission='kecamatan')
renderer='templates/form.pt', permission='wilayah')
def view_view(self): # row = query_id(request).first()
return super().view_view()
@view_config(route_name='kecamatan',
renderer='templates/table.pt',
permission='kecamatan')
permission='wilayah')
def view_list(self):
return super(Views, self).view_list()
......@@ -136,7 +138,7 @@ class Views(BaseView):
return result
@view_config(route_name='kecamatan-add',
renderer='templates/form.pt', permission='kecamatan')
renderer='templates/form.pt', permission='wilayah')
def view_add(self):
return super(Views, self).view_add()
......@@ -158,11 +160,16 @@ class Views(BaseView):
return d
@view_config(route_name='kecamatan-edit',
renderer='templates/form.pt', permission='kecamatan')
renderer='templates/form.pt', permission='wilayah')
def view_edt(self):
return super(Views, self).view_edit()
@view_config(route_name='kecamatan-delete',
renderer='templates/form.pt', permission='kecamatan')
renderer='templates/form.pt', permission='wilayah')
def view_delete(self):
return super(Views, self).view_delete()
@view_config(route_name='kecamatan-upload',
renderer='templates/form.pt', permission='wilayah')
def view_upload(self):
return super(Views, self).view_upload(exts=(".csv",))
......@@ -46,13 +46,25 @@ class AddSchema(colander.Schema):
kode = colander.SchemaNode(colander.String(),
validator=colander.Length(max=32), oid="kode")
nama = colander.SchemaNode(colander.String(), oid="nama")
url = colander.SchemaNode(colander.String(), oid="url",
title="URL/METHOD")
valu = colander.SchemaNode(colander.String(), oid="valu",
title="Action",
missing=colander.drop)
meth = colander.SchemaNode(colander.String(), oid="meth",
title="Method",
missing=colander.drop)
page_typ = colander.SchemaNode(colander.String(), oid="page_typ",
title="Page Type")
icon = colander.SchemaNode(colander.String(),
missing=colander.drop)
class_name = colander.SchemaNode(colander.String(), oid="url",
missing=colander.drop)
need_login = colander.SchemaNode(colander.Boolean())
need_login = colander.SchemaNode(colander.Integer(),
widget=widget.RadioChoiceWidget(
values=(("-1", "Unlogged"),
("0", "All"),
("1", "Logged User"),
)))
title = colander.SchemaNode(colander.String())
status = colander.SchemaNode(colander.Boolean(), oid="status")
......@@ -88,6 +100,14 @@ class ListSchema(colander.Schema):
nama = colander.SchemaNode(colander.String(), title="Nama")
status = colander.SchemaNode(colander.Boolean(), title="Status",
width='50pt')
need_login = colander.SchemaNode(colander.Integer(), title="Login",
width='50pt')
valu = colander.SchemaNode(colander.String(), title="Value",
width='50pt')
meth = colander.SchemaNode(colander.String(), title="Method",
width='50pt')
page_typ = colander.SchemaNode(colander.String(), title="Type",
width='50pt')
level_id = colander.SchemaNode(colander.String(), title="Level",
width='50pt')
parent = colander.SchemaNode(colander.String(), title="Induk",
......@@ -121,14 +141,14 @@ class ViewMenus(BaseView):
else:
current = None
found = Menus.query_kode(value['kode'])
found = Menus.query_kode(value['kode']).first()
if current:
if found and found.id != current.id:
err_kode()
elif found:
err_kode()
found = Menus.query_nama(value['nama'])
found = Menus.query_nama(value['nama']).first()
if current:
if found and found.id != current.id:
err_nama()
......@@ -164,6 +184,7 @@ class ViewMenus(BaseView):
permission='view')
def view_act(self):
request = self.req
params = request.params
url_dict = request.matchdict
table_alias = aliased(Menus)
......@@ -173,13 +194,16 @@ class ViewMenus(BaseView):
ColumnDT(Menus.nama, mData='nama'),
ColumnDT(table_alias.nama, mData='parent'),
ColumnDT(Menus.status, mData='status'),
ColumnDT(Menus.level_id, mData='level_id'), ]
ColumnDT(Menus.need_login, mData='need_login'),
ColumnDT(Menus.level_id, mData='level_id'),
ColumnDT(Menus.meth, mData='meth'),
ColumnDT(Menus.valu, mData='valu'),
ColumnDT(Menus.page_typ, mData='page_typ'), ]
query = Menus.query_grid() \
.outerjoin(table_alias, Menus.parent_id == table_alias.id)
query = self.filter_company(query)
row_table = DataTables(request.GET, query, columns)
return row_table.output_result()
elif url_dict['act'] == 'hon':
term = 'term' in params and params['term'] or ''
q = Menus.query(). \
......
......@@ -222,10 +222,11 @@ class ViewPartner(BaseView):
err_kode()
elif found:
err_kode()
value['is_vendor'] = 'is_vendor' in value and value[
'is_vendor'] and 1 or 0
value['is_customer'] = 'is_customer' in value and value[
'is_customer'] and 1 or 0
value['is_vendor'] = 'is_vendor' in value and \
value['is_vendor'] and 1 or 0
value['is_customer'] = 'is_customer' in value and \
value['is_customer'] and 1 or 0
value["status"] = 'status' in value and value['status'] and 1 or 0
def get_bindings(self, row=None):
......
......@@ -18,7 +18,9 @@ def provinsi_widget(node, kw):
url = node and hasattr(node, 'slave_url') and node.slave_url or ""
slave = node and hasattr(node, 'slave') and node.slave or ""
values.insert(0, ("", "Pilih Propinsi..."))
readonly = kw.get("readonly", False)
return widget_os.Select2MsWidget(values=values,
readonly=readonly,
url=url,
slave=slave,
placeholder="Pilih Provinsi")
......
......@@ -103,6 +103,7 @@ class AddSchema(colander.Schema):
colander.String(),
widget=widget_os.CaptchaWidget(),
oid="captcha", title=_("Captcha"))
if request.user and request.user.id and not external_user:
# todo: external user tidak ada password
# validasi harusnya menggunakan authentikasi ke provider lagi
......
......@@ -52,10 +52,12 @@
<!-- We recommend you use "your_style.css" to override SmartAdmin
specific styles this will also ensure you retrain your customization with each SmartAdmin update. -->
<!-- OTHER CSS -->
<!-- LOOP FORM CSS INCLUDED-->
<tal:loop tal:repeat="css_resource css">
<link rel="stylesheet" href="${home}${request.static_path(css_resource)}"
type="text/css">
type="text/css" tal:condition="css_resource[:4]!='http'">
<link rel="stylesheet" href="${css_resource}"
type="text/css" tal:condition="css_resource[:4]=='http'">
</tal:loop>
<!--? <link href="${home}/static/v3/js/plugin/bootstrap-datepicker/css/bootstrap-datepicker.min.css"-->
<!--? rel="stylesheet">-->
......@@ -271,8 +273,10 @@
<!--?<script src="${home}/static/v3/js/select2.full.min.js"></script>-->
<!--?<script src="${home}/deform_static/scripts/file_upload.js"></script>-->
<script src="${home}/static/v3/js/osipkd.js"></script>
<!-- LOOP ON JS RESOURCE -->
<tal:loop tal:repeat="js_resource js">
<script src="${home}${request.static_path(js_resource)}"></script>
<script src="${home}${request.static_path(js_resource)}" tal:condition="js_resource[:4]!='http'"></script>
<script src="${js_resource}" tal:condition="js_resource[:4]=='http'"></script>
</tal:loop>
<metal:js define-slot="js_files"></metal:js>
......
<html metal:use-macro="load: ./base3.1.pt">
<div metal:fill-slot="content">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"><i class="fa fa-fw fa-plus"></i>&nbsp;${request.title}</h3>
</div>
<div class="panel-body">
<div tal:content="structure form"></div>
<div class="jarviswidget jarviswidget-color-blueLight">
<div tal:content="structure table"></div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"><i class="fa fa-fw fa-plus"></i>&nbsp;${request.title}</h3>
</div>
<div class="panel-body">
<div tal:content="structure form"></div>
<div class="container">
<div tal:content="structure table"></div>
</div>
</div>
</div>
</div>
<div metal:fill-slot="scripts">
<script>
</script>
<div metal:define-slot="scripts"></div>
<script>
</script>
<div metal:define-slot="scripts"></div>
</div>
</html>
......
<script src="${home}/static/v3/js/plugin/datatables/jquery.dataTables.min.js"></script>
<script src="${home}/static/v3/js/plugin/datatables/dataTables.colVis.min.js"></script>
<script src="${home}/static/v3/js/plugin/datatables/dataTables.tableTools.min.js"></script>
<script src="${home}/static/v3/js/plugin/datatables/dataTables.bootstrap.min.js"></script>
<script src="${home}/static/v3/js/plugin/datatable-responsive/datatables.responsive.min.js"></script>
......@@ -69,7 +69,6 @@ def view_file(request):
input_file = request.POST['upload'].file
filename = request.POST['upload'].filename.lower()
ext = get_ext(filename).lower()
if ext.lower() not in ['.png', '.ico']:
request.session.flash('File harus format png', 'error')
return dict(form=form.render())
......
......@@ -4,7 +4,8 @@ import re
import colander
from deform import (widget, )
from opensipkd.tools import create_now, SaveFile
from opensipkd.tools.report import open_rml_row, csv_response, open_rml_pdf, pdf_response, file_response
from opensipkd.tools.report import open_rml_row, csv_response, open_rml_pdf, \
pdf_response, file_response
from pyramid.i18n import TranslationStringFactory
from pyramid.view import view_config
from sqlalchemy import (func, )
......@@ -95,7 +96,8 @@ class Views(BaseView):
path = os.path.join(base_path, 'reports')
rml_row = open_rml_row(path + '/user.row.rml')
rows = [rml_row.format(user_name=r.user_name, email=r.email,
registered_date=r.registered_date) for r in query.all()]
registered_date=r.registered_date) for r in
query.all()]
pdf, filename = open_rml_pdf(path + '/user.rml', rows=rows,
company=self.req.company,
departement=self.req.departement,
......@@ -118,7 +120,8 @@ class Views(BaseView):
values["email"] = values['email'].lower()
values["user_name"] = re.sub(' ', '', values['user_name']) # .lower()
values["security_code_date"] = create_now()
company_id = request.user and request.user.company_id or "company_id" in values and values["company_id"] or None
company_id = request.user and request.user.company_id or "company_id" in values and \
values["company_id"] or None
values["company_id"] = company_id
if 'is_api_key' in values:
values["api_key"] = generate_api_key()
......@@ -261,7 +264,7 @@ class EmailValidator(colander.Email, Validator):
email = value.lower()
q = DBSession.query(User).filter_by(email=email)
found = q.first()
if found and (not self.user or self.user.email!=found.email):
if found and (not self.user or self.user.email != found.email):
email_found()
......@@ -380,5 +383,7 @@ def user_group_set(user):
def query_register():
return DBSession.query(User.user_name, User.email,
func.to_char(User.registered_date, "DD-MM-YYYY").label("registered_date")).order_by(
func.to_char(User.registered_date,
"DD-MM-YYYY").label(
"registered_date")).order_by(
User.user_name)
......@@ -306,6 +306,7 @@ class ChangePassword(colander.Schema):
retype_password = colander.SchemaNode(
colander.String(), widget=widget.PasswordWidget())
password = colander.SchemaNode(colander.String(),
widget=widget.PasswordWidget(),
title=_("Old Password"))
......
import json
import logging
from colander import SchemaNode, null, Mapping, Invalid, text_, string_types
from deform.widget import Widget, _StrippedString, Select2Widget
_logging = logging.getLogger(__name__)
class DokumenWidget(Widget):
template = "opensipkd.base:/views/widgets/dokumen.pt"
......@@ -160,7 +165,8 @@ class BlokKavNoWidget(Widget):
result = "|".join([blok_kav_no, rt, rw])
if not blok_kav_no or not rt or not rw:
raise Invalid(field.schema, "Blok Kav No RT/RW tidak lengkap", result)
raise Invalid(field.schema, "Blok Kav No RT/RW tidak lengkap",
result)
return result
......@@ -183,6 +189,7 @@ class Select2MsWidget(Select2Widget):
template = "select2_ms.pt"
class QtyWidget(Widget):
template = "opensipkd.base:/views/widgets/qty.pt"
readonly_template = "opensipkd.base:/views/widgets/readonly/qty.pt"
......@@ -228,6 +235,7 @@ class QtyWidget(Widget):
return result
class CaptchaWidget(Widget):
"""
Renders an ``<input type="text"/>`` widget.
......@@ -275,6 +283,7 @@ class CaptchaWidget(Widget):
return null
return pstruct
class ImageWidget(Widget):
"""
Renders an ``<img src="src"/>`` widget.
......@@ -300,14 +309,177 @@ class ImageWidget(Widget):
strip = True
requirements = ()
height = "30px"
def __init__(self, **kw):
super().__init__(**kw)
def serialize(self, field, cstruct, **kw):
if cstruct in (null, None):
cstruct = ""
readonly = kw.get("readonly", self.readonly)
template = readonly and self.readonly_template or self.template
values = self.get_template_values(field, cstruct, kw)
return field.renderer(template, **values)
def deserialize(self, field, pstruct):
if pstruct is null:
return null
elif not isinstance(pstruct, string_types):
raise Invalid(field.schema, "Pstruct is not a string")
if self.strip:
pstruct = pstruct.strip()
if not pstruct:
return null
return pstruct
class MapWidget(Widget):
"""
Renders an ``<div id="map"/>`` widget.
**Attributes/Arguments**
template
The template name used to render the widget. Default:
``textinput``.
readonly_template
The template name used to render the widget in read-only mode.
Default: ``readonly/textinput``.
strip
If true, during deserialization, strip the value of leading
and trailing whitespace (default ``True``).
"""
template = "opensipkd.base:views/widgets/gmap.pt"
readonly_template = "opensipkd.base:views/widgets/readonly/gmap.pt"
map_center = [0, 0]
map_zoom = 12
gmap_key = None
gmap_control = ['Point', 'Polygon', 'LineString']
gmap_height = "400px"
gmap_width = "100%"
strip = True
html_info = {}
gmap_data_style = {
"editable": True,
"draggable": True,
"clickable": True,
"removable": True,
}
gmap_edit_url = ""
show_options = False
requirements = (('deform', None),
{
"js": "opensipkd.base:static/js/gmap.js",
"css": "deform:static/select2/select2.css",
},)
def __init__(self, **kw):
super().__init__(**kw)
_logging.info(self.gmap_data_style)
self.gmap_data_style = json.dumps(self.gmap_data_style)
def serialize(self, field, cstruct, **kw):
if cstruct in (null, None):
cstruct = ""
readonly = kw.get("readonly", self.readonly)
template = readonly and self.readonly_template or self.template
_logging.debug(self.gmap_data_style)
# if readonly:
gmap_data_style = {
"editable": not readonly,
"draggable": not readonly,
"clickable": True,
"removable": not readonly,
}
self.gmap_data_style = json.dumps(gmap_data_style)
_logging.info(self.gmap_data_style)
values = self.get_template_values(field, cstruct, kw)
return field.renderer(template, **values)
def deserialize(self, field, pstruct):
if pstruct is null:
return null
elif not isinstance(pstruct, string_types):
raise Invalid(field.schema, "Pstruct is not a string")
if self.strip:
pstruct = pstruct.strip()
if not pstruct:
return null
return pstruct
class LeafMapWidget(Widget):
"""
Renders an ``<div id="map"/>`` widget.
**Attributes/Arguments**
template
The template name used to render the widget. Default:
``textinput``.
readonly_template
The template name used to render the widget in read-only mode.
Default: ``readonly/textinput``.
strip
If true, during deserialization, strip the value of leading
and trailing whitespace (default ``True``).
"""
template = "opensipkd.base:views/widgets/leafmap.pt"
readonly_template = "opensipkd.base:views/widgets/readonly/leafmap.pt"
map_center = [0, 0]
map_zoom = 12
# gmap_control = ['Point', 'Polygon', 'LineString']
map_height = "400px"
map_width = "100%"
strip = True
html_info = {}
# gmap_data_style = {
# "editable": True,
# "draggable": True,
# "clickable": True,
# "removable": True,
# }
# gmap_edit_url = ""
show_options = True
requirements = (
('deform', None),
{
"js": ["opensipkd.base:static/v3/map/leaflet/leaflet.js",
"https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/0.4.2/leaflet.draw.js"],
"css": ["opensipkd.base:static/v3/map/leaflet/leaflet.css",
"https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/0.4.2/leaflet.draw.css"],
})
def __init__(self, **kw):
super().__init__(**kw)
# _logging.info(self.gmap_data_style)
# self.gmap_data_style = json.dumps(self.gmap_data_style)
def serialize(self, field, cstruct, **kw):
if cstruct in (null, None):
cstruct = ""
readonly = kw.get("readonly", self.readonly)
template = readonly and self.readonly_template or self.template
# _logging.debug(self.gmap_data_style)
# if readonly:
gmap_data_style = {
"editable": not readonly,
"draggable": not readonly,
"clickable": True,
"removable": not readonly,
}
# self.gmap_data_style = json.dumps(gmap_data_style)
# _logging.info(self.gmap_data_style)
values = self.get_template_values(field, cstruct, kw)
return field.renderer(template, **values)
......
<span tal:define="name name|field.name;
css_class css_class|field.widget.css_class;
oid oid|field.oid;
style style|field.widget.style;
map_zoom map_zoom|field.widget.map_zoom;
map_center map_center|field.widget.map_center;
gmap_key gmap_key|field.widget.gmap_key;
gmap_height gmap_height|field.widget.gmap_height;
gmap_width gmap_width|field.widget.gmap_width;
gmap_control gmap_control|field.widget.gmap_control;
html_info html_info|field.widget.html_info;
gmap_data_style gmap_data_style|field.widget.gmap_data_style;
gmap_edit_url gmap_edit_url|field.widget.gmap_edit_url;
show_options show_options|field.widget.show_options;
"
tal:omit-tag="">
<textarea id="${oid}" name="${name}" readonly
tal:attributes="class string: form-control ${css_class or ''};
style style;
attributes|field.widget.attributes|{};">${cstruct}</textarea>
<div class="row" tal:condition="show_options">
<div class="input-group">
<label class="control-label">Fill Color</label>
<input type="color" id="fillColor">
<label class="control-label">Opacity</label>
<select id="fillOpacity">
<option value="0.0">0.0</option>
<option value="0.1">0.1</option>
<option value="0.2">0.2</option>
<option value="0.3">0.3</option>
<option value="0.4">0.4</option>
<option value="0.5">0.5</option>
<option value="0.6">0.6</option>
<option value="0.7">0.7</option>
<option value="0.8">0.8</option>
<option value="0.9">0.9</option>
<option value="1.0">1.0</option>
</select>
<label class="control-label">Stroke Color</label>
<input type="color" id="strokeColor">
<label class="control-label">Opacity</label>
<select id="strokeOpacity">
<option value="0.0">0.0</option>
<option value="0.1">0.1</option>
<option value="0.2">0.2</option>
<option value="0.3">0.3</option>
<option value="0.4">0.4</option>
<option value="0.5">0.5</option>
<option value="0.6">0.6</option>
<option value="0.7">0.7</option>
<option value="0.8">0.8</option>
<option value="0.9">0.9</option>
<option value="1.0">1.0</option>
</select>
<label class="control-label">Weight</label>
<select id="strokeWeight">
<option value="1.0">1.0</option>
<option value="2.0">2.0</option>
<option value="3.0">3.0</option>
<option value="4.0">4.0</option>
<option value="5.0">5.0</option>
<option value="6.0">6.0</option>
<option value="7.0">7.0</option>
<option value="8.0">8.0</option>
<option value="9.0">9.0</option>
</select>
</div>
</div>
<div id="gmap" class="gmap"></div>
<style>
.gmap {
height: ${gmap_height};
width: ${gmap_width};
}
</style>
<script type="text/javascript">
let map_zoom = ${map_zoom};
let map_center = ${map_center};
let geom = document.getElementById('${oid}');
let contents = ${html_info};
let edit_url = "${gmap_edit_url}";
let gmapDataStyle = ${structure: gmap_data_style};
let gmapControls = ${gmap_control};
//styleControls = document.getElementById("${oid}-options");
//styleControls.value = JSON.stringify(featureStyleOptions);
</script>
<script async defer
src="https://maps.googleapis.com/maps/api/js?v=3&key=${gmap_key}&libraries=drawing&callback=initMap">
</script>
</span>
<span tal:define="name name|field.name;
css_class css_class|field.widget.css_class;
oid oid|field.oid;
style style|field.widget.style;
map_zoom map_zoom|field.widget.map_zoom;
map_center map_center|field.widget.map_center;
map_width map_width|field.widget.map_width;
map_height map_height|field.widget.map_height;
show_options show_options|field.widget.show_options;
"
tal:omit-tag="">
<!--? gmap_key gmap_key|field.widget.gmap_key;-->
<!--? gmap_control gmap_control|field.widget.gmap_control;-->
<!--? html_info html_info|field.widget.html_info;-->
<!--? gmap_data_style gmap_data_style|field.widget.gmap_data_style;-->
<!--? gmap_edit_url gmap_edit_url|field.widget.gmap_edit_url;-->
<textarea id="${oid}" name="${name}" readonly
tal:attributes="class string: form-control ${css_class or ''};
style style;
attributes|field.widget.attributes|{};">${cstruct}</textarea>
<div class="row" tal:condition="show_options">
<div class="input-group">
<label class="control-label">Fill Color</label>
<input type="color" id="fillColor">
<label class="control-label">Opacity</label>
<select id="fillOpacity">
<option value="0.0">0.0</option>
<option value="0.1">0.1</option>
<option value="0.2">0.2</option>
<option value="0.3">0.3</option>
<option value="0.4">0.4</option>
<option value="0.5">0.5</option>
<option value="0.6">0.6</option>
<option value="0.7">0.7</option>
<option value="0.8">0.8</option>
<option value="0.9">0.9</option>
<option value="1.0">1.0</option>
</select>
<label class="control-label">Stroke Color</label>
<input type="color" id="strokeColor">
<label class="control-label">Opacity</label>
<select id="strokeOpacity">
<option value="0.0">0.0</option>
<option value="0.1">0.1</option>
<option value="0.2">0.2</option>
<option value="0.3">0.3</option>
<option value="0.4">0.4</option>
<option value="0.5">0.5</option>
<option value="0.6">0.6</option>
<option value="0.7">0.7</option>
<option value="0.8">0.8</option>
<option value="0.9">0.9</option>
<option value="1.0">1.0</option>
</select>
<label class="control-label">Weight</label>
<select id="strokeWeight">
<option value="1.0">1.0</option>
<option value="2.0">2.0</option>
<option value="3.0">3.0</option>
<option value="4.0">4.0</option>
<option value="5.0">5.0</option>
<option value="6.0">6.0</option>
<option value="7.0">7.0</option>
<option value="8.0">8.0</option>
<option value="9.0">9.0</option>
</select>
</div>
</div>
<div id="map" class="lmap"></div>
<style>
.lmap {
height: ${map_height};
width: ${map_width};
}
</style>
<script type="text/javascript">
let map_zoom = ${map_zoom};
let map_center = ${map_center};
let geom = document.getElementById('${oid}');
//let contents = {html_info};
//let edit_url = "{gmap_edit_url}";
//let gmapDataStyle = {structure: gmap_data_style};
//let gmapControls = {gmap_control};
//styleControls = document.getElementById("${oid}-options");
//styleControls.value = JSON.stringify(featureStyleOptions);
// let map = L.map('map').setView(map_center, map_zoom);
L.CursorHandler = L.Handler.extend({
addHooks: function () {
this._popup = new L.Popup();
this._map.on('mouseover', this._open, this);
this._map.on('mousemove', this._update, this);
this._map.on('mouseout', this._close, this);
},
removeHooks: function () {
this._map.off('mouseover', this._open, this);
this._map.off('mousemove', this._update, this);
this._map.off('mouseout', this._close, this);
},
_open: function (e) {
this._update(e);
this._popup.openOn(this._map);
},
_close: function () {
this._map.closePopup(this._popup);
},
_update: function (e) {
this._popup.setLatLng(e.latlng)
.setContent(e.latlng.toString());
}
});
L.Map.addInitHook('addHandler', 'cursor', L.CursorHandler);
// let map = L.map('map', {
// center: map_center, //[map_center[1], map_center[0]],
// zoom: map_zoom,
// drawControl: true,
// crs: L.CRS.EPSG3857,
// cursor: false,
// layers: [new L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
// maxZoom: 19,
// attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
// crs: L.CRS.EPSG3857,
// })]
// });
let osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
osmAttrib = '&copy; <a href="http://openstreetmap.org/copyright">OpenStreetMap</a> contributors',
osm = L.tileLayer(osmUrl, {maxZoom: 18, attribution: osmAttrib}),
map = new L.Map('map', {center: map_center, zoom: map_zoom}),
drawnItems = L.featureGroup().addTo(map);
L.control.layers(
{
'osm': osm.addTo(map),
"google": L.tileLayer('http://www.google.cn/maps/vt?lyrs=s@189&gl=cn&x={x}&y={y}&z={z}', {
attribution: 'google'
})
},
{'drawlayer': drawnItems},
{position: 'topleft', collapsed: false}).addTo(map);
map.addControl(new L.Control.Draw({
edit: {
featureGroup: drawnItems,
poly: {
allowIntersection: false
}
},
draw: {
polygon: {
allowIntersection: false,
showArea: true
}
}
}));
map.on(L.Draw.Event.CREATED, function (event) {
var layer = event.layer;
drawnItems.addLayer(layer);
});
// let drawnItems = new L.FeatureGroup();
// map.addLayer(drawnItems);
// let drawControl = new L.Control.Draw({
// edit: {
// featureGroup: drawnItems
// }
// });
// map.addControl(drawControl);
// .addTo(map);
// let jsonLayer = L.geoJSON().addTo(map);
// jsonLayer.addData(JSON.parse(geom.value));
// map.fitBounds(jsonLayer.getBounds());
// let popup = L.popup();
let jsonFeatures = JSON.parse(geom.value);
let jsonLayer = L.geoJson(jsonFeatures, {
onEachFeature: onEachFeature
});
map.fitBounds(jsonLayer.getBounds());
// Take advantage of the onEachFeature callback to initialize drawnItems
function onEachFeature(feature, layer) {
drawnItems.addLayer(layer);
}
// function onMapClick(e) {
// popup
// .setLatLng(e.latlng)
// .setContent("You clicked the map at " + e.latlng.toString())
// .openOn(map);
// }
</script>
</span>
<span tal:define="name name|field.name;
css_class css_class|field.widget.css_class;
oid oid|field.oid;
style style|field.widget.style;
map_zoom map_zoom|field.widget.map_zoom;
map_center map_center|field.widget.map_center;
gmap_key gmap_key|field.widget.gmap_key;
gmap_height gmap_height|field.widget.gmap_height;
gmap_width gmap_width|field.widget.gmap_width;
gmap_control gmap_control|field.widget.gmap_control;
html_info html_info|field.widget.html_info;
gmap_data_style gmap_data_style|field.widget.gmap_data_style;
gmap_edit_url gmap_edit_url|field.widget.gmap_edit_url;
show_options show_options|field.widget.show_options;
"
tal:omit-tag="">
<textarea id="${oid}" name="${name}" readonly
tal:attributes="class string: form-control ${css_class or ''};
style style;
attributes|field.widget.attributes|{};">${cstruct}</textarea>
<div class="row" tal:condition="show_options">
<div id="gmap" class="gmap"></div>
<style>
.gmap {
height: ${gmap_height};
width: ${gmap_width};
}
</style>
<script type="text/javascript">
let map_zoom = ${map_zoom};
let map_center = ${map_center};
let geom = document.getElementById('${oid}');
let contents = ${html_info};
let edit_url = "${gmap_edit_url}";
let gmapDataStyle = ${structure: gmap_data_style};
let gmapControls = ${gmap_control};
//styleControls = document.getElementById("${oid}-options");
//styleControls.value = JSON.stringify(featureStyleOptions);
</script>
<script async defer
src="https://maps.googleapis.com/maps/api/js?v=3&key=${gmap_key}&libraries=drawing&callback=initMap">
</script>
</span>
......@@ -112,8 +112,14 @@ class DeTable(field.Field):
data=[],
allow_edit=True,
allow_delete=True,
allow_view=True,
**kw
):
# field.Field.__init__(self, schema, **kw)
super().__init__(schema, **kw)
self.request = kw.get("request")
self.rows = kw.get("rows")
params = params and f"?{params}" or ""
btn_close_js = "{window.location = '/'; return false;}"
btn_add_js = "{window.location = o%sUri+'/add%s';}" % (tableid, params)
......@@ -128,9 +134,10 @@ class DeTable(field.Field):
btn_csv_js = "{window.location = o%sUri+'/csv/act%s';}" % (
tableid, params)
btn_pdf_js = "{window.open(o%sUri+'/pdf/act%s');}" % (tableid, params)
btn_upload_js = "{window.location = o%sUri+'/upload%s';}" % (
tableid, params)
action_suffix = f"{action_suffix}{params}"
field.Field.__init__(self, schema, **kw)
self.request = kw.get("request")
_buttons = []
for button in buttons:
if isinstance(button, compat.string_types):
......@@ -150,12 +157,13 @@ class DeTable(field.Field):
""")
_scripts.append(f'$("#{tableid + button.name}").click(function ()' +
eval('btn_' + button.name + '_js') + ');')
self.buttons = "','".join(_buttons).replace('\n', "").replace(';',
';\n')
self.buttons = "','".join(_buttons).replace('\n', ""). \
replace(';', ';\n')
self.tableid = tableid
self.scripts = ''.join(_scripts).replace(';', ";\n")
self.allow_edit = json.dumps(allow_edit)
self.allow_delete = json.dumps(allow_delete)
self.allow_view = json.dumps(allow_view)
table_widget = getattr(schema, "widget", None)
if table_widget is None:
table_widget = widget.TableWidget()
......@@ -185,7 +193,7 @@ class DeTable(field.Field):
d["orderable"] = f.orderable
data.append(f"orderable: {f.orderable}")
if hasattr(f, "url"):
d["url"]=f.url
d["url"] = f.url
# request = kw.get("request")
# if request:
# d["url"] = request.static_url(f.url)
......@@ -238,7 +246,6 @@ class DeTable(field.Field):
self.sorts = sorts
self.paginates = paginates
self.filters = filters
print(self.headers)
class Button(object):
......
......@@ -18,6 +18,7 @@
data data|field.data;
allow_edit allow_edit|field.allow_edit;
allow_delete allow_delete|field.allow_delete;
allow_view allow_delete|field.allow_view;
"
tal:attributes="style style; class css_class; attributes|field.widget.attributes|{};"
i18n:domain="detable"
......@@ -80,14 +81,17 @@
let url = ${tableid}Columns[co].url;
${tableid}Columns[co].render = function (data) {
let result = "No Data"
if (data!=null) {
if (data != null) {
result = '<a href="' + url + data + '" target="_blank">Link</a>&nbsp;';
}
return result;
}
} else if (${tableid}Columns[co].data === "id" && ${tableid}Columns[co].action === true) {
${tableid}Columns[co].render = function (id) {
let result = '<a href="${url}/' + id + '/view"><i class="fas fa-eye" aria-hidden="true"></i></a>&nbsp;';
let result = ""
if (${allow_view}) {
result = '<a href="${url}/' + id + '/view"><i class="fas fa-eye" aria-hidden="true"></i></a>&nbsp;';
}
if (${allow_edit}) {
result += '<a href="${url}/' + id + '/edit"><i class="fas fa-edit" aria-hidden="true"></i></a>&nbsp;'
}
......
......@@ -44,7 +44,7 @@ class CommonModel(object):
for column in self.__table__.columns:
value = getattr(self, column.name)
if value or null:
if type(column.type)==DateTime:
if type(column.type) == DateTime and date_format:
if value:
values[column.name] = value.strftime(date_format)
else:
......
......@@ -22,7 +22,10 @@ class Menus(Base, NamaModel):
parent_id = Column(Integer, ForeignKey('public.menus.id'))
level_id = Column(SmallInteger)
order_id = Column(SmallInteger)
url = Column(String(256)) # value
valu = Column(String(256)) # value/action
meth = Column(String(256)) # new method
page_typ = Column(String(256)) # PageType
url = Column(String(256))
icon = Column(String(256))
css_class = Column(String(256))
need_login = Column(SmallInteger, server_default="1")
......@@ -71,4 +74,3 @@ class Menus(Base, NamaModel):
row = cls.query().filter(cls.kode == parent).first()
if row:
return cls.query().filter(cls.parent_id == row.id)
......@@ -8,14 +8,14 @@ Pillow>=9.1.1
lxml>=4.9.0
beautifulsoup4>=4.11.1
soupsieve>=2.3.2.post1
pip~=18.1
wheel~=0.37.0
pip~=22.3.1
wheel~=0.38.4
rsa>=4.8
pyasn1~=0.4.8
Chameleon>=3.10.1
six~=1.16.0
Mako>=1.2.0
Babel~=2.10.2
Babel~=2.11.0
Beaker~=1.11.0
Pygments>=2.12.0
MarkupSafe>=2.1.1
......@@ -27,7 +27,7 @@ peppercorn~=0.6
iso8601>=1.0.2
google~=3.0.0
cachetools>=5.2.0
certifi~=2022.5.18.1
certifi~=2022.9.24
urllib3~=1.26.6
requests>=2.28.0
google-api-python-client>=2.51.0
......@@ -38,21 +38,21 @@ venusian~=3.0.0
plaster~=1.0
hupper~=1.10.3
waitress>=2.1.2
greenlet~=1.1.1
greenlet~=2.0.1
pyparsing>=3.0.9
httplib2>=0.20.4
icecream~=2.1.1
executing~=0.8.0
executing~=1.2.0
paginate~=0.5.6
idna~=3.2
asttokens~=2.0.5
asttokens~=2.1.0
setuptools>=57.4.0
uritemplate>=4.1.1
reportlab~=3.6.1
PyJWT>=2.4.0
py~=1.11.0
attrs~=21.4.0
pytest~=7.1.1
attrs~=22.1.0
pytest~=7.2.0
pluggy~=1.0.0
iniconfig~=1.1.1
cffi>=1.15.0
......@@ -65,6 +65,9 @@ pyramid_rpc
zipp~=3.8.0
papyrus~=2.4
geojson~=2.5.0
GeoAlchemy2~=0.12.1
pybind11~=2.9.2
qrcode~=7.3.1
\ No newline at end of file
qrcode~=7.3.1
exceptiongroup~=1.0.4
Shapely~=1.8.5.post1
GeoAlchemy~=0.7.2
tandur~=0.0.1b0
\ 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!