Enhance form handling and response structure in BaseView and user login flow

1 parent c69c7bef
...@@ -20,6 +20,7 @@ from opensipkd.tools.buttons import btn_save, btn_cancel, btn_close, btn_delete, ...@@ -20,6 +20,7 @@ from opensipkd.tools.buttons import btn_save, btn_cancel, btn_close, btn_delete,
btn_pdf, btn_unpost, btn_post btn_pdf, btn_unpost, btn_post
from opensipkd.tools.captcha import get_captcha from opensipkd.tools.captcha import get_captcha
from opensipkd.tools.report import csv_response, file_response from opensipkd.tools.report import csv_response, file_response
from pyramid.request import Response
from .common import DataTables from .common import DataTables
from .. import DBSession, get_params, get_urls from .. import DBSession, get_params, get_urls
from ..scripts.initializedb import append_csv from ..scripts.initializedb import append_csv
...@@ -199,7 +200,19 @@ class BaseView(object): ...@@ -199,7 +200,19 @@ class BaseView(object):
'jenis'] or self.jenis 'jenis'] or self.jenis
self.ses['jenis'] = self.jenis self.ses['jenis'] = self.jenis
def form2dict(self, field):
children = []
for c in field.children:
children.append(self.form2dict(c))
d = {
"id": field.oid,
"name": field.name,
"error" : {"msg": field.error and field.error.msg or ""},
"children":children
}
return d
def query_register(self, **kwargs): def query_register(self, **kwargs):
pass pass
...@@ -546,6 +559,10 @@ class BaseView(object): ...@@ -546,6 +559,10 @@ class BaseView(object):
return self.route_list(**kwargs) return self.route_list(**kwargs)
def returned_form(self, form, table=None, **kwargs): def returned_form(self, form, table=None, **kwargs):
if self.req.is_xhr:
d = self.form2dict(form.field)
return Response(json=d)
resources = form.get_widget_resources() resources = form.get_widget_resources()
readonly = "readonly" in kwargs and kwargs["readonly"] or False readonly = "readonly" in kwargs and kwargs["readonly"] or False
kwargs["readonly"] = readonly kwargs["readonly"] = readonly
...@@ -556,18 +573,21 @@ class BaseView(object): ...@@ -556,18 +573,21 @@ class BaseView(object):
table = table["form"] table = table["form"]
# resources["js"] = list(resources["js"]) # resources["js"] = list(resources["js"])
# resources["css"] = list(resources["css"]) # resources["css"] = list(resources["css"])
if is_object: if not is_object:
return dict(form=form, form = form.render(readonly=readonly)
table=table and table.render() or None,
scripts=self.form_scripts, # return dict(form=form,
css=resources["css"], # table=table and table.render() or None,
js=resources["js"], # scripts=self.form_scripts,
**kwargs # css=resources["css"],
) # js=resources["js"],
# **kwargs
return dict(form=form.render(readonly=readonly), # )
return dict(form=form,
table=table and table.render() or None, table=table and table.render() or None,
scripts=self.form_scripts, css=resources["css"], scripts=self.form_scripts,
css=resources["css"],
js=resources["js"], js=resources["js"],
**kwargs **kwargs
) )
......
...@@ -312,7 +312,7 @@ class Registrasi(BaseView): ...@@ -312,7 +312,7 @@ class Registrasi(BaseView):
forget(self.req) forget(self.req)
self.ses.delete() self.ses.delete()
@view_config(route_name='register', renderer='templates/form.pt') # @view_config(route_name='register', renderer='templates/form.pt')
def view_register(self): def view_register(self):
self.bindings = dict(user=None) self.bindings = dict(user=None)
request = self.req request = self.req
......
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" <html lang="en" tal:define="home request._host;">
tal:define="home request._host;">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
<!-- SmartAdmin Styles : Caution! DO NOT change the order --> <!-- SmartAdmin Styles : Caution! DO NOT change the order -->
<link rel="stylesheet" type="text/css" media="screen" <link rel="stylesheet" type="text/css" media="screen"
href="${home}/static/v3/css/smartadmin-production-plugins.min.css"> href="${home}/static/v3/css/smartadmin-production-plugins.min.css">
<link rel="stylesheet" type="text/css" media="screen" href="${home}/static/v3/css/smartadmin-production.min.css"> <link rel="stylesheet" type="text/css" media="screen" href="${home}/static/v3/css/smartadmin-production.min.css">
<link rel="stylesheet" type="text/css" media="screen" href="${home}/static/v3/css/smartadmin-skins.min.css"> <link rel="stylesheet" type="text/css" media="screen" href="${home}/static/v3/css/smartadmin-skins.min.css">
...@@ -31,181 +31,178 @@ ...@@ -31,181 +31,178 @@
</head> </head>
<body> <body>
<div id="content" class="container"> <div id="content" class="container">
<div class="row"> <div class="row">
<div class="col-xs-12 col-sm-12 col-md-6 col-md-offset-3 col-lg-4 col-lg-offset-4" style="margin-top:50px"> <div class="col-xs-12 col-sm-12 col-md-6 col-md-offset-3 col-lg-4 col-lg-offset-4" style="margin-top:50px">
<div class="well no-padding"> <div class="well no-padding">
<form id="deform" method="POST" enctype="multipart/form-data" accept-charset="utf-8" <form id="deform" method="POST" enctype="multipart/form-data" accept-charset="utf-8"
class="smart-form client-form panel form-signin" style="border:0px;"> class="smart-form client-form panel form-signin" style="border:0px;">
<header class="bg-color-blue"> <header class="bg-color-blue">
<h1 class="txt-color-white login-header-big" align="center" <h1 class="txt-color-white login-header-big" align="center" style="letter-spacing:1px;">
style="letter-spacing:1px;">${request.app_name}</h1> ${request.app_name}</h1>
</header> </header>
<fieldset class="deformFormFieldset"> <fieldset class="deformFormFieldset">
<input type="hidden" name="_charset_"/> <input type="hidden" name="_charset_" />
<input type="hidden" name="__formid__" value="deform"/> <input type="hidden" name="__formid__" value="deform" />
<div tal:condition="request.session.peek_flash()"> <div tal:condition="request.session.peek_flash()">
<div class="alert alert-success" tal:repeat="message request.session.pop_flash()"><i <div class="alert alert-success" tal:repeat="message request.session.pop_flash()"><i
class="fa fa-fw fa-lg fa-check-circle"></i>&nbsp;${message} class="fa fa-fw fa-lg fa-check-circle"></i>&nbsp;${message}
</div> </div>
</div>
<div tal:condition="request.session.peek_flash('error')">
<div class="alert alert-danger" tal:repeat="message request.session.pop_flash('error')"><i
class="fa fa-fw fa-lg fa-times-circle"></i>&nbsp;${message}
</div> </div>
</div> <div tal:condition="request.session.peek_flash('error')">
<div class="alert alert-danger" tal:repeat="message request.session.pop_flash('error')"><i
<div class="col-md-12" align="center"> class="fa fa-fw fa-lg fa-times-circle"></i>&nbsp;${message}
<img src="${home}/static/img/logo.png"
class="img-float img-thumbnail" style="height:auto;width:auto;border:none;"/>
</div>
<div class="clearfix"></div>
<section>
<label class="label">USERNAME</label>
<label class="input"> <i class="icon-append fa fa-user"></i>
<input id="username" type="text" name="username" class="form-control">
<b class="tooltip tooltip-top-right">
<i class="fa fa-user txt-color-teal"></i>
ISI DENGAN USERNAME ANDA</b></label>
</section>
<section>
<label class="label">PASSWORD</label>
<label class="input"> <i class="icon-append fa fa-lock"></i>
<input id="password" type="password" name="password" class="form-control">
<b class="tooltip tooltip-top-right"><i class="fa fa-lock txt-color-teal"></i> ISI
DENGAN PASSWORD ANDA</b> </label>
</section>
<section>
<div class="form-group">
<label class=" checkbox checkbox-inline">
<input id="show_password" type="checkbox" style="left: 20px;">Show Password</label>
<div class="note">
<a href="${home}/reset-password" id="lupa" >Lupa Password?</a>
</div> </div>
</div> </div>
</section> <div class="col-md-12" align="center">
<img src="${home}/static/img/logo.png" class="img-float img-thumbnail"
style="height:auto;width:auto;border:none;" />
</div>
<div class="clearfix"></div>
<section>
<label class="label">USERNAME</label>
<label class="input"> <i class="icon-append fa fa-user"></i>
<input id="username" type="text" name="username" class="form-control">
<b class="tooltip tooltip-top-right">
<i class="fa fa-user txt-color-teal"></i>
ISI DENGAN USERNAME ANDA</b></label>
</section>
<section>
<label class="label">PASSWORD</label>
<label class="input"> <i class="icon-append fa fa-lock"></i>
<input id="password" type="password" name="password" class="form-control">
<b class="tooltip tooltip-top-right"><i class="fa fa-lock txt-color-teal"></i> ISI
DENGAN PASSWORD ANDA</b> </label>
</section>
<section>
<div class="form-group">
<label class=" checkbox checkbox-inline">
<input id="show_password" type="checkbox" style="left: 20px;">Show Password</label>
<div class="note">
<a href="${home}/reset-password" id="lupa">Lupa Password?</a>
</div>
</div>
</section>
<section> <section>
<div tal:condition="'csrf_token' in form"> <div tal:condition="'csrf_token' in form">
<div tal:define="field form['csrf_token']" style="display: none;"> <div tal:define="field form['csrf_token']" style="display: none;">
${structure:field.serialize()} ${structure:field.serialize()}
</div>
</div> </div>
</div> </section>
</section>
</fieldset>
</fieldset> <footer>
<footer> <section>
<section> <div class="row" style="float:right">
<div class="row" style="float:right"> <button type="submit" id="login-btn" name="login" class="btn btn-primary" style="float:left"
<button type="submit" id="login-btn" name="login" value="Login">
class="btn btn-primary" style="float:left" Login
value="Login"> </button>
Login <button tal:condition="allow_register(request)" id="register" name="register" class="btn btn-info"
</button>
<button tal:condition="allow_register(request)"
id="register" name="register" class="btn btn-info"
value="Register" style="float:left"> value="Register" style="float:left">
Register Register
</button> </button>
<!--? <div class="clearfix"></div>--> <!--? <div class="clearfix"></div>-->
</div>
</section>
</footer>
<footer>
<section>
<div class="row" tal:condition="request.google_signin_client_id and allow_register(request)">
<div id="g_id_onload"
data-client_id="${request.google_signin_client_id}"
data-callback="onSignIn">
</div> </div>
<div class="g_id_signin" data-type="standard"></div> </section>
</div> </footer>
</section> <footer>
<section> <section>
<input id="provider_name" type="hidden" name="provider_name" class="form-control"> <div class="row" tal:condition="request.google_signin_client_id and allow_register(request)">
<input id="id_token" type="hidden" name="id_token" class="form-control"> <div id="g_id_onload" data-client_id="${request.google_signin_client_id}" data-callback="onSignIn">
</section> </div>
</footer> <div class="g_id_signin" data-type="standard"></div>
</div>
</section>
<section>
<input id="provider_name" type="hidden" name="provider_name" class="form-control">
<input id="id_token" type="hidden" name="id_token" class="form-control">
</section>
</footer>
</form> </form>
</div>
</div> </div>
</div> </div>
</div> </div>
</div>
<!-- Bootstrap core JavaScript <!-- Bootstrap core JavaScript
================================================== --> ================================================== -->
<!-- Placed at the end of the document so the pages load faster --> <!-- Placed at the end of the document so the pages load faster -->
<script type="text/javascript" src="${home}/deform_static/scripts/jquery-2.0.3.min.js"></script> <script type="text/javascript" src="${home}/deform_static/scripts/jquery-2.0.3.min.js"></script>
<script type="text/javascript" src="${home}/deform_static/scripts/bootstrap.min.js"></script> <script type="text/javascript" src="${home}/deform_static/scripts/bootstrap.min.js"></script>
<script type="text/javascript" src="${home}/deform_static/scripts/deform.js"></script> <script type="text/javascript" src="${home}/deform_static/scripts/deform.js"></script>
<!--? <script tal:condition="request.google_signin_client_id"--> <!--? <script tal:condition="request.google_signin_client_id"-->
<!--? src="https://apis.google.com/js/platform.js" async defer></script>--> <!--? src="https://apis.google.com/js/platform.js" async defer></script>-->
<script tal:condition="request.google_signin_client_id" <script tal:condition="request.google_signin_client_id" src="https://accounts.google.com/gsi/client" async
src="https://accounts.google.com/gsi/client" async defer></script> defer></script>
<script tal:condition="request.google_signin_client_id"> <script tal:condition="request.google_signin_client_id">
window.onload = function (e) { window.onload = function (e) {
const value = document.cookie; const value = document.cookie;
const parts = value.split(`g_state=`); const parts = value.split(`g_state=`);
if (parts.length === 2) { if (parts.length === 2) {
document.cookie = document.cookie + ";max-age=0"; document.cookie = document.cookie + ";max-age=0";
}
}
function onSignIn(googleUser) {
// var profile = googleUser.getBasicProfile();
// console.log('ID: ' + profile.getId()); // Do not send to your backend! Use an ID token instead.
// console.log('Name: ' + profile.getName());
// console.log('Image URL: ' + profile.getImageUrl());
// console.log('Email: ' + profile.getEmail()); // This is null if the 'email' scope is not present.
//getId(), getName(), getGivenName(), getFamilyName(), getImageUrl(), getEmail() methods, and
// console.log(googleUser);
// console.log(googleUser.getId());
// console.log(googleUser.getName());
// var id_token = googleUser.getAuthResponse().id_token;
document.getElementById('provider_name').value = "google";
document.getElementById('id_token').value = JSON.stringify(googleUser);
document.getElementById("deform").submit();
// var xhr = new XMLHttpRequest();
// xhr.open('POST', '/googlesignin');
// xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
// xhr.onload = function() {
// console.log('Signed in as: ' + xhr.responseText);
// };
// xhr.send('idtoken=' + id_token);
}
$(document).ready(function () {
$("form#deform").keypress(function (event) {
var keycode = (event.keyCode ? event.keyCode : event.which);
if (keycode == '13') {
event.preventDefault();
$("button#login-btn").click();
} }
}
function onSignIn(googleUser) {
// var profile = googleUser.getBasicProfile();
// console.log('ID: ' + profile.getId()); // Do not send to your backend! Use an ID token instead.
// console.log('Name: ' + profile.getName());
// console.log('Image URL: ' + profile.getImageUrl());
// console.log('Email: ' + profile.getEmail()); // This is null if the 'email' scope is not present.
//getId(), getName(), getGivenName(), getFamilyName(), getImageUrl(), getEmail() methods, and
// console.log(googleUser);
// console.log(googleUser.getId());
// console.log(googleUser.getName());
// var id_token = googleUser.getAuthResponse().id_token;
document.getElementById('provider_name').value = "google";
document.getElementById('id_token').value = JSON.stringify(googleUser);
document.getElementById("deform").submit();
// var xhr = new XMLHttpRequest();
// xhr.open('POST', '/googlesignin');
// xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
// xhr.onload = function() {
// console.log('Signed in as: ' + xhr.responseText);
// };
// xhr.send('idtoken=' + id_token);
}
$(document).ready(function () {
$("form#deform").keypress(function (event) {
var keycode = (event.keyCode ? event.keyCode : event.which);
if (keycode == '13') {
event.preventDefault();
$("button#login-btn").click();
}
});
}); });
}); </script>
</script> <script>
<script> $(document).ready(function () {
$(document).ready(function () { $("#show_password").on("click", function () {
$("#show_password").on("click", function () { var x = $("#password");
var x = $("#password"); if (x.attr("type") === "password") {
if (x.attr("type") === "password") { x.attr("type", "text");
x.attr("type","text"); } else {
} else { x.attr("type", "password");
x.attr("type","password"); }
} });
}); });
}); </script>
</script>
</body> </body>
</html> </html>
\ No newline at end of file \ No newline at end of file
<html metal:use-macro="load: ./base3.1.pt" tal:define="
scripts scripts|scripts;
readonly readonly|readonly;">
<div metal:fill-slot="content">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"><i class="fa fa-fw fa-plus"></i>&nbsp;${request.title}</h3>
</div>
<div class="panel-body">
<div tal:content="structure form.render(readonly=readonly)"></div>
</div>
</div>
</div>
<div metal:fill-slot="scripts">
<script>
$(document).ready(function () {
// $(".read-only").attr("readonly", true);
$(".readonly").attr("readonly", true);
$(".date").attr("readonly", true);
// $(".date").datepicker({
// format: 'dd-mm-yyyy'
// });
${ structure: scripts }
});
</script>
<div metal:define-slot="scripts"></div>
</div>
</html>
\ No newline at end of file \ No newline at end of file
...@@ -23,7 +23,7 @@ import os ...@@ -23,7 +23,7 @@ import os
import re import re
from datetime import timedelta, datetime from datetime import timedelta, datetime
from importlib import import_module from importlib import import_module
from pyramid.request import Response
import colander import colander
from deform import widget, Form, ValidationFailure, Button from deform import widget, Form, ValidationFailure, Button
from pyramid.csrf import new_csrf_token from pyramid.csrf import new_csrf_token
...@@ -196,7 +196,12 @@ class ViewLogin(BaseView): ...@@ -196,7 +196,12 @@ class ViewLogin(BaseView):
except ValidationFailure as e: except ValidationFailure as e:
msg = 'Login gagal' msg = 'Login gagal'
set_user_log(msg, request, log, identity) set_user_log(msg, request, log, identity)
if self.req.is_xhr:
d = self.form2dict(form.field)
return Response(json=d)
request.session.flash(msg, 'error') request.session.flash(msg, 'error')
return HTTPFound(location=get_urls(request.route_url('login'))) return HTTPFound(location=get_urls(request.route_url('login')))
values = dict(c) values = dict(c)
......
<form <form tal:define="style style|field.widget.style;
tal:define="style style|field.widget.style;
css_class css_class|string:${field.widget.css_class or field.css_class or ''}; css_class css_class|string:${field.widget.css_class or field.css_class or ''};
item_template item_template|field.widget.item_template; item_template item_template|field.widget.item_template;
autocomplete autocomplete|field.autocomplete; autocomplete autocomplete|field.autocomplete;
...@@ -11,62 +10,45 @@ ...@@ -11,62 +10,45 @@
ajax_options ajax_options|field.ajax_options; ajax_options ajax_options|field.ajax_options;
formid formid|field.formid; formid formid|field.formid;
action action|field.action or None; action action|field.action or None;
method method|field.method;" method method|field.method;" tal:attributes="autocomplete autocomplete;
tal:attributes="autocomplete autocomplete;
style style; style style;
class css_class; class css_class;
action action; action action;
attributes|field.widget.attributes|{};" attributes|field.widget.attributes|{};" id="${formid}" method="${method}" enctype="multipart/form-data"
id="${formid}" accept-charset="utf-8" class="deform ${field.bootstrap_form_style | 'form-horizontal'}" i18n:domain="deform">
method="${method}"
enctype="multipart/form-data"
accept-charset="utf-8"
class="deform ${field.bootstrap_form_style | 'form-horizontal'}"
i18n:domain="deform"
>
<fieldset class="deform-form-fieldset"> <fieldset class="deform-form-fieldset">
<div class="row"> <div class="row">
<legend tal:condition="title">${title}</legend> <legend tal:condition="title">${title}</legend>
<input type="hidden" name="_charset_"/> <input type="hidden" name="_charset_" />
<input type="hidden" name="__formid__" value="${formid}"/> <input type="hidden" name="__formid__" value="${formid}" />
<div class="alert alert-danger" tal:condition="field.error"> <div class="alert alert-danger" tal:condition="field.error">
<div class="error-msg-lbl" i18n:translate="">There was a problem with your submission</div> <div class="error-msg-lbl" i18n:translate="">There was a problem with your submission</div>
<div class="error-msg-detail" i18n:translate="">Errors have been highlighted below</div> <div class="error-msg-detail" i18n:translate="">Errors have been highlighted below</div>
<p class="error-msg">${field.errormsg}</p> <p class="error-msg">${field.errormsg}</p>
</div> </div>
<p class="section first" tal:condition="description"> <p class="section first" tal:condition="description">
${description} ${description}
</p> </p>
<div tal:repeat="child field" <div tal:repeat="child field" tal:replace="structure child.render_template(item_template)" />
tal:replace="structure child.render_template(item_template)"/>
</div> </div>
<div class="row"> <div class="row">
<div class="form-group deform-form-buttons"> <div class="form-group deform-form-buttons">
<tal:loop tal:repeat="button buttons"> <tal:loop tal:repeat="button buttons">
<button <button tal:define="btn_disposition repeat.button.start and 'btn-primary' or 'btn-default';"
tal:define="btn_disposition repeat.button.start and 'btn-primary' or 'btn-default';" tal:attributes="disabled button.disabled if button.disabled else None;
tal:attributes="disabled button.disabled if button.disabled else None; attributes|button.attributes|{};" id="${formid+button.name}" name="${button.name}"
attributes|button.attributes|{};" type="${button.type}" class="btn ${button.css_class or btn_disposition}" value="${button.value}"
id="${formid+button.name}" tal:condition="button.type != 'link'">
name="${button.name}"
type="${button.type}"
class="btn ${button.css_class or btn_disposition}"
value="${button.value}"
tal:condition="button.type != 'link'">
<span tal:condition="button.icon" class="glyphicon glyphicon-${button.icon}"></span> <span tal:condition="button.icon" class="glyphicon glyphicon-${button.icon}"></span>
${button.title} ${button.title}
</button> </button>
<a <a tal:define="btn_disposition repeat.button.start and 'btn-primary' or 'btn-default';
tal:define="btn_disposition repeat.button.start and 'btn-primary' or 'btn-default'; btn_href button.value|''" class="btn ${button.css_class or btn_disposition}"
btn_href button.value|''" id="${field.formid + button.name}" href="${btn_href}" tal:condition="button.type == 'link'">
class="btn ${button.css_class or btn_disposition}"
id="${field.formid + button.name}"
href="${btn_href}"
tal:condition="button.type == 'link'">
<span tal:condition="button.icon" class="glyphicon glyphicon-${button.icon}"></span> <span tal:condition="button.icon" class="glyphicon glyphicon-${button.icon}"></span>
${button.title} ${button.title}
</a> </a>
</tal:loop> </tal:loop>
</div> </div>
...@@ -75,34 +57,38 @@ ...@@ -75,34 +57,38 @@
<script type="text/javascript" tal:condition="use_ajax"> <script type="text/javascript" tal:condition="use_ajax">
deform.addCallback( deform.addCallback(
'${formid}', '${formid}',
function (oid) { function (oid) {
var target = '#' + oid; var target = '#' + oid;
var options = { var options = {
target: target, // target: target,
replaceTarget: true, // replaceTarget: true,
success: function () { replaceTarget: false,
deform.processCallbacks(); success: function () {
deform.focusFirstInput(target); deform.processCallbacks();
}, deform.focusFirstInput(target);
beforeSerialize: function () { },
// See http://bit.ly/1agBs9Z (hack to fix tinymce-related ajax bug) beforeSerialize: function () {
if ('tinymce' in window) { // See http://bit.ly/1agBs9Z (hack to fix tinymce-related ajax bug)
$(tinymce.get()).each( if ('tinymce' in window) {
function (i, el) { $(tinymce.get()).each(
var content = el.getContent(); function (i, el) {
var editor_input = document.getElementById(el.id); var content = el.getContent();
editor_input.value = content; var editor_input = document.getElementById(el.id);
}); editor_input.value = content;
} });
} }
};
var extra_options = ${ajax_options} ||
{
} }
; };
$('#' + oid).ajaxForm($.extend(options, extra_options)); var extra_options = ${ ajax_options }|| {};
} $('#' + oid).ajaxForm($.extend(options, extra_options));
// $('#' + oid).ajaxForm(
// function () {
// alert("test");
// }
// );
}
); );
</script> </script>
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!