Commit 63871500 by Tatang S

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

2 parents df0feac0 b58ade88
This diff could not be displayed because it is too large.
3.0.2 13-07-2023
Perubahan Colander>2.0
Perubahan SQLAlchemy>2.0
beaker>=1.12.1
3.0.1 25-04-2022 3.0.1 25-04-2022
Penambahan Feature ```detable``` Penambahan Feature ```detable```
......
...@@ -197,8 +197,30 @@ Tambahkan blok berikut ini dibawah ini file ...@@ -197,8 +197,30 @@ Tambahkan blok berikut ini dibawah ini file
proxy_set_header X-Forwarded-Host $host:$server_port; proxy_set_header X-Forwarded-Host $host:$server_port;
proxy_set_header X-Forwarded-Port $server_port; proxy_set_header X-Forwarded-Port $server_port;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 60s;
proxy_send_timeout 90s;
proxy_read_timeout 90s;
proxy_buffering off;
proxy_temp_file_write_size 64k;
proxy_pass http://127.0.0.1:6543/; proxy_pass http://127.0.0.1:6543/;
proxy_redirect off;
} }
``` ```
Other Configuration
```
[server:main]
use = egg:waitress#main
host = 0.0.0.0
port = 6543
;port = %(http_port)s digunakan jika port akan menggunakan parameter
trusted_proxy = 10.8.50.23
trusted_proxy_count = 1
trusted_proxy_headers = x-forwarded-for x-forwarded-host x-forwarded-proto x-forwarded-port
clear_untrusted_proxy_headers = yes
url_scheme = https # HTTP or https
```
...@@ -2,8 +2,6 @@ import locale ...@@ -2,8 +2,6 @@ import locale
import logging import logging
import re import re
import colander
try: try:
from urllib import (urlencode, quote, quote_plus, ) from urllib import (urlencode, quote, quote_plus, )
except ImportError: except ImportError:
...@@ -21,6 +19,8 @@ from .security import ( ...@@ -21,6 +19,8 @@ from .security import (
group_finder, group_finder,
get_user, MySecurityPolicy, get_user, MySecurityPolicy,
) )
from pyramid.csrf import get_csrf_token
from opensipkd.models import ( from opensipkd.models import (
DBSession, DBSession,
Base, Base,
...@@ -149,6 +149,7 @@ def add_global(event): ...@@ -149,6 +149,7 @@ def add_global(event):
event['change_unit'] = change_unit event['change_unit'] = change_unit
event['get_params'] = get_params event['get_params'] = get_params
event['get_urls'] = get_urls event['get_urls'] = get_urls
event['get_csrf_token'] = get_csrf_token
def get_params(params, alternate=None, settings=None): def get_params(params, alternate=None, settings=None):
...@@ -180,6 +181,13 @@ def get_ini(request, var): ...@@ -180,6 +181,13 @@ def get_ini(request, var):
return return
def get_password_strength(request):
settings = get_settings()
if 'password_strength' in settings and settings['password_strength']:
return settings['password_strength']
return True
def get_ini_params(request, params=None, alternate=None, settings=None): def get_ini_params(request, params=None, alternate=None, settings=None):
""" """
Digunakan untuk mengambil nilai dari konfigurasi sesuai params yang disebut Digunakan untuk mengambil nilai dari konfigurasi sesuai params yang disebut
...@@ -196,6 +204,10 @@ def get_ini_params(request, params=None, alternate=None, settings=None): ...@@ -196,6 +204,10 @@ def get_ini_params(request, params=None, alternate=None, settings=None):
def get_id_card_folder(ext=None): def get_id_card_folder(ext=None):
folder = get_params("partner_idcard_folder", '/tmp/idcard') folder = get_params("partner_idcard_folder", '/tmp/idcard')
if ext: if ext:
if ext and os.sep != '/':
ext = ext.replace('/', '\\')
if not os.path.exists(folder + ext):
os.makedirs(folder + ext)
return folder + ext return folder + ext
return folder return folder
...@@ -427,7 +439,7 @@ def main(global_config, **settings): ...@@ -427,7 +439,7 @@ def main(global_config, **settings):
None: {"js": "opensipkd.base:static/jquery/jquery.maskMoney.min.js"}} None: {"js": "opensipkd.base:static/jquery/jquery.maskMoney.min.js"}}
engine = engine_from_config( engine = engine_from_config(
settings, 'sqlalchemy.', client_encoding='utf8', convert_unicode=True) settings, 'sqlalchemy.', client_encoding='utf8') # , convert_unicode=True
DBSession.configure(bind=engine) DBSession.configure(bind=engine)
LogDBSession.configure(bind=engine) LogDBSession.configure(bind=engine)
Base.metadata.bind = engine Base.metadata.bind = engine
...@@ -444,6 +456,7 @@ def main(global_config, **settings): ...@@ -444,6 +456,7 @@ def main(global_config, **settings):
config = Configurator(settings=settings, config = Configurator(settings=settings,
root_factory='opensipkd.models.RootFactory', root_factory='opensipkd.models.RootFactory',
session_factory=session_factory) session_factory=session_factory)
config.set_default_csrf_options(require_csrf=False)
modules = get_modules(settings) modules = get_modules(settings)
from importlib import import_module from importlib import import_module
for module in modules: for module in modules:
...@@ -484,7 +497,9 @@ def main(global_config, **settings): ...@@ -484,7 +497,9 @@ def main(global_config, **settings):
config.add_request_method(disable_responsive, 'disable_responsive', config.add_request_method(disable_responsive, 'disable_responsive',
reify=True) reify=True)
config.add_request_method(get_ini, 'get_ini', reify=True) config.add_request_method(get_ini, 'get_ini', reify=True)
config.add_translation_dirs('opensipkd.base:locale/') config.add_request_method(get_csrf_token, 'get_csrf_token', reify=True)
# config.add_translation_dirs('opensipkd.base:locale/')
config.add_static_view('static', 'opensipkd.base:static', config.add_static_view('static', 'opensipkd.base:static',
cache_max_age=3600) cache_max_age=3600)
......
...@@ -243,7 +243,9 @@ def reset_sequence_(cls, seq): ...@@ -243,7 +243,9 @@ def reset_sequence_(cls, seq):
q = DBSession.query(cls) q = DBSession.query(cls)
if not q.first(): if not q.first():
sql = "SELECT setval('{}', 1, false)".format(seq) sql = "SELECT setval('{}', 1, false)".format(seq)
DBSession.bind.execute(sql) # DBSession.bind.execute(sql)
#sqlalchemy 2
DBSession.execute(text(sql))
def reset_sequences(): def reset_sequences():
......
$(document).ready(function () {
$('#parent_nm').bind('typeahead:selected', function (obj, datum, name) {
$('#parent_id').val(datum.id);
});
$('#parent_nm').on('input',
function (e) {
let val = $('#parent_nm').val();
if (val === null || val === "") {
$('#parent_id').val("");
}
});
});
\ No newline at end of file \ No newline at end of file
...@@ -4,4 +4,13 @@ $(document).ready(function () { ...@@ -4,4 +4,13 @@ $(document).ready(function () {
$('#parent_kd').val(datum.kode); $('#parent_kd').val(datum.kode);
console.log(datum.kode); console.log(datum.kode);
}); });
$('#parent_nm').on('input',
function (e) {
let val = $('#parent_nm').val();
if (val === null || val === "") {
$('#parent_id').val("");
$('#parent_kd').val("");
}
});
}); });
\ No newline at end of file \ No newline at end of file
...@@ -22,11 +22,13 @@ from .base_views import BaseView ...@@ -22,11 +22,13 @@ from .base_views import BaseView
from opensipkd.models import ( from opensipkd.models import (
DBSession, UserService, ) DBSession, UserService, )
from .common import DataTables, ColumnDT from .common import DataTables, ColumnDT
from pyramid.csrf import new_csrf_token
_ = TranslationStringFactory('login') _ = TranslationStringFactory('login')
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@view_config(context=HTTPNotFound, renderer='templates/404.pt') @view_config(context=HTTPNotFound, renderer='templates/404.pt')
def not_found(request): def not_found(request):
path = request.path path = request.path
...@@ -63,6 +65,7 @@ class Home(BaseView): ...@@ -63,6 +65,7 @@ class Home(BaseView):
# session = request.session # session = request.session
modules = request.menus modules = request.menus
modules_default = get_params('modules_default') modules_default = get_params('modules_default')
submodules_default = get_params('submenus')
# request.session['modules'] = modules # request.session['modules'] = modules
# request.session['modules_default'] = modules_default # request.session['modules_default'] = modules_default
log.info(request.session.peek_flash()) log.info(request.session.peek_flash())
...@@ -78,10 +81,10 @@ class Home(BaseView): ...@@ -78,10 +81,10 @@ class Home(BaseView):
if home_tpl: if home_tpl:
return render_to_response( return render_to_response(
home_tpl, home_tpl,
dict(modules=modules, logo=logo), dict(modules=modules, logo=logo, submodules=[]),
request=request request=request
) )
return dict(modules=modules, logo=logo) return dict(modules=modules, logo=logo, submodules=[])
@view_config(context=HTTPForbidden, renderer='templates/403.pt') @view_config(context=HTTPForbidden, renderer='templates/403.pt')
...@@ -149,3 +152,5 @@ two_minutes = timedelta(1.0 / 24 / 60) ...@@ -149,3 +152,5 @@ two_minutes = timedelta(1.0 / 24 / 60)
def deferred_jenis(node, kw): def deferred_jenis(node, kw):
values = kw.get('daftar_jenis', []) values = kw.get('daftar_jenis', [])
return widget.RadioChoiceWidget(values=values) return widget.RadioChoiceWidget(values=values)
...@@ -2,31 +2,25 @@ import logging ...@@ -2,31 +2,25 @@ import logging
import os import os
import re import re
from datetime import datetime from datetime import datetime
from email.utils import parseaddr
import colander
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 deform import (widget, Form, ValidationFailure, FileData, )
from opensipkd.tools.captcha import get_captcha
from opensipkd.tools.report import csv_response, file_response
from pyramid.httpexceptions import HTTPFound, HTTPNotFound from pyramid.httpexceptions import HTTPFound, HTTPNotFound
from .common import DataTables from opensipkd.base.views.upload import tmpstore
from .. import DBSession, get_params, get_urls from opensipkd.tools import dmy, get_settings, get_ext, \
from opensipkd.tools import dmy, date_from_str, get_settings, get_ext, \
date_from_str, get_random_string date_from_str, get_random_string
import colander
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, \ from opensipkd.tools.buttons import btn_save, btn_cancel, btn_close, btn_delete, \
btn_view, btn_add, btn_edit, btn_csv, \ btn_add, btn_csv, \
btn_pdf btn_pdf
from opensipkd.tools.captcha import get_captcha
from opensipkd.models import User, Menus from opensipkd.tools.report import csv_response, file_response
from .common import DataTables
from .. import DBSession, get_params, get_urls
from ..scripts.initializedb import append_csv from ..scripts.initializedb import append_csv
from ..tools.api import auth_from_rpc
from ...detable import DeTable from ...detable import DeTable
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
...@@ -167,13 +161,15 @@ class BaseView(object): ...@@ -167,13 +161,15 @@ class BaseView(object):
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.'
def route_list(self, msg=None, error="", **kwargs): def route_list(self, **kwargs):
msg = kwargs.get("msg")
error = kwargs.get("error", "")
list_url = kwargs.get("list_url", None)
if msg: if msg:
self.ses.flash(msg, error) self.ses.flash(msg, error)
list_url = kwargs.get("list_url", None)
if not list_url: if not list_url:
list_url = self.req.route_url(self.list_route) list_url = self.req.route_url(self.list_route)
log.error(list_url)
if self.headers: if self.headers:
return HTTPFound(location=get_urls(list_url), return HTTPFound(location=get_urls(list_url),
headers=self.headers) headers=self.headers)
...@@ -342,10 +338,10 @@ class BaseView(object): ...@@ -342,10 +338,10 @@ class BaseView(object):
def cancel_act(self, **kwargs): def cancel_act(self, **kwargs):
return self.route_list(**kwargs) return self.route_list(**kwargs)
def after_add(self, **kwargs): def after_add(self, row=None, **kwargs):
return self.route_list(**kwargs) return self.route_list(**kwargs)
def after_edit(self, **kwargs): def after_edit(self, row=None, **kwargs):
return self.route_list(**kwargs) return self.route_list(**kwargs)
def after_view(self, **kwargs): def after_view(self, **kwargs):
......
import logging
import colander import colander
from datatables import ColumnDT
from deform import (widget, ) from deform import (widget, )
from pyramid.view import (view_config, ) from pyramid.view import (view_config, )
from sqlalchemy.orm import aliased
from opensipkd.models import ResProvinsi, ResDati2, ResDesa, User
from .partner_base import PartnerSchema, NamaSchema
from opensipkd.models import DBSession, ResCompany, ResKecamatan, Partner from opensipkd.models import DBSession, ResCompany, ResKecamatan, Partner
from opensipkd.models import ResProvinsi, ResDati2, ResDesa, User
from . import DataTables
from .partner_base import PartnerSchema
from .. import get_urls
from ..views import BaseView from ..views import BaseView
log = logging.getLogger(__name__)
SESS_ADD_FAILED = 'Tambah pemda gagal' SESS_ADD_FAILED = 'Tambah pemda gagal'
SESS_EDIT_FAILED = 'Edit pemda gagal' SESS_EDIT_FAILED = 'Edit pemda gagal'
...@@ -20,14 +27,34 @@ def company_widget(node, kw): ...@@ -20,14 +27,34 @@ def company_widget(node, kw):
class AddSchema(PartnerSchema): class AddSchema(PartnerSchema):
parent_id = colander.SchemaNode(
colander.Integer(),
widget=widget.HiddenWidget(), oid="parent_id", missing=colander.drop,
)
parent_nm = colander.SchemaNode(
colander.String(), missing=colander.drop,
widget=widget.AutocompleteInputWidget(
size=60, min_length=3,
requirements=(("typeahead", None), ("deform", None),
{"js": "opensipkd.base:static/js/form/company.js"})
),
oid="parent_nm", title="Induk")
def after_bind(self, node, kw): def after_bind(self, node, kw):
self["email"].missing = colander.drop self["email"].missing = colander.drop
request = kw["request"]
self["parent_nm"].widget = widget.AutocompleteInputWidget(
size=60, min_length=3,
requirements=(("typeahead", None), ("deform", None),
{"js": "opensipkd.base:static/js/form/company.js"}),
values=get_urls(f"{request.route_url('company')}/hon/act"))
class EditSchema(AddSchema): class EditSchema(AddSchema):
id = colander.SchemaNode(colander.String(), missing=colander.drop, id = colander.SchemaNode(colander.String(), missing=colander.drop,
widget=widget.HiddenWidget(readonly=True), widget=widget.HiddenWidget(readonly=True),
visible=False,) visible=False, )
partner_id = colander.SchemaNode(colander.Integer(), partner_id = colander.SchemaNode(colander.Integer(),
widget=widget.HiddenWidget(), widget=widget.HiddenWidget(),
missing=colander.drop, missing=colander.drop,
...@@ -35,7 +62,7 @@ class EditSchema(AddSchema): ...@@ -35,7 +62,7 @@ class EditSchema(AddSchema):
class ListSchema(colander.Schema): class ListSchema(colander.Schema):
id = colander.SchemaNode(colander.String(),title="Action") id = colander.SchemaNode(colander.String(), title="Action")
kode = colander.SchemaNode( kode = colander.SchemaNode(
colander.String(), colander.String(),
validator=colander.Length(max=32), validator=colander.Length(max=32),
...@@ -46,6 +73,14 @@ class ListSchema(colander.Schema): ...@@ -46,6 +73,14 @@ class ListSchema(colander.Schema):
colander.String(), colander.String(),
validator=colander.Length(max=64), validator=colander.Length(max=64),
oid="nama") oid="nama")
parent_nm = colander.SchemaNode(
colander.String(),
validator=colander.Length(max=64),
oid="parent_nm",
field="alias.nama"
)
status = colander.SchemaNode( status = colander.SchemaNode(
colander.Integer(), colander.Integer(),
widget=widget.CheckboxWidget(), widget=widget.CheckboxWidget(),
...@@ -116,16 +151,19 @@ class ViewCompany(BaseView): ...@@ -116,16 +151,19 @@ class ViewCompany(BaseView):
found = User.get_by_identity(value.get('email')) found = User.get_by_identity(value.get('email'))
if found: if found:
err_user() err_user()
value["status"]="status" in value and value["status"] and 1 or 0 value["status"] = "status" in value and value["status"] and 1 or 0
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_vendor"] and 1 or 0
value["is_customer"]="is_customer" in value and value["is_customer"] and 1 or 0 value["is_customer"] = "is_customer" in value and value["is_customer"] and 1 or 0
def get_bindings(self, row=None): def get_bindings(self, row=None):
provinsi_list = ResProvinsi.get_list() provinsi_list = ResProvinsi.get_list()
partner = row and row.partner or None partner = row and row.partner or None
dati2_list = partner and partner.provinsi_id and ResDati2.get_list(partner.provinsi_id) or [] dati2_list = partner and partner.provinsi_id and ResDati2.get_list(
kecamatan_list = partner and partner.dati2_id and ResKecamatan.get_list(partner.dati2_id) or [] partner.provinsi_id) or []
desa_list = partner and partner.kecamatan_id and ResDesa.get_list(partner.kecamatan_id) or [] kecamatan_list = partner and partner.dati2_id and ResKecamatan.get_list(
partner.dati2_id) or []
desa_list = partner and partner.kecamatan_id and ResDesa.get_list(
partner.kecamatan_id) or []
return dict(provinsi_list=provinsi_list, return dict(provinsi_list=provinsi_list,
dati2_list=dati2_list, dati2_list=dati2_list,
kecamatan_list=kecamatan_list, kecamatan_list=kecamatan_list,
...@@ -143,10 +181,46 @@ class ViewCompany(BaseView): ...@@ -143,10 +181,46 @@ class ViewCompany(BaseView):
def view_list(self): def view_list(self):
return super().view_list() return super().view_list()
def filter_company(self, query):
if self.req.user.company_id:
return query.filter(
self.table.id == self.req.user.company_id)
return query
@view_config(route_name='company-act', renderer='json', @view_config(route_name='company-act', renderer='json',
permission='view') permission='view')
def view_act(self): def view_act(self):
return super().view_act() request = self.req
ses = request.session
params = request.params
url_dict = request.matchdict
alias = aliased(ResCompany)
if url_dict['act'] == 'grid':
columns = [ColumnDT(self.table.id, mData='id'),
ColumnDT(self.table.kode, mData='kode'),
ColumnDT(self.table.nama, mData='nama'),
ColumnDT(alias.nama, mData='parent_nm'),
ColumnDT(self.table.status, mData='status'),
# ColumnDT(Departemen.level_id, mData='level_id'),
]
query = DBSession.query().select_from(ResCompany).outerjoin(
alias, ResCompany.parent_id == 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 = DBSession.query(self.table) \
.filter(self.table.status == 1,
self.table.nama.ilike('%%%s%%' % term)) \
.order_by(self.table.nama)
rows = q.all()
r = []
for k in rows:
d = dict(id=k.id, value=k.nama, kode=k.kode, nama=k.nama)
r.append(d)
log.error(r)
return r
@view_config(route_name='company-add', @view_config(route_name='company-add',
renderer='templates/form.pt', permission='company') renderer='templates/form.pt', permission='company')
...@@ -154,12 +228,17 @@ class ViewCompany(BaseView): ...@@ -154,12 +228,17 @@ class ViewCompany(BaseView):
return super(ViewCompany, self).view_add() return super(ViewCompany, self).view_add()
def get_values(self, row, istime=False): def get_values(self, row, istime=False):
d = super().get_values(row,istime) d = super().get_values(row, istime)
partner = row.partner partner = row.partner
if partner : if partner:
p = partner.to_dict() p = partner.to_dict()
del p["id"] del p["id"]
d.update(p) d.update(p)
if "parent_id" in d and d["parent_id"]:
parent = ResCompany.query_id(d["parent_id"]).first()
if parent:
d["parent_nm"] = parent.nama
return d return d
@view_config(route_name='company-edit', @view_config(route_name='company-edit',
...@@ -173,6 +252,11 @@ class ViewCompany(BaseView): ...@@ -173,6 +252,11 @@ class ViewCompany(BaseView):
return super(ViewCompany, self).view_delete() return super(ViewCompany, self).view_delete()
def save_request(self, values, row=None): def save_request(self, values, row=None):
# Save Partner First
parent_id = "parent_id" in values and values["parent_id"] or None
if parent_id:
del values["parent_id"]
if 'partner_id' in values: if 'partner_id' in values:
part = Partner.query_id(values['partner_id']).first() part = Partner.query_id(values['partner_id']).first()
values["id"] = part.id values["id"] = part.id
...@@ -185,8 +269,12 @@ class ViewCompany(BaseView): ...@@ -185,8 +269,12 @@ class ViewCompany(BaseView):
part.from_dict(values) part.from_dict(values)
DBSession.add(part) DBSession.add(part)
DBSession.flush() DBSession.flush()
# Save Company
if part: if part:
values["partner_id"] = part.id values["partner_id"] = part.id
if parent_id:
values["parent_id"] = parent_id
if "id" in self.req.matchdict: if "id" in self.req.matchdict:
values["id"] = self.req.matchdict["id"] values["id"] = self.req.matchdict["id"]
...@@ -197,4 +285,3 @@ class ViewCompany(BaseView): ...@@ -197,4 +285,3 @@ class ViewCompany(BaseView):
DBSession.add(part) DBSession.add(part)
DBSession.flush() DBSession.flush()
return row return row
...@@ -5,14 +5,15 @@ from datetime import datetime ...@@ -5,14 +5,15 @@ from datetime import datetime
import colander import colander
from deform import (widget, ) from deform import (widget, )
from opensipkd.tools import (get_ext, get_random_string, get_settings)
from pyramid.view import (view_config, ) from pyramid.view import (view_config, )
from sqlalchemy import func from sqlalchemy import func
from sqlalchemy.orm import aliased from sqlalchemy.orm import aliased
from opensipkd.models import DBSession, Departemen, Partner, PartnerDepartemen, ResCompany
from opensipkd.tools import (get_ext, get_random_string, get_settings)
from .company import company_widget from .company import company_widget
from .upload import AddSchema as UploadSchema from .upload import AddSchema as UploadSchema
from opensipkd.models import DBSession, Departemen, Partner, PartnerDepartemen from .. import get_params
from ..views import ColumnDT, DataTables, BaseView, get_urls from ..views import ColumnDT, DataTables, BaseView, get_urls
SESS_ADD_FAILED = 'Tambah departemen gagal' SESS_ADD_FAILED = 'Tambah departemen gagal'
...@@ -45,10 +46,11 @@ class AddSchema(colander.Schema): ...@@ -45,10 +46,11 @@ class AddSchema(colander.Schema):
widget=widget.AutocompleteInputWidget( widget=widget.AutocompleteInputWidget(
size=60, min_length=3, size=60, min_length=3,
requirements=(("typeahead", None), ("deform", None), requirements=(("typeahead", None), ("deform", None),
{"js": "opensipkd.base:static/js/form/departemen.js"}) {"js": "opensipkd.base:static/js/form/departemen.js"}),
# options={"allowClear": True}
), ),
oid="parent_nm", title="Induk") oid="parent_nm", title="Induk")
parent_kd = colander.SchemaNode(colander.String(), parent_kd = colander.SchemaNode(colander.String(),
widget=widget.TextInputWidget(css_class="readonly"), widget=widget.TextInputWidget(css_class="readonly"),
missing=colander.drop, oid="parent_kd", title="Kode Induk") missing=colander.drop, oid="parent_kd", title="Kode Induk")
...@@ -75,19 +77,13 @@ class AddSchema(colander.Schema): ...@@ -75,19 +77,13 @@ class AddSchema(colander.Schema):
def after_bind(self, schema, kwargs): def after_bind(self, schema, kwargs):
request = kwargs["request"] request = kwargs["request"]
# self["parent_nm"] = colander.SchemaNode(
# colander.String(),
# missing=colander.drop,
# widget=AutocompleteInputWidget(
# size=60, min_length=3,
# values=f"{request.route_url('departemen')}/hon/act"),
# oid="parent_nm",
# title="Induk", )
self["parent_nm"].widget = widget.AutocompleteInputWidget( self["parent_nm"].widget = widget.AutocompleteInputWidget(
size=60, min_length=3, size=60, min_length=3,
requirements=(("typeahead", None), ("deform", None), requirements=(("typeahead", None), ("deform", None),
{"js": "opensipkd.base:static/js/form/departemen.js"}), {"js": "opensipkd.base:static/js/form/departemen.js"}),
values=get_urls(f"{request.route_url('departemen')}/hon/act")) values=get_urls(f"{request.route_url('departemen')}/hon/act"),
)
if request.user.company_id: if request.user.company_id:
self["company_id"].widget = widget.HiddenWidget() self["company_id"].widget = widget.HiddenWidget()
self["company_id"].default = request.user.company_id self["company_id"].default = request.user.company_id
...@@ -100,12 +96,13 @@ class EditSchema(AddSchema): ...@@ -100,12 +96,13 @@ class EditSchema(AddSchema):
class ListSchema(colander.Schema): class ListSchema(colander.Schema):
id = colander.SchemaNode(colander.String(), title="Action", visible=False) id = colander.SchemaNode(colander.String(), title="Action", visible=False)
kode = colander.SchemaNode(colander.String(), title="Kode", width='100pt') kode = colander.SchemaNode(colander.String(), title="Kode")
nama = colander.SchemaNode(colander.String(), title="Nama") nama = colander.SchemaNode(colander.String(), title="Nama")
status = colander.SchemaNode(colander.Boolean(), title="Status", width='50pt', status = colander.SchemaNode(colander.Boolean(), title="Status", width='50pt',
widget=widget.CheckboxWidget()) widget=widget.CheckboxWidget())
level_id = colander.SchemaNode(colander.Integer(), title="Level", width='50pt') level_id = colander.SchemaNode(colander.Integer(), title="Level", width='40pt')
parent = colander.SchemaNode(colander.String(), title="Induk", width='200pt') parent = colander.SchemaNode(colander.String(), title="Induk")
company_nm = colander.SchemaNode(colander.String(), title="Company")
class ViewDepartemen(BaseView): class ViewDepartemen(BaseView):
...@@ -166,14 +163,22 @@ class ViewDepartemen(BaseView): ...@@ -166,14 +163,22 @@ class ViewDepartemen(BaseView):
if child.children: if child.children:
self.update_children(child.children) self.update_children(child.children)
def save_request(self, values, row=None): #save(self, row, values): def save_request(self, values, row=None): # save(self, row, values):
for k, v in values.items(): for k, v in values.items():
if not v: if not v:
setattr(row, k, None) setattr(row, k, None)
values["level_id"] = 1
if "parent_id" in values and values["parent_id"]:
qry_parent = self.table.query_id(values["parent_id"])
parent = qry_parent.first()
if parent and parent.level_id:
values["level_id"] = parent.level_id + 1
if "parent_id" not in values:
values["parent_id"] = None
row = super().save_request(values, row) row = super().save_request(values, row)
return row return row
@view_config(route_name='departemen-view', @view_config(route_name='departemen-view',
renderer='templates/form.pt', permission='departemen') renderer='templates/form.pt', permission='departemen')
def view_view(self): def view_view(self):
...@@ -199,9 +204,12 @@ class ViewDepartemen(BaseView): ...@@ -199,9 +204,12 @@ class ViewDepartemen(BaseView):
ColumnDT(Departemen.nama, mData='nama'), ColumnDT(Departemen.nama, mData='nama'),
ColumnDT(dep_alias.nama, mData='parent'), ColumnDT(dep_alias.nama, mData='parent'),
ColumnDT(Departemen.status, mData='status'), ColumnDT(Departemen.status, mData='status'),
ColumnDT(Departemen.level_id, mData='level_id'), ] ColumnDT(Departemen.level_id, mData='level_id'),
ColumnDT(ResCompany.nama, mData='company_nm'), ]
query = DBSession.query().select_from(Departemen).outerjoin( query = DBSession.query().select_from(Departemen).outerjoin(
dep_alias, Departemen.parent_id == dep_alias.id) dep_alias, Departemen.parent_id == dep_alias.id).outerjoin(
ResCompany, self.table.company_id == ResCompany.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()
...@@ -244,7 +252,7 @@ class ViewDepartemen(BaseView): ...@@ -244,7 +252,7 @@ class ViewDepartemen(BaseView):
# todo Check ulang untuk hon # todo Check ulang untuk hon
term = 'term' in params and params['term'] or '' term = 'term' in params and params['term'] or ''
settings = get_settings() settings = get_settings()
level_id = self.req.get_params('departemen_chg_id', 0) level_id = get_params('departemen_chg_id', 0)
q = DBSession.query(Departemen).filter(Departemen.status == 1, q = DBSession.query(Departemen).filter(Departemen.status == 1,
Departemen.nama.ilike( Departemen.nama.ilike(
'%%%s%%' % '%%%s%%' %
...@@ -300,6 +308,9 @@ class ViewDepartemen(BaseView): ...@@ -300,6 +308,9 @@ class ViewDepartemen(BaseView):
r.append(d) r.append(d)
return r return r
def get_bindings(self, row=None):
return {"company_list": ResCompany.get_list()}
@view_config(route_name='departemen-add', renderer='templates/form.pt', @view_config(route_name='departemen-add', renderer='templates/form.pt',
permission='departemen') permission='departemen')
def view_add(self): def view_add(self):
......
...@@ -25,7 +25,7 @@ class AddSchema(colander.Schema): ...@@ -25,7 +25,7 @@ class AddSchema(colander.Schema):
cur_departemen_nm = colander.SchemaNode( cur_departemen_nm = colander.SchemaNode(
colander.String(), colander.String(),
widget=widget.TextInputWidget(readonly=True), widget=widget.TextInputWidget(readonly=True),
title="Departemen Kini", title="Departemen Aktif",
missing=colander.drop) missing=colander.drop)
departemen_id = colander.SchemaNode( departemen_id = colander.SchemaNode(
colander.Integer(), colander.Integer(),
...@@ -44,6 +44,7 @@ class AddSchema(colander.Schema): ...@@ -44,6 +44,7 @@ class AddSchema(colander.Schema):
level_id = colander.SchemaNode( level_id = colander.SchemaNode(
colander.String(), colander.String(),
# widget = widget.HiddenWidget(), # widget = widget.HiddenWidget(),
missing=colander.drop,
oid="level_id", oid="level_id",
title="Level") title="Level")
callback = colander.SchemaNode( callback = colander.SchemaNode(
...@@ -57,7 +58,7 @@ class ChangeDepartemen(BaseView): ...@@ -57,7 +58,7 @@ class ChangeDepartemen(BaseView):
######## ########
# List # # List #
######## ########
@view_config(route_name='departemen-chg', renderer='templates/departemen/chg.pt') @view_config(route_name='departemen-chg', renderer='templates/departemen_chg.pt')
def view_departemen_chg(self): def view_departemen_chg(self):
request = self.req request = self.req
ses = request.session ses = request.session
......
...@@ -30,11 +30,13 @@ SESS_EDIT_FAILED = 'Edit partner gagal' ...@@ -30,11 +30,13 @@ SESS_EDIT_FAILED = 'Edit partner gagal'
class AddSchema(PartnerSchema): class AddSchema(PartnerSchema):
is_vendor = colander.SchemaNode( is_vendor = colander.SchemaNode(
colander.Boolean(), colander.Integer(),
widget=widget.CheckboxWidget(true_val="1", false_val="0"),
oid="is_vendor", oid="is_vendor",
title="Vendor") title="Vendor")
is_customer = colander.SchemaNode( is_customer = colander.SchemaNode(
colander.Boolean(), colander.Integer(),
widget=widget.CheckboxWidget(true_val="1", false_val="0"),
oid="is_customer", oid="is_customer",
title="Customer") title="Customer")
company_id = colander.SchemaNode( company_id = colander.SchemaNode(
......
import colander import colander
import json from deform import (widget, )
from deform import (Form, widget, ValidationFailure, )
from pyramid.httpexceptions import (HTTPFound, )
from pyramid.view import (view_config, ) from pyramid.view import (view_config, )
from sqlalchemy import or_ from sqlalchemy import or_
from sqlalchemy.orm import aliased from sqlalchemy.orm import aliased
...@@ -11,7 +9,7 @@ from opensipkd.models import DBSession as PartnerDBSession, DBSession, \ ...@@ -11,7 +9,7 @@ from opensipkd.models import DBSession as PartnerDBSession, DBSession, \
from opensipkd.models import Departemen, Jabatan from opensipkd.models import Departemen, Jabatan
from opensipkd.models import Partner, PartnerDepartemen from opensipkd.models import Partner, PartnerDepartemen
from opensipkd.tools import dmy, date_from_str from opensipkd.tools import dmy, date_from_str
from opensipkd.tools.buttons import btn_cancel, btn_save, btn_delete, btn_close from . import widget_os
from ..views import ColumnDT, DataTables, BaseView from ..views import ColumnDT, DataTables, BaseView
SESS_ADD_FAILED = 'Tambah posisi partner gagal' SESS_ADD_FAILED = 'Tambah posisi partner gagal'
...@@ -19,20 +17,26 @@ SESS_EDIT_FAILED = 'Edit posisi partner gagal' ...@@ -19,20 +17,26 @@ SESS_EDIT_FAILED = 'Edit posisi partner gagal'
class AddSchema(colander.Schema): class AddSchema(colander.Schema):
nama_widget = widget.AutocompleteInputWidget(
size=60,
values='/partner/hon/act',
min_length=2,
style="z-index: 100000 !important;")
departemen_widget = widget.AutocompleteInputWidget( departemen_widget = widget.AutocompleteInputWidget(
size=60, size=60,
values='/departemen/hon/act', values='/departemen/hon/act',
min_length=1) min_length=2,
style="z-index: 100001 !important;")
nama_widget = widget.AutocompleteInputWidget(
size=60,
values='/partner/hon/act',
min_length=1)
jabatan_widget = widget.AutocompleteInputWidget( jabatan_widget = widget.AutocompleteInputWidget(
size=60, size=60,
values='/jabatan/hon/act', values='/jabatan/hon/act',
min_length=1) min_length=2,
style="z-index: 99999 !important;")
partner_id = colander.SchemaNode( partner_id = colander.SchemaNode(
colander.Integer(), colander.Integer(),
...@@ -66,11 +70,12 @@ class AddSchema(colander.Schema): ...@@ -66,11 +70,12 @@ class AddSchema(colander.Schema):
mulai = colander.SchemaNode( mulai = colander.SchemaNode(
colander.String(), colander.String(),
oid="mulai", oid="mulai",
widget=widget.DateInputWidget(format="dd-mm-yyyy")) widget=widget_os.DateInputWidget(css_class="date", format="dd-mm-yyyy"))
selesai = colander.SchemaNode( selesai = colander.SchemaNode(
colander.String(), colander.String(),
oid="selesai", oid="selesai",
widget=widget.DateInputWidget(format="dd-mm-yyyy")) widget=widget_os.DateInputWidget(css_class="date", format="dd-mm-yyyy",
style="z-index: 9999 !important;"))
class EditSchema(AddSchema): class EditSchema(AddSchema):
......
<div metal:use-macro="load: main.pt"> <div metal:use-macro="load: main.pt">
<div metal:fill-slot="content"> <div metal:fill-slot="content">
<div class="panel panel-default">
<div class="panel-body">
<div class="col-md-12">
<div tal:content="structure form"/> <div tal:content="structure form"/>
</div> </div>
</div>
</div>
</div>
</div> </div>
<div metal:use-macro="load: main.pt"> <div metal:use-macro="load: main.pt">
<div metal:fill-slot="content"> <div metal:fill-slot="content">
<div class="panel panel-default">
<div class="panel-body">
<div class="col-md-12">
<div tal:content="structure form"/> <div tal:content="structure form"/>
</div> </div>
</div>
</div>
</div>
</div> </div>
<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">${request.title}</h3>
</div>
<div class="panel-body">
<form method="post" accept-charset="utf-8" id="myform" class="form-horizontal" enctype="multipart/form-data">
<div class="alert alert-danger" tal:condition="form and form.error and True or False">
<div class="error-msg-lbl">Kesalahan Pengisian Form</div>
<p class="error-msg">${form.errormsg}</p>
</div>
<div class="form-group" tal:repeat="f form">
<div id="item-${f.oid}" style="${f.widget.hidden and 'display:none;' or 'display:block;'}">
<label for="${f.oid}" class="control-label col-md-2 ${f.required and 'required' or ''} " id="req-${f.oid}">
${f.title}</label>
<div class="col-md-3">
${structure:f.serialize()}
<p id="error-${f.oid}" class="help-block" tal:condition="f.error"
tal:repeat="error f.error.messages()">
${error}</p>
</div>
</div>
</div>
<div class="col-md-4">
<label class="control-label col-md-3"></label>
<button type="submit" class="btn btn-primary" id="simpan" name="simpan">Simpan</button>
<button type="submit" class="btn btn-warning" id="batal" name="batal">Tutup</button>
</div>
</form>
</div> <!--panel-body-->
</div>
</div>
<div metal:fill-slot="scripts">
<script src="${home}/deform_static/scripts/typeahead.min.js"></script>
<script>
$(document).ready(function () {
$('#departemen_nm').typeahead({"minLength": 1, "remote": "/departemen/hon_level/act?term=%QUERY", "limit": 8});
$('#departemen_nm').bind('typeahead:selected', function (obj, datum, name) {
$('#departemen_id').val(datum.id);
$('#departemen_kd').val(datum.kode);
$('#level_id').val(datum.level_id);
});
$('#departemen_kd').attr("readonly", true);
$('#level_id').attr("readonly", true);
});
</script>
</div>
</html>
\ No newline at end of file \ No newline at end of file
<html metal:use-macro="load: ./base3.1.pt"> <html metal:use-macro="load: ./base3.1.pt"
tal:define="scripts scripts|scripts" >
<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">
...@@ -12,15 +13,15 @@ ...@@ -12,15 +13,15 @@
<div metal:fill-slot="scripts"> <div metal:fill-slot="scripts">
<script> <script>
// $(document).ready(function () { $(document).ready(function () {
// $(".read-only").attr("readonly", true); // $(".read-only").attr("readonly", true);
// $(".readonly").attr("readonly", true); $(".readonly").attr("readonly", true);
// $(".date").attr("readonly", true); $(".date").attr("readonly", true);
// $(".date").datepicker({ // $(".date").datepicker({
// format: 'dd-mm-yyyy' // format: 'dd-mm-yyyy'
// }); // });
<!--? ${structure:scripts}--> ${structure:scripts}
// }); });
</script> </script>
<div metal:define-slot="scripts"></div> <div metal:define-slot="scripts"></div>
</div> </div>
......
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
<img alt="" class="icon-modul" src="${home}/static/icon/${modul.replace('/','_')}.png"> <img alt="" class="icon-modul" src="${home}/static/icon/${modul.replace('/','_')}.png">
<span>${modules[modul]}</span> <span>${modules[modul]}</span>
</a> </a>
<a tal:condition="modul.find('://')>-1" target="_blank" <a tal:condition="modul.find('://')>-1"
href="${modul}" class="box"> href="${modul}" class="box">
<img alt="" class="icon-modul" <img alt="" class="icon-modul"
src="${home}/static/icon/${modul.replace('://','').replace('https','').replace('http','')}.png"> src="${home}/static/icon/${modul.replace('://','').replace('https','').replace('http','')}.png">
......
...@@ -83,6 +83,14 @@ ...@@ -83,6 +83,14 @@
href="${home}/reset-password">Lupa Password?</a> href="${home}/reset-password">Lupa Password?</a>
</div> </div>
</section> </section>
<section>
<div tal:condition="'csrf_token' in form">
<div tal:define="field form['csrf_token']" style="display: none;">
${structure:field.serialize()}
</div>
</div>
</section>
</fieldset> </fieldset>
<footer> <footer>
<section> <section>
......
<div metal:use-macro="load: main.pt"> <div metal:use-macro="load: main.pt">
<div metal:fill-slot="content"> <div metal:fill-slot="content">
<div class="panel panel-default">
<div class="panel-body">
<div class="col-md-12">
<div tal:content="structure form"/> <div tal:content="structure form"/>
</div> </div>
</div>
</div>
</div>
</div> </div>
...@@ -9,9 +9,6 @@ ...@@ -9,9 +9,6 @@
<div class="panel-body"> <div class="panel-body">
<div class="col-md-10 col-md-offset-1"> <div class="col-md-10 col-md-offset-1">
<blockquote>
<p>Untuk Background, beri nama background pada file<br>Untuk Logo beri nama logo pada file</p>
</blockquote>
<div tal:content="structure form"/> <div tal:content="structure form"/>
</div> </div>
</div> </div>
......
import os import os
import colander import colander
from deform import ( from deform import (Form, widget, FileData, )
Form,
widget,
FileData,
)
from deform.interfaces import FileUploadTempStore from deform.interfaces import FileUploadTempStore
from opensipkd.tools import (
get_ext,
dict_to_str,
)
from pyramid.httpexceptions import HTTPFound from pyramid.httpexceptions import HTTPFound
from pyramid.view import view_config from pyramid.view import view_config
from opensipkd.tools import (get_ext, dict_to_str, )
from .view_tools import CSRFSchema
from .. import get_urls from .. import get_urls
# from unggah import DbUpload
def route_list(request, p={}): def route_list(request, p={}):
q = dict_to_str(p) q = dict_to_str(p)
return HTTPFound(location=get_urls(request.route_url('upload-logo', _query=q))) return HTTPFound(location=get_urls(request.route_url('upload-logo', _query=q)))
...@@ -43,46 +38,46 @@ def route_list(request, p={}): ...@@ -43,46 +38,46 @@ def route_list(request, p={}):
tmpstore = FileUploadTempStore() tmpstore = FileUploadTempStore()
class AddSchema(colander.Schema): class AddSchema(CSRFSchema):
upload = colander.SchemaNode( upload = colander.SchemaNode(
FileData(), FileData(),
widget=widget.FileUploadWidget(tmpstore), widget=widget.FileUploadWidget(tmpstore),
title='Unggah') title='Unggah')
typ = colander.SchemaNode( image_for = colander.SchemaNode(
colander.String(), colander.String(),
widget=widget.SelectWidget(values=(('img', "Image"), ('icon', "Icon"))), widget=widget.SelectWidget(values=(('oth', "Other"), ('logo', "Logo"),
title='Jenis') ('bg', "Background"))),
title='Peruntukan')
def get_form(schema_cls): def get_form(request, schema_cls):
schema = schema_cls() schema = schema_cls()
schema = schema.bind(request=request)
return Form(schema, buttons=('simpan', 'batal')) return Form(schema, buttons=('simpan', 'batal'))
@view_config(route_name='upload-logo', @view_config(route_name='upload-logo',
renderer='templates/upload.pt', renderer='templates/upload.pt',
permission='upload-logo') permission='upload-logo', require_csrf=True)
def view_file(request): def view_file(request):
form = get_form(AddSchema) form = get_form(request, AddSchema)
if request.POST: if request.POST:
if 'simpan' in request.POST: if 'simpan' in request.POST:
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' atau 'ico'", 'error')
return dict(form=form.render()) return dict(form=form.render())
_here = os.path.dirname(__file__) _here = os.path.dirname(__file__)
static_path = os.path.join(os.path.dirname(_here), 'static') static_path = os.path.join(os.path.dirname(_here), 'static')
if filename.startswith('logo'): fname = filename
if request.POST["image_for"] == "logo":
fname = f"logo{ext}" fname = f"logo{ext}"
elif filename.startswith('background'): elif request.POST["image_for"] == "bg":
fname = f"background{ext}" fname = f"background{ext}"
else: typ = ext == '.png' and "img" or 'icon'
fname = filename folder = os.path.join(static_path, typ)
folder = os.path.join(static_path, request.POST['typ'])
if not os.path.exists(folder): if not os.path.exists(folder):
os.makedirs(folder) os.makedirs(folder)
...@@ -94,6 +89,7 @@ def view_file(request): ...@@ -94,6 +89,7 @@ def view_file(request):
if not data: if not data:
break break
output_file.write(data) output_file.write(data)
request.session.flash(f"Sukses upload {fname}")
return route_list(request) return route_list(request)
return dict(form=form.render()) return dict(form=form.render())
...@@ -124,9 +124,11 @@ class Views(BaseView): ...@@ -124,9 +124,11 @@ 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 \ # company_id = request.user and request.user.company_id or "company_id" in values and \
values["company_id"] or None # values["company_id"] or None
values["company_id"] = company_id # values["company_id"] = company_id
if "company_id" not in values:
values["company_id"]=None
if 'is_api_key' in values: if 'is_api_key' in values:
values["api_key"] = generate_api_key() values["api_key"] = generate_api_key()
insert = not row insert = not row
......
...@@ -25,30 +25,28 @@ from datetime import timedelta, datetime ...@@ -25,30 +25,28 @@ from datetime import timedelta, datetime
from importlib import import_module from importlib import import_module
import colander import colander
import requests
from deform import widget, Form, ValidationFailure, Button from deform import widget, Form, ValidationFailure, Button
from icecream import ic from pyramid.csrf import new_csrf_token
from pyramid.httpexceptions import HTTPFound, HTTPNotFound from pyramid.httpexceptions import HTTPFound, HTTPNotFound
from pyramid.renderers import render_to_response from pyramid.renderers import render_to_response
from pyramid.security import remember, forget from pyramid.security import remember, forget
from pyramid.view import view_config from pyramid.view import view_config
from pyramid_mailer.message import Message
from ziggurat_foundations.models.services.external_identity import \ from ziggurat_foundations.models.services.external_identity import \
ExternalIdentityService ExternalIdentityService
from ziggurat_foundations.models.services.user import UserService from ziggurat_foundations.models.services.user import UserService
from opensipkd.base import DBSession, get_params from opensipkd.base import DBSession, get_params
from opensipkd.base.views import _, one_hour, two_minutes, BaseView
from opensipkd.models import User, ExternalIdentity, Partner from opensipkd.models import User, ExternalIdentity, Partner
from opensipkd.tools import create_now, set_user_log, get_settings from opensipkd.tools import create_now, set_user_log, get_settings
from opensipkd.base.views import _, one_hour, two_minutes, BaseView
from pyramid_mailer.message import Message
from opensipkd.tools.buttons import btn_cancel from opensipkd.tools.buttons import btn_cancel
from opensipkd.tools.form_api import formfield2dict
from .. import get_urls from .. import get_urls
from .view_tools import CSRFSchema
log = __import__("logging").getLogger(__name__) log = __import__("logging").getLogger(__name__)
class Login(colander.Schema): class Login(CSRFSchema):
username = colander.SchemaNode( username = colander.SchemaNode(
colander.String(), colander.String(),
widget=widget.TextInputWidget( widget=widget.TextInputWidget(
...@@ -59,6 +57,15 @@ class Login(colander.Schema): ...@@ -59,6 +57,15 @@ class Login(colander.Schema):
password = colander.SchemaNode( password = colander.SchemaNode(
colander.String(), widget=widget.PasswordWidget()) colander.String(), widget=widget.PasswordWidget())
# def after_bind(self, schema, kwargs):
# request = kwargs["request"]
# csrf_token = new_csrf_token(request)
# log.error(csrf_token)
# self["csrf_token"] = colander.SchemaNode(
# colander.String(), widget=widget.HiddenWidget(),
# default=csrf_token
# )
# http://deformdemo.repoze.org/interfield/ # http://deformdemo.repoze.org/interfield/
def login_validator(form, value): def login_validator(form, value):
...@@ -161,7 +168,7 @@ def oauth2_login(request, params=None): ...@@ -161,7 +168,7 @@ def oauth2_login(request, params=None):
class ViewLogin(BaseView): class ViewLogin(BaseView):
@view_config(route_name='login', renderer='templates/form.pt') @view_config(route_name='login', renderer='templates/form.pt', require_csrf=True)
def view_login(self): def view_login(self):
request = self.req request = self.req
request.session["login"] = True request.session["login"] = True
...@@ -174,7 +181,8 @@ class ViewLogin(BaseView): ...@@ -174,7 +181,8 @@ class ViewLogin(BaseView):
request.session.flash('Anda sudah login', 'error') request.session.flash('Anda sudah login', 'error')
return HTTPFound(location=get_urls(f"{request.route_url('home')}")) return HTTPFound(location=get_urls(f"{request.route_url('home')}"))
schema = Login(validator=login_validator) schema = Login()
schema = schema.bind(request=self.req)
form = Form(schema, buttons=('login',)) form = Form(schema, buttons=('login',))
message = "" message = ""
if 'login' in request.POST: if 'login' in request.POST:
...@@ -190,6 +198,7 @@ class ViewLogin(BaseView): ...@@ -190,6 +198,7 @@ class ViewLogin(BaseView):
return HTTPFound(location=get_urls(request.route_url('login'))) return HTTPFound(location=get_urls(request.route_url('login')))
values = dict(c) values = dict(c)
# start cek external module # start cek external module
pckgs = get_params('external-uim') pckgs = get_params('external-uim')
if user: if user:
...@@ -234,7 +243,7 @@ class ViewLogin(BaseView): ...@@ -234,7 +243,7 @@ class ViewLogin(BaseView):
request.session.flash(str(e), "error") request.session.flash(str(e), "error")
return render_to_response( return render_to_response(
login_tpl, dict( login_tpl, dict(
form=form.render(), form=form,
message=message, message=message,
url=get_urls(request.route_url('login')), url=get_urls(request.route_url('login')),
next_url=next_url, next_url=next_url,
...@@ -245,19 +254,19 @@ class ViewLogin(BaseView): ...@@ -245,19 +254,19 @@ class ViewLogin(BaseView):
return HTTPFound(location=get_urls(request.route_url('login'))) return HTTPFound(location=get_urls(request.route_url('login')))
if user and user.status == 1: if user and user.status == 1:
return redirect_login(request, user) return redirect_login(request, user)
# values = {"csrf_token": new_csrf_token(request)}
login = "" login = ""
if login_tpl == 'templates/login.pt': # if login_tpl == 'templates/login.pt':
return dict(form=form.render(), # return dict(form=form.render(),
message=message, # message=message,
url=get_urls(request.route_url('login')), # url=get_urls(request.route_url('login')),
next_url=next_url, # next_url=next_url,
login=login, ) # login=login, )
return render_to_response( return render_to_response(
renderer_name=login_tpl, renderer_name=login_tpl,
request=request, request=request,
value=dict(form=form.render(), value=dict(form=form,
message=message, message=message,
url=get_urls(request.route_url('login')), url=get_urls(request.route_url('login')),
next_url=next_url, next_url=next_url,
...@@ -290,7 +299,7 @@ btn_home = Button("home", css_class="btn-success") ...@@ -290,7 +299,7 @@ btn_home = Button("home", css_class="btn-success")
class Logout(BaseView): class Logout(BaseView):
@view_config(route_name='logout', renderer="templates/logout.pt") @view_config(route_name='logout', renderer="templates/logout.pt", require_csrf=False)
def view_logout(self): def view_logout(self):
request = self.req request = self.req
if not request.user: if not request.user:
...@@ -311,6 +320,7 @@ class Logout(BaseView): ...@@ -311,6 +320,7 @@ class Logout(BaseView):
if "g_state" in request.cookies: if "g_state" in request.cookies:
request.response.delete_cookie("g_state", '/') request.response.delete_cookie("g_state", '/')
form.set_appstruct({"message": "Sukses Logout"}) form.set_appstruct({"message": "Sukses Logout"})
request.session["login"] = False
return dict(form=form.render()) return dict(form=form.render())
...@@ -326,8 +336,9 @@ class ChangePassword(colander.Schema): ...@@ -326,8 +336,9 @@ class ChangePassword(colander.Schema):
def change_password_validator(form, value): def change_password_validator(form, value):
exc = colander.Invalid(form, '') pass
user = form.request.user # exc = colander.Invalid(form, '')
# user = form.request.user
# if not UserService.check_password(user, value["password"]): # if not UserService.check_password(user, value["password"]):
# exc["password"] = 'Login Failed' # exc["password"] = 'Login Failed'
# raise exc # raise exc
...@@ -449,7 +460,7 @@ def send_email_security_code( ...@@ -449,7 +460,7 @@ def send_email_security_code(
or 'mail.username' not in settings: or 'mail.username' not in settings:
return return
url = '{}password/{}?password={}'.format( url = '{}/password/{}?password={}'.format(
request.home, user.security_code, password) request.home, user.security_code, password)
minutes = int(time_remain.seconds / 60) minutes = int(time_remain.seconds / 60)
......
import colander
from pyramid.csrf import new_csrf_token, get_csrf_token
from opensipkd.base.views import widget
class CSRFSchema(colander.Schema):
def after_bind(self, schema, kwargs):
request = kwargs["request"]
csrf_token = get_csrf_token(request)
if not csrf_token:
csrf_token = new_csrf_token(request)
self["csrf_token"] = colander.SchemaNode(
colander.String(), widget=widget.HiddenWidget(),
default=csrf_token
)
import json import json
import logging import logging
from colander import SchemaNode, null, Mapping, Invalid, string_types from colander import SchemaNode, null, Mapping, Invalid # , string_types
from deform.widget import Widget, _StrippedString, Select2Widget, default_resources, \
ResourceRegistry, default_resource_registry
from deform.form import Button
from iso8601.iso8601 import ISO8601_REGEX
from deform.i18n import _
from colander import compat from colander import compat
from deform import widget from deform import widget
from deform.form import Button
from deform.i18n import _
from deform.widget import Widget, _StrippedString, Select2Widget, \
DateInputWidget as WidgetDateInputWidget
from deform.widget import string_types
from iso8601.iso8601 import ISO8601_REGEX
_logging = logging.getLogger(__name__) _logging = logging.getLogger(__name__)
...@@ -709,3 +711,7 @@ class TextInputWidget(widget.TextInputWidget): ...@@ -709,3 +711,7 @@ class TextInputWidget(widget.TextInputWidget):
if isinstance(self.button, compat.string_types): if isinstance(self.button, compat.string_types):
self.button = Button(self.button, type="button") self.button = Button(self.button, type="button")
class DateInputWidget(WidgetDateInputWidget):
type_name = "text"
...@@ -18,9 +18,11 @@ ...@@ -18,9 +18,11 @@
'${oid}', '${oid}',
function deform_cb(oid) { function deform_cb(oid) {
if (!Modernizr.inputtypes['date'] ||"${type_name}" != "date" || window.forceDateTimePolyfill){ if (!Modernizr.inputtypes['date'] ||"${type_name}" != "date" || window.forceDateTimePolyfill){
console.log(${options_json});
$('#' + oid).pickadate(${options_json}); $('#' + oid).pickadate(${options_json});
} }
} }
); );
</script> </script>
</div> </div>
<tal:block tal:define="oid oid|field.oid; <tal:block tal:define="oid oid|field.oid;
css_class css_class|field.widget.css_class; css_class css_class|field.widget.css_class;
style style|field.widget.style; style style|field.widget.style;
preview_url cstruct.get('preview_url')|cstruct.get('base64')|''; preview_url cstruct.get('preview_url')|'';
ext str(cstruct.get('filename').split('.')[-1:][0]).lower()|[]; ext str(cstruct.get('filename').split('.')[-1:][0]).lower()|[];
fname str(cstruct.get('filename'))|''; fname str(cstruct.get('filename'))|'';
delete cstruct.get('delete')|'';"> delete cstruct.get('delete')|'';
maxsize field.widget.size|5242880;">
${field.start_mapping()} ${field.start_mapping()}
<tal:block tal:condition="preview_url and ext in ['jpg','jpeg','png','bmp','gif']"> <img id="preview-${oid}" alt="" src="${preview_url}" style="width:100px;height:auto;display:block;"
<img id="preview-${oid}" alt="" src="${structure: preview_url}" style="width:100px;height:auto;" onload="window.URL.revokeObjectURL(this.src);"></img>
onload="window.URL.revokeObjectURL(this.src)"></img> <a id="label-${oid}" tal:condition="preview_url" class="label label-default" href="${preview_url}"
<br>
</tal:block>
<tal:block tal:condition="not preview_url or ext not in ['jpg','jpeg','png','bmp','gif']">
<img id="preview-${oid}" alt="" src="" style="width:100px;height:auto;"
onload="window.URL.revokeObjectURL(this.src)"></img>
<br>
</tal:block>
<a id="label-${oid}" tal:condition="preview_url" class="label label-default" href="${structure: preview_url}"
target="_blank"><i class="fa fa-search"></i> View</a> target="_blank"><i class="fa fa-search"></i> View</a>
<button id="labeldelete-${oid}" type="button" tal:condition="delete" class="label label-danger" href="#"
target="_blank"
onclick="var daft = document.getElementById('daftar_file_hapus').value.replace(/\[|\]/g,'').split(',');
daft.push('${fname}');
document.getElementById('daftar_file_hapus').value=daft;
document.getElementById('${oid}-close').click();">
<i class="fa fa-remove"></i> Delete</button>
<input type="file" name="upload" id="${oid}" <input type="file" name="upload" id="${oid}"
tal:attributes="style style; tal:attributes="style style;
accept accept|field.widget.accept; accept accept|field.widget.accept;
data-filename fname; data-filename fname;
attributes|field.widget.attributes|{};" attributes|field.widget.attributes|{};"/>
onchange="document.getElementById('preview-'+this.id).src = window.URL.createObjectURL(this.files[0]);
document.getElementById('labeldelete-'+this.id).remove();
document.getElementById('label-'+this.id).remove();"/>
<input tal:define="uid cstruct.get('uid')" <input tal:define="uid cstruct.get('uid')"
tal:condition="uid" tal:condition="uid"
type="hidden" name="uid" value="${uid}"/> type="hidden" name="uid" value="${uid}"/>
...@@ -41,6 +24,15 @@ ...@@ -41,6 +24,15 @@
deform.addCallback('${oid}', function (oid) { deform.addCallback('${oid}', function (oid) {
$('#' + oid).upload(); $('#' + oid).upload();
}); });
document.getElementById("${oid}").onchange = function() {
if(this.files[0].size > ${maxsize}){
alert("File is too big!");
this.value = "";
document.getElementById('preview-'+this.id).src = '';
}
document.getElementById('preview-'+this.id).src = window.URL.createObjectURL(this.files[0]);
document.getElementById('labeldelete-'+this.id).remove();
document.getElementById('label-'+this.id).remove();
};
</script> </script>
</tal:block> </tal:block>
<tal:block tal:define="name name|field.name; <tal:block tal:define="
oid oid|field.oid;"> name name|field.name;
<div class="input"> oid oid|field.oid;
">
<div class="input">
<input <input
type="password" type="password"
name="${name}" name="${name}"
onkeyup="checkPasswordStrength${oid}();"
value="${field.widget.redisplay and cstruct or ''}" value="${field.widget.redisplay and cstruct or ''}"
tal:attributes="style style|field.widget.style; tal:attributes="style style|field.widget.style;
class string: form-control ${css_class|field.widget.css_class or ''}; class string: form-control ${css_class|field.widget.css_class or ''};
attributes|field.widget.attributes|{};" attributes|field.widget.attributes|{};"
id="${oid}"/> id="${oid}"/>
<!--? onkeyup="checkPasswordStrength${oid}();"-->
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input type="checkbox" id="view${oid}"> <input type="checkbox" id="view${oid}">
...@@ -17,23 +20,23 @@ ...@@ -17,23 +20,23 @@
</label> </label>
</div> </div>
<div id="${oid}-password-strength-status"></div> <div id="${oid}-password-strength-status"></div>
</div> </div>
<style> <style>
#password-strength-status { #password-strength-status {
padding: 5px 10px; padding: 5px 10px;
border-radius: 4px; border-radius: 4px;
margin-top: 5px; margin-top: 5px;
}
</style>
<script type="text/javascript">
$('#view${oid}').change(function(){
if ($(this).prop('checked')==true){
$('#${oid}').attr('type','text');
} }
else { </style>
$('#${oid}').attr('type','password'); <script type="text/javascript">
$('#view${oid}').change(function () {
if ($(this).prop('checked') == true) {
$('#${oid}').attr('type', 'text');
} else {
$('#${oid}').attr('type', 'password');
} }
}); });
function checkPasswordStrength${oid}() { function checkPasswordStrength${oid}() {
var number = /([0-9])/; var number = /([0-9])/;
var alphabets = /([a-zA-Z])/; var alphabets = /([a-zA-Z])/;
...@@ -48,14 +51,13 @@ ...@@ -48,14 +51,13 @@
$('#${oid}-password-strength-status').removeClass(); $('#${oid}-password-strength-status').removeClass();
$('#${oid}-password-strength-status').addClass('label label-success'); $('#${oid}-password-strength-status').addClass('label label-success');
$('#${oid}-password-strength-status').html("Strong"); $('#${oid}-password-strength-status').html("Strong");
} } else {
else {
$('#${oid}-password-strength-status').removeClass(); $('#${oid}-password-strength-status').removeClass();
$('#${oid}-password-strength-status').addClass('label label-warning'); $('#${oid}-password-strength-status').addClass('label label-warning');
$('#${oid}-password-strength-status').html("Medium (should include alphabets, numbers and special characters.)"); $('#${oid}-password-strength-status').html("Medium (should include alphabets, numbers and special characters.)");
} }
} }
} }
</script> </script>
</tal:block> </tal:block>
...@@ -99,7 +99,6 @@ ...@@ -99,7 +99,6 @@
<input type="hidden" name="__end__" value="${field.name}:sequence"/> <input type="hidden" name="__end__" value="${field.name}:sequence"/>
<!-- /sequence --> <!-- /sequence -->
<input tal:define="templates subfields[0][1].widget.template|''" tal:condition="templates=='file_upload'" id="daftar_file_hapus" name="daftar_file_hapus" type="hidden">
</div> </div>
</div> </div>
......
...@@ -12,8 +12,8 @@ from sqlalchemy import inspect as sa_inspect ...@@ -12,8 +12,8 @@ from sqlalchemy import inspect as sa_inspect
class MySession(Session): class MySession(Session):
def execute(self, clause, params=None, mapper=None, **kw): def execute(self, clause, params=None, mapper=None, **kw):
# Your magic with clause here # Your magic with clause here
# print("Session:", clause, params, mapper, kw) print("Session:", clause, params, mapper, kw)
return Session.execute(self, clause, params, mapper) return Session.execute(self, clause, params) #, mapper
session_factory = sessionmaker(class_=MySession) session_factory = sessionmaker(class_=MySession)
......
from sqlalchemy import Column, String, SmallInteger, Integer, DateTime, ForeignKey from sqlalchemy import Column, String, SmallInteger, Integer, DateTime, ForeignKey
from sqlalchemy.orm import relationship, backref from sqlalchemy.orm import relationship, backref
from .users import User
from .meta import Base
from .base import NamaModel, DefaultModel, DBSession, KodeModel from .base import NamaModel, DefaultModel, DBSession, KodeModel
from .meta import Base
from .partner import Partner from .partner import Partner
from .users import User
class Route(Base, NamaModel): class Route(Base, NamaModel):
...@@ -42,7 +42,14 @@ class UserDeviceModel(Base, KodeModel): ...@@ -42,7 +42,14 @@ class UserDeviceModel(Base, KodeModel):
expired = Column(DateTime(timezone=True)) expired = Column(DateTime(timezone=True))
user = relationship(User, backref=backref("devices")) user = relationship(User, backref=backref("devices"))
class ResCompany(Base, NamaModel): class ResCompany(Base, NamaModel):
__tablename__ = 'company' __tablename__ = 'company'
id = Column(Integer, primary_key=True)
partner_id = Column(Integer, ForeignKey(Partner.id)) partner_id = Column(Integer, ForeignKey(Partner.id))
partner = relationship(Partner, backref=backref("company")) partner = relationship("Partner", backref=backref("company"))
parent_id = Column(Integer, ForeignKey("company.id"))
children = relationship("ResCompany")
parent = relationship(
"ResCompany", remote_side=[id], primaryjoin="ResCompany.parent_id==ResCompany.id"
)
...@@ -10,6 +10,7 @@ from sqlalchemy.orm import ( ...@@ -10,6 +10,7 @@ from sqlalchemy.orm import (
backref backref
) )
from . import ResCompany
from ..models import DBSession, Base from ..models import DBSession, Base
from ..models import (NamaModel, from ..models import (NamaModel,
TABLE_ARGS) TABLE_ARGS)
...@@ -26,7 +27,7 @@ class Departemen(Base, NamaModel): ...@@ -26,7 +27,7 @@ class Departemen(Base, NamaModel):
level_id = Column(SmallInteger) level_id = Column(SmallInteger)
children = relationship( children = relationship(
"Departemen", backref=backref('parent', remote_side=[id])) "Departemen", backref=backref('parent', remote_side=[id]))
company_id = Column(Integer) company_id = Column(Integer, ForeignKey(ResCompany.id))
def get_parents(self, start=False): def get_parents(self, start=False):
allparents = [] allparents = []
......
...@@ -16,7 +16,6 @@ psycopg2-binary ...@@ -16,7 +16,6 @@ psycopg2-binary
alembic>=0.3.4 alembic>=0.3.4
pytz pytz
sqlalchemy-datatables sqlalchemy-datatables
z3c.rml
py3o.template py3o.template
wheezy.captcha wheezy.captcha
google-api-python-client google-api-python-client
......
...@@ -10,7 +10,9 @@ with open(os.path.join(here, 'CHANGES.txt')) as f: ...@@ -10,7 +10,9 @@ with open(os.path.join(here, 'CHANGES.txt')) as f:
line = CHANGES.splitlines()[0] line = CHANGES.splitlines()[0]
version = line.split()[0] version = line.split()[0]
requires = [ requires = [
'sqlalchemy==1.4.50',
'wheel', 'wheel',
'colander==1.8.3',
'pyramid', 'pyramid',
'pyramid_tm', 'pyramid_tm',
'SQLAlchemy', 'SQLAlchemy',
...@@ -21,7 +23,7 @@ requires = [ ...@@ -21,7 +23,7 @@ requires = [
'ziggurat-foundations', 'ziggurat-foundations',
'zope.sqlalchemy', 'zope.sqlalchemy',
'pytz', 'pytz',
'deform >= 2.0a2', 'deform',
'psycopg2-binary', 'psycopg2-binary',
'pyramid_chameleon', 'pyramid_chameleon',
'pyramid_rpc', 'pyramid_rpc',
...@@ -33,8 +35,8 @@ requires = [ ...@@ -33,8 +35,8 @@ requires = [
'google-api-python-client', 'google-api-python-client',
'google', 'google',
'pyjwt', 'pyjwt',
'z3c.rml', # 'z3c.rml',
'opensipkd-tools @git+https://git.opensipkd.com/aa.gusti/opensipkd-tools.git', # 'opensipkd-tools @git+https://git.opensipkd.com/aa.gusti/opensipkd-tools.git',
] ]
dev_requires = [ dev_requires = [
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!