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 = {} ...@@ -86,13 +86,31 @@ titles = {}
def add_cors_headers_response_callback(event): def add_cors_headers_response_callback(event):
def cors_headers(request, response): def cors_headers(request, response):
response.headers.update({ origin = request.headers.get("Origin", None)
'Access-Control-Allow-Origin': '*', 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-Methods': 'POST,GET,DELETE,PUT,OPTIONS',
'Access-Control-Allow-Headers': 'Origin, Content-Type, Accept, Authorization', 'Access-Control-Allow-Headers': 'Origin, Content-Type, Accept, Authorization',
'Access-Control-Allow-Credentials': 'true',
'Access-Control-Max-Age': '1728000', '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) 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 \ No newline at end of file
...@@ -160,6 +160,7 @@ dati2-view,/dati2/{id}/view,Kabupaten/Kota View,1,0 ...@@ -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-delete,/dati2/{id}/delete,Kabupaten/Kota Hapus,1,0
dati2-act,/dati2/{act}/act,Kabupaten/Kota Act,1,0 dati2-act,/dati2/{act}/act,Kabupaten/Kota Act,1,0
dati2-rpt,/dati2/{rpt}/rpt,Kabupaten/Kota Report,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,/kecamatan,Kecamatan,1,0
kecamatan-add,/kecamatan/add,Kecamatan Add,1,0 kecamatan-add,/kecamatan/add,Kecamatan Add,1,0
kecamatan-edit,/kecamatan/{id}/edit,Kecamatan Edit,1,0 kecamatan-edit,/kecamatan/{id}/edit,Kecamatan Edit,1,0
...@@ -167,6 +168,7 @@ kecamatan-view,/kecamatan/{id}/view,Kecamatan View,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-delete,/kecamatan/{id}/delete,Kecamatan Hapus,1,0
kecamatan-act,/kecamatan/{act}/act,Kecamatan Act,1,0 kecamatan-act,/kecamatan/{act}/act,Kecamatan Act,1,0
kecamatan-rpt,/kecamatan/{rpt}/rpt,Kecamatan Report,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,/desa,Desa/Kelurahan,1,0
desa-add,/desa/add,Desa/Kelurahan Add,1,0 desa-add,/desa/add,Desa/Kelurahan Add,1,0
desa-edit,/desa/{id}/edit,Desa/Kelurahan Edit,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 ...@@ -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-delete,/desa/{id}/delete,Desa/Kelurahan Hapus,1,0
desa-act,/desa/{act}/act,Desa/Kelurahan Act,1,0 desa-act,/desa/{act}/act,Desa/Kelurahan Act,1,0
desa-rpt,/desa/{rpt}/rpt,Desa/Kelurahan Report,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,/company,Pemda,1,0
company-add,/company/add,Pemda Add,1,0 company-add,/company/add,Pemda Add,1,0
company-edit,/company/{id}/edit,Pemda Edit,1,0 company-edit,/company/{id}/edit,Pemda Edit,1,0
......
import logging
import os import os
import sys import sys
import csv import csv
...@@ -20,6 +21,7 @@ from opensipkd.models import ( ...@@ -20,6 +21,7 @@ from opensipkd.models import (
from sqlalchemy.dialects import oracle from sqlalchemy.dialects import oracle
from sqlalchemy import text from sqlalchemy import text
log = logging.getLogger(__name__)
# , mssql # , mssql
# from .tools import mkdir # from .tools import mkdir
...@@ -144,6 +146,7 @@ def append_csv(table, filename, keys, get_file_func=get_file, ...@@ -144,6 +146,7 @@ def append_csv(table, filename, keys, get_file_func=get_file,
is_first = True is_first = True
fmap = dict() fmap = dict()
for cf in reader: for cf in reader:
log.info(cf)
if is_first: if is_first:
is_first = False is_first = False
for fname in cf.keys(): 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 ...@@ -5,6 +5,7 @@ from datetime import datetime
from datatables import ColumnDT from datatables import ColumnDT
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from opensipkd.base.views.upload import tmpstore
from opensipkd.tools.captcha import get_captcha from opensipkd.tools.captcha import get_captcha
from pyramid.httpexceptions import HTTPFound from pyramid.httpexceptions import HTTPFound
...@@ -14,7 +15,7 @@ from .. import DBSession, get_params ...@@ -14,7 +15,7 @@ from .. import DBSession, get_params
from opensipkd.tools import dmy, date_from_str, get_settings, get_ext, \ from opensipkd.tools import dmy, date_from_str, get_settings, get_ext, \
date_from_str date_from_str
import colander import colander
from deform import (widget, Form, ValidationFailure, Button, ) from deform import (widget, Form, ValidationFailure, Button, FileData, )
from email.utils import parseaddr from email.utils import parseaddr
from opensipkd.tools.buttons import btn_save, btn_cancel, btn_close, btn_delete, \ 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, ...@@ -22,12 +23,21 @@ from opensipkd.tools.buttons import btn_save, btn_cancel, btn_close, btn_delete,
btn_pdf btn_pdf
from opensipkd.models import User, Menus from opensipkd.models import User, Menus
from ..scripts.initializedb import append_csv
from ..tools.api import auth_from_rpc from ..tools.api import auth_from_rpc
from ...detable import DeTable from ...detable import DeTable
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class UploadSchema(colander.Schema):
upload = colander.SchemaNode(
FileData(),
widget=widget.FileUploadWidget(tmpstore),
title='Unggah')
class BaseView(object): class BaseView(object):
def __init__(self, request): def __init__(self, request):
self.req = request self.req = request
...@@ -140,6 +150,7 @@ class BaseView(object): ...@@ -140,6 +150,7 @@ class BaseView(object):
});""" });"""
self.edit_schema = "" self.edit_schema = ""
self.add_schema = "" self.add_schema = ""
self.upload_schema = UploadSchema
self.table = "" self.table = ""
self.home = self.req.route_url('home')[:-1] self.home = self.req.route_url('home')[:-1]
self.buttons = None self.buttons = None
...@@ -147,6 +158,7 @@ class BaseView(object): ...@@ -147,6 +158,7 @@ class BaseView(object):
self.bindings = {} self.bindings = {}
self.autocomplete = 'on' self.autocomplete = 'on'
self.action_suffix = "/grid/act" self.action_suffix = "/grid/act"
self.upload_keys = ["kode"]
def delete_msg(self, row): def delete_msg(self, row):
return f'Data ID {row.id} sudah dihapus.' return f'Data ID {row.id} sudah dihapus.'
...@@ -241,9 +253,62 @@ class BaseView(object): ...@@ -241,9 +253,62 @@ class BaseView(object):
return self.route_list("Nilai Data tidak ditemukan", "error") return self.route_list("Nilai Data tidak ditemukan", "error")
form.set_appstruct(values) form.set_appstruct(values)
table = self.get_item_table(row) table = self.get_item_table(row)
resources = form.get_widget_resources()
return dict(form=form.render(readonly=True), return dict(form=form.render(readonly=True),
table=table and table.render() or None, 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): def before_add(self):
return {} return {}
...@@ -502,13 +567,16 @@ username_re = re.compile('^[a-z0-9_]{6,16}$', re.IGNORECASE) ...@@ -502,13 +567,16 @@ username_re = re.compile('^[a-z0-9_]{6,16}$', re.IGNORECASE)
def user_name_validator(node, value): def user_name_validator(node, value):
if not username_re.match(value): if not username_re.match(value):
raise colander.Invalid(node, raise colander.Invalid(
'Value must be between 6 and 16 characters and can only contain uppercase and lowercase alphanumeric characters or an underscore') node,
'Value must be between 6 and 16 characters and can only contain ' +
'uppercase and lowercase alphanumeric characters or an underscore')
def need_captcha(): def need_captcha():
is_captcha = get_params("reg_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(): def need_verify():
......
...@@ -7,6 +7,7 @@ from . import widget_os ...@@ -7,6 +7,7 @@ from . import widget_os
from .provinsi import provinsi_widget from .provinsi import provinsi_widget
from opensipkd.models import DBSession, ResDati2, kategori_dati2, ResProvinsi from opensipkd.models import DBSession, ResDati2, kategori_dati2, ResProvinsi
from ..views import BaseView from ..views import BaseView
_ = TranslationStringFactory("opensipkd") _ = TranslationStringFactory("opensipkd")
SESS_ADD_FAILED = 'Tambah dati2 gagal' SESS_ADD_FAILED = 'Tambah dati2 gagal'
...@@ -29,12 +30,15 @@ def dati2_widget(node, kw): ...@@ -29,12 +30,15 @@ def dati2_widget(node, kw):
class AddSchema(colander.Schema): class AddSchema(colander.Schema):
provinsi_id = colander.SchemaNode(colander.String(), provinsi_id = colander.SchemaNode(colander.String(),
widget=provinsi_widget, widget=provinsi_widget,
validator=colander.Length(max=32), oid="kode") validator=colander.Length(max=32),
oid="kode")
kode = colander.SchemaNode(colander.String(), kode = colander.SchemaNode(colander.String(),
validator=colander.Length(max=32), oid="kode") validator=colander.Length(max=32), oid="kode")
kategori = colander.SchemaNode(colander.String(), kategori = colander.SchemaNode(colander.String(),
widget=widget.SelectWidget(values=kategori_dati2), widget=widget.SelectWidget(
validator=colander.Length(max=32), oid="kode") values=kategori_dati2),
validator=colander.Length(max=32),
oid="kode")
nama = colander.SchemaNode(colander.String(), oid="nama") nama = colander.SchemaNode(colander.String(), oid="nama")
...@@ -103,12 +107,12 @@ class ViewDati2(BaseView): ...@@ -103,12 +107,12 @@ class ViewDati2(BaseView):
@view_config(route_name='dati2', @view_config(route_name='dati2',
renderer='templates/table.pt', renderer='templates/table.pt',
permission='dati2') permission='wilayah')
def view_list(self): def view_list(self):
return super(ViewDati2, self).view_list() return super(ViewDati2, self).view_list()
@view_config(route_name='dati2-view', @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() def view_view(self): # row = query_id(request).first()
return super(ViewDati2, self).view_view() return super(ViewDati2, self).view_view()
...@@ -129,16 +133,21 @@ class ViewDati2(BaseView): ...@@ -129,16 +133,21 @@ class ViewDati2(BaseView):
return result return result
@view_config(route_name='dati2-add', @view_config(route_name='dati2-add',
renderer='templates/form.pt', permission='dati2') renderer='templates/form.pt', permission='wilayah')
def view_add(self): def view_add(self):
return super(ViewDati2, self).view_add() return super(ViewDati2, self).view_add()
@view_config(route_name='dati2-edit', @view_config(route_name='dati2-edit',
renderer='templates/form.pt', permission='dati2') renderer='templates/form.pt', permission='wilayah')
def view_edt(self): def view_edt(self):
return super(ViewDati2, self).view_edit() return super(ViewDati2, self).view_edit()
@view_config(route_name='dati2-delete', @view_config(route_name='dati2-delete',
renderer='templates/form.pt', permission='dati2') renderer='templates/form.pt', permission='wilayah')
def view_delete(self): def view_delete(self):
return super(ViewDati2, self).view_delete() 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 import colander
from deform import (widget, ) from deform import (widget, )
from opensipkd.tools.buttons import btn_upload, btn_close, btn_add
from pyramid.i18n import TranslationStringFactory from pyramid.i18n import TranslationStringFactory
from pyramid.view import (view_config, ) from pyramid.view import (view_config, )
...@@ -76,6 +77,7 @@ class ViewDesa(BaseView): ...@@ -76,6 +77,7 @@ class ViewDesa(BaseView):
self.edit_schema = EditSchema self.edit_schema = EditSchema
self.table = ResDesa self.table = ResDesa
self.list_schema = ListSchema self.list_schema = ListSchema
self.list_buttons = (btn_add, btn_close, btn_upload)
def form_validator(self, form, value): def form_validator(self, form, value):
def err_kode(): def err_kode():
...@@ -132,13 +134,13 @@ class ViewDesa(BaseView): ...@@ -132,13 +134,13 @@ class ViewDesa(BaseView):
return d return d
@view_config(route_name='desa-view', @view_config(route_name='desa-view',
renderer='templates/form.pt', permission='desa') renderer='templates/form.pt', permission='wilayah')
def view_view(self): def view_view(self):
return super().view_view() return super().view_view()
@view_config(route_name='desa', @view_config(route_name='desa',
renderer='templates/table.pt', renderer='templates/table.pt',
permission='desa') permission='wilayah')
def view_list(self): def view_list(self):
return super(ViewDesa, self).view_list() return super(ViewDesa, self).view_list()
...@@ -160,16 +162,21 @@ class ViewDesa(BaseView): ...@@ -160,16 +162,21 @@ class ViewDesa(BaseView):
return result return result
@view_config(route_name='desa-add', @view_config(route_name='desa-add',
renderer='templates/form.pt', permission='desa') renderer='templates/form.pt', permission='wilayah')
def view_add(self): def view_add(self):
return super(ViewDesa, self).view_add() return super(ViewDesa, self).view_add()
@view_config(route_name='desa-edit', @view_config(route_name='desa-edit',
renderer='templates/form.pt', permission='desa') renderer='templates/form.pt', permission='wilayah')
def view_edt(self): def view_edt(self):
return super(ViewDesa, self).view_edit() return super(ViewDesa, self).view_edit()
@view_config(route_name='desa-delete', @view_config(route_name='desa-delete',
renderer='templates/form.pt', permission='desa') renderer='templates/form.pt', permission='wilayah')
def view_delete(self): def view_delete(self):
return super(ViewDesa, self).view_delete() 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 import colander
from deform import (widget, ) from deform import (widget, )
from opensipkd.tools.buttons import btn_upload, btn_close, btn_add
from pyramid.i18n import TranslationStringFactory from pyramid.i18n import TranslationStringFactory
from opensipkd.base.views.provinsi import provinsi_widget from opensipkd.base.views.provinsi import provinsi_widget
...@@ -72,6 +73,7 @@ class Views(BaseView): ...@@ -72,6 +73,7 @@ class Views(BaseView):
self.edit_schema = EditSchema self.edit_schema = EditSchema
self.table = ResKecamatan self.table = ResKecamatan
self.list_schema = ListSchema self.list_schema = ListSchema
self.list_buttons = (btn_add, btn_close, btn_upload)
def form_validator(self, form, value): def form_validator(self, form, value):
def err_kode(): def err_kode():
...@@ -108,13 +110,13 @@ class Views(BaseView): ...@@ -108,13 +110,13 @@ class Views(BaseView):
err_nama() err_nama()
@view_config(route_name='kecamatan-view', @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() def view_view(self): # row = query_id(request).first()
return super().view_view() return super().view_view()
@view_config(route_name='kecamatan', @view_config(route_name='kecamatan',
renderer='templates/table.pt', renderer='templates/table.pt',
permission='kecamatan') permission='wilayah')
def view_list(self): def view_list(self):
return super(Views, self).view_list() return super(Views, self).view_list()
...@@ -136,7 +138,7 @@ class Views(BaseView): ...@@ -136,7 +138,7 @@ class Views(BaseView):
return result return result
@view_config(route_name='kecamatan-add', @view_config(route_name='kecamatan-add',
renderer='templates/form.pt', permission='kecamatan') renderer='templates/form.pt', permission='wilayah')
def view_add(self): def view_add(self):
return super(Views, self).view_add() return super(Views, self).view_add()
...@@ -158,11 +160,16 @@ class Views(BaseView): ...@@ -158,11 +160,16 @@ class Views(BaseView):
return d return d
@view_config(route_name='kecamatan-edit', @view_config(route_name='kecamatan-edit',
renderer='templates/form.pt', permission='kecamatan') renderer='templates/form.pt', permission='wilayah')
def view_edt(self): def view_edt(self):
return super(Views, self).view_edit() return super(Views, self).view_edit()
@view_config(route_name='kecamatan-delete', @view_config(route_name='kecamatan-delete',
renderer='templates/form.pt', permission='kecamatan') renderer='templates/form.pt', permission='wilayah')
def view_delete(self): def view_delete(self):
return super(Views, self).view_delete() 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): ...@@ -46,13 +46,25 @@ class AddSchema(colander.Schema):
kode = colander.SchemaNode(colander.String(), kode = colander.SchemaNode(colander.String(),
validator=colander.Length(max=32), oid="kode") validator=colander.Length(max=32), oid="kode")
nama = colander.SchemaNode(colander.String(), oid="nama") nama = colander.SchemaNode(colander.String(), oid="nama")
url = colander.SchemaNode(colander.String(), oid="url", valu = colander.SchemaNode(colander.String(), oid="valu",
title="URL/METHOD") 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(), icon = colander.SchemaNode(colander.String(),
missing=colander.drop) missing=colander.drop)
class_name = colander.SchemaNode(colander.String(), oid="url", class_name = colander.SchemaNode(colander.String(), oid="url",
missing=colander.drop) 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()) title = colander.SchemaNode(colander.String())
status = colander.SchemaNode(colander.Boolean(), oid="status") status = colander.SchemaNode(colander.Boolean(), oid="status")
...@@ -88,6 +100,14 @@ class ListSchema(colander.Schema): ...@@ -88,6 +100,14 @@ class ListSchema(colander.Schema):
nama = colander.SchemaNode(colander.String(), title="Nama") nama = colander.SchemaNode(colander.String(), title="Nama")
status = colander.SchemaNode(colander.Boolean(), title="Status", status = colander.SchemaNode(colander.Boolean(), title="Status",
width='50pt') 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", level_id = colander.SchemaNode(colander.String(), title="Level",
width='50pt') width='50pt')
parent = colander.SchemaNode(colander.String(), title="Induk", parent = colander.SchemaNode(colander.String(), title="Induk",
...@@ -121,14 +141,14 @@ class ViewMenus(BaseView): ...@@ -121,14 +141,14 @@ class ViewMenus(BaseView):
else: else:
current = None current = None
found = Menus.query_kode(value['kode']) found = Menus.query_kode(value['kode']).first()
if current: if current:
if found and found.id != current.id: if found and found.id != current.id:
err_kode() err_kode()
elif found: elif found:
err_kode() err_kode()
found = Menus.query_nama(value['nama']) found = Menus.query_nama(value['nama']).first()
if current: if current:
if found and found.id != current.id: if found and found.id != current.id:
err_nama() err_nama()
...@@ -164,6 +184,7 @@ class ViewMenus(BaseView): ...@@ -164,6 +184,7 @@ class ViewMenus(BaseView):
permission='view') permission='view')
def view_act(self): def view_act(self):
request = self.req request = self.req
params = request.params params = request.params
url_dict = request.matchdict url_dict = request.matchdict
table_alias = aliased(Menus) table_alias = aliased(Menus)
...@@ -173,13 +194,16 @@ class ViewMenus(BaseView): ...@@ -173,13 +194,16 @@ class ViewMenus(BaseView):
ColumnDT(Menus.nama, mData='nama'), ColumnDT(Menus.nama, mData='nama'),
ColumnDT(table_alias.nama, mData='parent'), ColumnDT(table_alias.nama, mData='parent'),
ColumnDT(Menus.status, mData='status'), 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() \ query = Menus.query_grid() \
.outerjoin(table_alias, Menus.parent_id == table_alias.id) .outerjoin(table_alias, Menus.parent_id == table_alias.id)
query = self.filter_company(query) query = self.filter_company(query)
row_table = DataTables(request.GET, query, columns) row_table = DataTables(request.GET, query, columns)
return row_table.output_result() return row_table.output_result()
elif url_dict['act'] == 'hon': elif url_dict['act'] == 'hon':
term = 'term' in params and params['term'] or '' term = 'term' in params and params['term'] or ''
q = Menus.query(). \ q = Menus.query(). \
......
...@@ -222,10 +222,11 @@ class ViewPartner(BaseView): ...@@ -222,10 +222,11 @@ class ViewPartner(BaseView):
err_kode() err_kode()
elif found: elif found:
err_kode() err_kode()
value['is_vendor'] = 'is_vendor' in value and value[
'is_vendor'] and 1 or 0 value['is_vendor'] = 'is_vendor' in value and \
value['is_customer'] = 'is_customer' in value and value[ value['is_vendor'] and 1 or 0
'is_customer'] 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 value["status"] = 'status' in value and value['status'] and 1 or 0
def get_bindings(self, row=None): def get_bindings(self, row=None):
......
...@@ -18,7 +18,9 @@ def provinsi_widget(node, kw): ...@@ -18,7 +18,9 @@ def provinsi_widget(node, kw):
url = node and hasattr(node, 'slave_url') and node.slave_url or "" url = node and hasattr(node, 'slave_url') and node.slave_url or ""
slave = node and hasattr(node, 'slave') and node.slave or "" slave = node and hasattr(node, 'slave') and node.slave or ""
values.insert(0, ("", "Pilih Propinsi...")) values.insert(0, ("", "Pilih Propinsi..."))
readonly = kw.get("readonly", False)
return widget_os.Select2MsWidget(values=values, return widget_os.Select2MsWidget(values=values,
readonly=readonly,
url=url, url=url,
slave=slave, slave=slave,
placeholder="Pilih Provinsi") placeholder="Pilih Provinsi")
......
...@@ -103,6 +103,7 @@ class AddSchema(colander.Schema): ...@@ -103,6 +103,7 @@ class AddSchema(colander.Schema):
colander.String(), colander.String(),
widget=widget_os.CaptchaWidget(), widget=widget_os.CaptchaWidget(),
oid="captcha", title=_("Captcha")) oid="captcha", title=_("Captcha"))
if request.user and request.user.id and not external_user: if request.user and request.user.id and not external_user:
# todo: external user tidak ada password # todo: external user tidak ada password
# validasi harusnya menggunakan authentikasi ke provider lagi # validasi harusnya menggunakan authentikasi ke provider lagi
......
...@@ -52,10 +52,12 @@ ...@@ -52,10 +52,12 @@
<!-- We recommend you use "your_style.css" to override SmartAdmin <!-- We recommend you use "your_style.css" to override SmartAdmin
specific styles this will also ensure you retrain your customization with each SmartAdmin update. --> 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"> <tal:loop tal:repeat="css_resource css">
<link rel="stylesheet" href="${home}${request.static_path(css_resource)}" <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> </tal:loop>
<!--? <link href="${home}/static/v3/js/plugin/bootstrap-datepicker/css/bootstrap-datepicker.min.css"--> <!--? <link href="${home}/static/v3/js/plugin/bootstrap-datepicker/css/bootstrap-datepicker.min.css"-->
<!--? rel="stylesheet">--> <!--? rel="stylesheet">-->
...@@ -271,8 +273,10 @@ ...@@ -271,8 +273,10 @@
<!--?<script src="${home}/static/v3/js/select2.full.min.js"></script>--> <!--?<script src="${home}/static/v3/js/select2.full.min.js"></script>-->
<!--?<script src="${home}/deform_static/scripts/file_upload.js"></script>--> <!--?<script src="${home}/deform_static/scripts/file_upload.js"></script>-->
<script src="${home}/static/v3/js/osipkd.js"></script> <script src="${home}/static/v3/js/osipkd.js"></script>
<!-- LOOP ON JS RESOURCE -->
<tal:loop tal:repeat="js_resource js"> <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> </tal:loop>
<metal:js define-slot="js_files"></metal:js> <metal:js define-slot="js_files"></metal:js>
......
<html metal:use-macro="load: ./base3.1.pt"> <html metal:use-macro="load: ./base3.1.pt">
<div metal:fill-slot="content"> <div metal:fill-slot="content">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title"><i class="fa fa-fw fa-plus"></i>&nbsp;${request.title}</h3> <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> </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>
<div metal:fill-slot="scripts"> <div metal:fill-slot="scripts">
<script> <script>
</script> </script>
<div metal:define-slot="scripts"></div> <div metal:define-slot="scripts"></div>
</div> </div>
</html> </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): ...@@ -69,7 +69,6 @@ def view_file(request):
input_file = request.POST['upload'].file input_file = request.POST['upload'].file
filename = request.POST['upload'].filename.lower() filename = request.POST['upload'].filename.lower()
ext = get_ext(filename).lower() ext = get_ext(filename).lower()
if ext.lower() not in ['.png', '.ico']: if ext.lower() not in ['.png', '.ico']:
request.session.flash('File harus format png', 'error') request.session.flash('File harus format png', 'error')
return dict(form=form.render()) return dict(form=form.render())
......
...@@ -4,7 +4,8 @@ import re ...@@ -4,7 +4,8 @@ import re
import colander import colander
from deform import (widget, ) from deform import (widget, )
from opensipkd.tools import create_now, SaveFile 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.i18n import TranslationStringFactory
from pyramid.view import view_config from pyramid.view import view_config
from sqlalchemy import (func, ) from sqlalchemy import (func, )
...@@ -95,7 +96,8 @@ class Views(BaseView): ...@@ -95,7 +96,8 @@ class Views(BaseView):
path = os.path.join(base_path, 'reports') path = os.path.join(base_path, 'reports')
rml_row = open_rml_row(path + '/user.row.rml') rml_row = open_rml_row(path + '/user.row.rml')
rows = [rml_row.format(user_name=r.user_name, email=r.email, 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, pdf, filename = open_rml_pdf(path + '/user.rml', rows=rows,
company=self.req.company, company=self.req.company,
departement=self.req.departement, departement=self.req.departement,
...@@ -118,7 +120,8 @@ class Views(BaseView): ...@@ -118,7 +120,8 @@ class Views(BaseView):
values["email"] = values['email'].lower() values["email"] = values['email'].lower()
values["user_name"] = re.sub(' ', '', values['user_name']) # .lower() values["user_name"] = re.sub(' ', '', values['user_name']) # .lower()
values["security_code_date"] = create_now() 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 values["company_id"] = company_id
if 'is_api_key' in values: if 'is_api_key' in values:
values["api_key"] = generate_api_key() values["api_key"] = generate_api_key()
...@@ -261,7 +264,7 @@ class EmailValidator(colander.Email, Validator): ...@@ -261,7 +264,7 @@ class EmailValidator(colander.Email, Validator):
email = value.lower() email = value.lower()
q = DBSession.query(User).filter_by(email=email) q = DBSession.query(User).filter_by(email=email)
found = q.first() 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() email_found()
...@@ -380,5 +383,7 @@ def user_group_set(user): ...@@ -380,5 +383,7 @@ def user_group_set(user):
def query_register(): def query_register():
return DBSession.query(User.user_name, User.email, 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) User.user_name)
...@@ -306,6 +306,7 @@ class ChangePassword(colander.Schema): ...@@ -306,6 +306,7 @@ class ChangePassword(colander.Schema):
retype_password = colander.SchemaNode( retype_password = colander.SchemaNode(
colander.String(), widget=widget.PasswordWidget()) colander.String(), widget=widget.PasswordWidget())
password = colander.SchemaNode(colander.String(), password = colander.SchemaNode(colander.String(),
widget=widget.PasswordWidget(),
title=_("Old Password")) title=_("Old Password"))
......
import json
import logging
from colander import SchemaNode, null, Mapping, Invalid, text_, string_types from colander import SchemaNode, null, Mapping, Invalid, text_, string_types
from deform.widget import Widget, _StrippedString, Select2Widget from deform.widget import Widget, _StrippedString, Select2Widget
_logging = logging.getLogger(__name__)
class DokumenWidget(Widget): class DokumenWidget(Widget):
template = "opensipkd.base:/views/widgets/dokumen.pt" template = "opensipkd.base:/views/widgets/dokumen.pt"
...@@ -160,7 +165,8 @@ class BlokKavNoWidget(Widget): ...@@ -160,7 +165,8 @@ class BlokKavNoWidget(Widget):
result = "|".join([blok_kav_no, rt, rw]) result = "|".join([blok_kav_no, rt, rw])
if not blok_kav_no or not rt or not 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 return result
...@@ -183,6 +189,7 @@ class Select2MsWidget(Select2Widget): ...@@ -183,6 +189,7 @@ class Select2MsWidget(Select2Widget):
template = "select2_ms.pt" template = "select2_ms.pt"
class QtyWidget(Widget): class QtyWidget(Widget):
template = "opensipkd.base:/views/widgets/qty.pt" template = "opensipkd.base:/views/widgets/qty.pt"
readonly_template = "opensipkd.base:/views/widgets/readonly/qty.pt" readonly_template = "opensipkd.base:/views/widgets/readonly/qty.pt"
...@@ -228,6 +235,7 @@ class QtyWidget(Widget): ...@@ -228,6 +235,7 @@ class QtyWidget(Widget):
return result return result
class CaptchaWidget(Widget): class CaptchaWidget(Widget):
""" """
Renders an ``<input type="text"/>`` widget. Renders an ``<input type="text"/>`` widget.
...@@ -275,6 +283,7 @@ class CaptchaWidget(Widget): ...@@ -275,6 +283,7 @@ class CaptchaWidget(Widget):
return null return null
return pstruct return pstruct
class ImageWidget(Widget): class ImageWidget(Widget):
""" """
Renders an ``<img src="src"/>`` widget. Renders an ``<img src="src"/>`` widget.
...@@ -300,14 +309,177 @@ class ImageWidget(Widget): ...@@ -300,14 +309,177 @@ class ImageWidget(Widget):
strip = True strip = True
requirements = () requirements = ()
height = "30px" 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): def __init__(self, **kw):
super().__init__(**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): def serialize(self, field, cstruct, **kw):
if cstruct in (null, None): if cstruct in (null, None):
cstruct = "" cstruct = ""
readonly = kw.get("readonly", self.readonly) readonly = kw.get("readonly", self.readonly)
template = readonly and self.readonly_template or self.template 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) values = self.get_template_values(field, cstruct, kw)
return field.renderer(template, **values) 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): ...@@ -112,8 +112,14 @@ class DeTable(field.Field):
data=[], data=[],
allow_edit=True, allow_edit=True,
allow_delete=True, allow_delete=True,
allow_view=True,
**kw **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 "" params = params and f"?{params}" or ""
btn_close_js = "{window.location = '/'; return false;}" btn_close_js = "{window.location = '/'; return false;}"
btn_add_js = "{window.location = o%sUri+'/add%s';}" % (tableid, params) btn_add_js = "{window.location = o%sUri+'/add%s';}" % (tableid, params)
...@@ -128,9 +134,10 @@ class DeTable(field.Field): ...@@ -128,9 +134,10 @@ class DeTable(field.Field):
btn_csv_js = "{window.location = o%sUri+'/csv/act%s';}" % ( btn_csv_js = "{window.location = o%sUri+'/csv/act%s';}" % (
tableid, params) tableid, params)
btn_pdf_js = "{window.open(o%sUri+'/pdf/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}" action_suffix = f"{action_suffix}{params}"
field.Field.__init__(self, schema, **kw)
self.request = kw.get("request")
_buttons = [] _buttons = []
for button in buttons: for button in buttons:
if isinstance(button, compat.string_types): if isinstance(button, compat.string_types):
...@@ -150,12 +157,13 @@ class DeTable(field.Field): ...@@ -150,12 +157,13 @@ class DeTable(field.Field):
""") """)
_scripts.append(f'$("#{tableid + button.name}").click(function ()' + _scripts.append(f'$("#{tableid + button.name}").click(function ()' +
eval('btn_' + button.name + '_js') + ');') eval('btn_' + button.name + '_js') + ');')
self.buttons = "','".join(_buttons).replace('\n', "").replace(';', self.buttons = "','".join(_buttons).replace('\n', ""). \
';\n') replace(';', ';\n')
self.tableid = tableid self.tableid = tableid
self.scripts = ''.join(_scripts).replace(';', ";\n") self.scripts = ''.join(_scripts).replace(';', ";\n")
self.allow_edit = json.dumps(allow_edit) self.allow_edit = json.dumps(allow_edit)
self.allow_delete = json.dumps(allow_delete) self.allow_delete = json.dumps(allow_delete)
self.allow_view = json.dumps(allow_view)
table_widget = getattr(schema, "widget", None) table_widget = getattr(schema, "widget", None)
if table_widget is None: if table_widget is None:
table_widget = widget.TableWidget() table_widget = widget.TableWidget()
...@@ -185,7 +193,7 @@ class DeTable(field.Field): ...@@ -185,7 +193,7 @@ class DeTable(field.Field):
d["orderable"] = f.orderable d["orderable"] = f.orderable
data.append(f"orderable: {f.orderable}") data.append(f"orderable: {f.orderable}")
if hasattr(f, "url"): if hasattr(f, "url"):
d["url"]=f.url d["url"] = f.url
# request = kw.get("request") # request = kw.get("request")
# if request: # if request:
# d["url"] = request.static_url(f.url) # d["url"] = request.static_url(f.url)
...@@ -238,7 +246,6 @@ class DeTable(field.Field): ...@@ -238,7 +246,6 @@ class DeTable(field.Field):
self.sorts = sorts self.sorts = sorts
self.paginates = paginates self.paginates = paginates
self.filters = filters self.filters = filters
print(self.headers)
class Button(object): class Button(object):
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
data data|field.data; data data|field.data;
allow_edit allow_edit|field.allow_edit; allow_edit allow_edit|field.allow_edit;
allow_delete allow_delete|field.allow_delete; 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|{};" tal:attributes="style style; class css_class; attributes|field.widget.attributes|{};"
i18n:domain="detable" i18n:domain="detable"
...@@ -80,14 +81,17 @@ ...@@ -80,14 +81,17 @@
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; return result;
} }
} else if (${tableid}Columns[co].data === "id" && ${tableid}Columns[co].action === true) { } else if (${tableid}Columns[co].data === "id" && ${tableid}Columns[co].action === true) {
${tableid}Columns[co].render = function (id) { ${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}) { if (${allow_edit}) {
result += '<a href="${url}/' + id + '/edit"><i class="fas fa-edit" aria-hidden="true"></i></a>&nbsp;' result += '<a href="${url}/' + id + '/edit"><i class="fas fa-edit" aria-hidden="true"></i></a>&nbsp;'
} }
......
...@@ -44,7 +44,7 @@ class CommonModel(object): ...@@ -44,7 +44,7 @@ class CommonModel(object):
for column in self.__table__.columns: for column in self.__table__.columns:
value = getattr(self, column.name) value = getattr(self, column.name)
if value or null: if value or null:
if type(column.type)==DateTime: if type(column.type) == DateTime and date_format:
if value: if value:
values[column.name] = value.strftime(date_format) values[column.name] = value.strftime(date_format)
else: else:
......
...@@ -22,7 +22,10 @@ class Menus(Base, NamaModel): ...@@ -22,7 +22,10 @@ class Menus(Base, NamaModel):
parent_id = Column(Integer, ForeignKey('public.menus.id')) parent_id = Column(Integer, ForeignKey('public.menus.id'))
level_id = Column(SmallInteger) level_id = Column(SmallInteger)
order_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)) icon = Column(String(256))
css_class = Column(String(256)) css_class = Column(String(256))
need_login = Column(SmallInteger, server_default="1") need_login = Column(SmallInteger, server_default="1")
...@@ -71,4 +74,3 @@ class Menus(Base, NamaModel): ...@@ -71,4 +74,3 @@ class Menus(Base, NamaModel):
row = cls.query().filter(cls.kode == parent).first() row = cls.query().filter(cls.kode == parent).first()
if row: if row:
return cls.query().filter(cls.parent_id == row.id) return cls.query().filter(cls.parent_id == row.id)
...@@ -8,14 +8,14 @@ Pillow>=9.1.1 ...@@ -8,14 +8,14 @@ Pillow>=9.1.1
lxml>=4.9.0 lxml>=4.9.0
beautifulsoup4>=4.11.1 beautifulsoup4>=4.11.1
soupsieve>=2.3.2.post1 soupsieve>=2.3.2.post1
pip~=18.1 pip~=22.3.1
wheel~=0.37.0 wheel~=0.38.4
rsa>=4.8 rsa>=4.8
pyasn1~=0.4.8 pyasn1~=0.4.8
Chameleon>=3.10.1 Chameleon>=3.10.1
six~=1.16.0 six~=1.16.0
Mako>=1.2.0 Mako>=1.2.0
Babel~=2.10.2 Babel~=2.11.0
Beaker~=1.11.0 Beaker~=1.11.0
Pygments>=2.12.0 Pygments>=2.12.0
MarkupSafe>=2.1.1 MarkupSafe>=2.1.1
...@@ -27,7 +27,7 @@ peppercorn~=0.6 ...@@ -27,7 +27,7 @@ peppercorn~=0.6
iso8601>=1.0.2 iso8601>=1.0.2
google~=3.0.0 google~=3.0.0
cachetools>=5.2.0 cachetools>=5.2.0
certifi~=2022.5.18.1 certifi~=2022.9.24
urllib3~=1.26.6 urllib3~=1.26.6
requests>=2.28.0 requests>=2.28.0
google-api-python-client>=2.51.0 google-api-python-client>=2.51.0
...@@ -38,21 +38,21 @@ venusian~=3.0.0 ...@@ -38,21 +38,21 @@ venusian~=3.0.0
plaster~=1.0 plaster~=1.0
hupper~=1.10.3 hupper~=1.10.3
waitress>=2.1.2 waitress>=2.1.2
greenlet~=1.1.1 greenlet~=2.0.1
pyparsing>=3.0.9 pyparsing>=3.0.9
httplib2>=0.20.4 httplib2>=0.20.4
icecream~=2.1.1 icecream~=2.1.1
executing~=0.8.0 executing~=1.2.0
paginate~=0.5.6 paginate~=0.5.6
idna~=3.2 idna~=3.2
asttokens~=2.0.5 asttokens~=2.1.0
setuptools>=57.4.0 setuptools>=57.4.0
uritemplate>=4.1.1 uritemplate>=4.1.1
reportlab~=3.6.1 reportlab~=3.6.1
PyJWT>=2.4.0 PyJWT>=2.4.0
py~=1.11.0 py~=1.11.0
attrs~=21.4.0 attrs~=22.1.0
pytest~=7.1.1 pytest~=7.2.0
pluggy~=1.0.0 pluggy~=1.0.0
iniconfig~=1.1.1 iniconfig~=1.1.1
cffi>=1.15.0 cffi>=1.15.0
...@@ -65,6 +65,9 @@ pyramid_rpc ...@@ -65,6 +65,9 @@ pyramid_rpc
zipp~=3.8.0 zipp~=3.8.0
papyrus~=2.4 papyrus~=2.4
geojson~=2.5.0 geojson~=2.5.0
GeoAlchemy2~=0.12.1
pybind11~=2.9.2 pybind11~=2.9.2
qrcode~=7.3.1
\ No newline at end of file \ 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 \ 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!