Commit e63eced9 by Owo Sugiana

Tambah /theme1/user/add yang lebih ringkas

1 parent 0459a542
0.2.1 2022-04-11
----------------
- Tambah route /theme1/user/add untuk form yang lebih ringkas.
0.2 2022-04-09
--------------
- Form User menggunakan ColanderAlchemy
......
......@@ -33,7 +33,8 @@ requires = [
'pyramid_beaker',
'pyramid_mailer',
'sqlalchemy-datatables',
'ColanderAlchemy']
'ColanderAlchemy',
'yaml']
tests_require = [
'WebTest >= 1.3.1', # py3 compat
......
......@@ -8,8 +8,6 @@ reset-password
reset-password-sent
login-by-code-failed
user
user-grid,/user/grid
user-act,/user/{act}/act
user-add,/user/add
user-edit,/user/{id}
user-delete,/user/{id}/delete
......@@ -18,3 +16,7 @@ group-add,/group/add
group-edit,/group/{id}
group-delete,/group/{id}/delete
api
user-act,/user/{act}/act
theme1-user,/theme1/user
theme1-user-add,/theme1/user/add
theme1-user-edit,/theme1/user/{id}
import os
import yaml
import colander
from deform import field
from deform.widget import MappingWidget
class PanelWidget(MappingWidget):
# templates/user/panel.pt
template = 'panel'
class Panel(field.Field):
widget = PanelWidget()
here = os.path.abspath(os.path.dirname(__file__))
my_templates = os.path.join(here, 'templates', 'user')
search_path = [my_templates]
Panel.set_zpt_renderer(search_path)
here = os.path.abspath(os.path.dirname(__file__))
schema = colander.Schema()
def get_widgets(form, fields, level=0):
r = []
for field_id in fields:
if field_id.find('Group') == 0:
field = get_widgets(form, fields[field_id], level+1)
else:
# templates/user/mapping_item.pt
form[field_id].my_level = level
field = form[field_id].render_template('mapping_item')
r.append(field)
return '\n'.join(r)
def render(form, yml_file):
for button in form.buttons:
if isinstance(button, tuple):
button = button[0]
yml_file = os.path.join(here, yml_file)
with open(yml_file) as f:
y = yaml.safe_load(f)
panels = []
for title in y['Panels']:
panel = Panel(schema)
panel.title = title
fields = y['Panels'][title]
panel.my_widgets = get_widgets(form, fields)
panels.append(panel.render())
return '\n'.join(panels)
<div metal:use-macro="load: ../../../templates/layout-menu.pt">
<div metal:fill-slot="content" i18n:domain="user">
<h1 i18n:translate="Add user">Add user</h1>
<div tal:content="structure form"/>
</div>
</div>
<div metal:use-macro="load: ../../../templates/layout-menu.pt">
<div metal:fill-slot="content" i18n:domain="user">
<h1 i18n:translate="Edit user">Edit user</h1>
<div tal:content="structure form"/>
</div>
</div>
%YAML 1.2
---
Panels:
No Title:
user_name: User Name
email: Email
groups: Grup
Bio Data:
tempat_lahir: Tempat Lahir
tgl_lahir: Tanggal Lahir
Alamat:
Group 1:
alamat: Alamat
Group:
rt: RT
rw: RW
Group 2:
kelurahan: Kelurahan
kecamatan: Kecamatan
kabupaten: Kota / Kabupaten
provinsi: Provinsi
<form
tal:define="style style|field.widget.style;
css_class css_class|string:${field.widget.css_class or field.css_class or ''};
item_template item_template|field.widget.item_template;
autocomplete autocomplete|field.autocomplete;
title title|field.title;
errormsg errormsg|field.errormsg;
description description|field.description;
buttons buttons|field.buttons;
use_ajax use_ajax|field.use_ajax;
ajax_options ajax_options|field.ajax_options;
formid formid|field.formid;
action action|field.action or None;
method method|field.method;"
tal:attributes="autocomplete autocomplete;
style style;
class css_class;
action action;
attributes|field.widget.attributes|{};"
id="${formid}"
method="${method}"
enctype="multipart/form-data"
accept-charset="utf-8"
i18n:domain="deform">
<!-- fieldset class="deform-form-fieldset" -->
<legend tal:condition="title">${title}</legend>
<input type="hidden" name="_charset_"/>
<input type="hidden" name="__formid__" value="${formid}"/>
<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-detail" i18n:translate="">
Errors have been highlighted below
</div>
<p class="error-msg">${field.errormsg}</p>
</div>
<p class="section first" tal:condition="description">
${description}
</p>
<div tal:replace="structure field.my_widgets"/>
<div class="form-group deform-form-buttons">
<tal:loop tal:repeat="button buttons">
<button
tal:define="btn_disposition repeat.button.start and 'btn-primary' or 'btn-default';"
tal:attributes="disabled button.disabled if button.disabled else None;
attributes|button.attributes|{};"
id="${formid+button.name}"
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>
${button.title}
</button>
<a
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}"
id="${field.formid + button.name}"
href="${btn_href}"
tal:condition="button.type == 'link'">
<span tal:condition="button.icon" class="glyphicon glyphicon-${button.icon}"></span>
${button.title}
</a>
</tal:loop>
</div>
<!-- /fieldset -->
<script type="text/javascript" tal:condition="use_ajax">
deform.addCallback(
'${formid}',
function(oid) {
var target = '#' + oid;
var options = {
target: target,
replaceTarget: true,
success: function() {
deform.processCallbacks();
deform.focusFirstInput(target);
},
beforeSerialize: function() {
// See http://bit.ly/1agBs9Z (hack to fix tinymce-related ajax bug)
if ('tinymce' in window) {
$(tinymce.get()).each(
function(i, el) {
var content = el.getContent();
var editor_input = document.getElementById(el.id);
editor_input.value = content;
});
}
}
};
var extra_options = ${ajax_options} || {};
$('#' + oid).ajaxForm($.extend(options, extra_options));
}
);
</script>
</form>
<div metal:use-macro="load: ../layout-menu.pt">
<div metal:use-macro="load: ../../../templates/layout-menu.pt">
<div metal:fill-slot="head">
<link rel="stylesheet" type="text/css" href="/static/datatables.min.css"/>
</div>
......
<div class="${field.my_level < 2 and 'col-md-6' or 'col-md-2'}">
<div tal:define="error_class error_class|field.widget.error_class;
description description|field.description;
title title|field.title;
oid oid|field.oid;
hidden hidden|field.widget.hidden;
category category|field.widget.category;
structural hidden or category == 'structural';
required required|field.required;"
class="form-group ${field.error and 'has-error' or ''} ${field.widget.item_css_class or ''} ${field.default_item_css_class()}"
title="${description}"
id="item-${oid}"
tal:omit-tag="structural"
i18n:domain="user">
<label for="${oid}"
class="col-md-3 control-label ${required and 'required' or ''}"
tal:condition="not structural"
id="req-${oid}">
${title}
</label>
<div tal:define="input_prepend field.widget.input_prepend | None;
input_append field.widget.input_append | None"
tal:omit-tag="not (input_prepend or input_append)">
<span class="input-group-addon" tal:condition="input_prepend">
${input_prepend}
</span>
<div class="col-md-9">
<span tal:replace="structure field.serialize(cstruct).strip()"/>
<span class="input-group-addon" tal:condition="input_append">
${input_append}
</span>
</div>
</div>
<p class="help-block"
tal:define="errstr 'error-%s' % field.oid"
tal:repeat="msg field.error.messages()"
i18n:translate=""
tal:attributes="id repeat.msg.index==0 and errstr or
('%s-%s' % (errstr, repeat.msg.index))"
tal:condition="field.error and not field.widget.hidden and not field.typ.__class__.__name__=='Mapping'">
${msg}
</p>
<p tal:condition="field.description and not field.widget.hidden" class="help-block">
${field.description}
</p>
</div>
</div>
<div
tal:define="title field.title;
widgets field.my_widgets;"
class="panel">
<div tal:condition="title != 'No Title'" class="panel-heading bg-info">
<h2 class="panel-title text-center">${title}</h2>
</div>
<div class="panel-body">
<div tal:replace="structure widgets"/>
</div>
</div>
import logging
import os
from sqlalchemy import (
func,
cast,
Text,
)
from pyramid.view import view_config
from pyramid.i18n import TranslationStringFactory
from deform import (
Form,
ZPTRendererFactory,
)
from datatables import (
ColumnDT,
DataTables,
)
from ..models import DBSession
from ..models.ziggurat import User
log = logging.getLogger(__name__)
from ...models import DBSession
from ...models.ziggurat import User
from ..user import (
deform_templates,
BaseView,
)
from .renderer import render
_ = TranslationStringFactory('user')
......@@ -27,9 +32,14 @@ columns = [
ColumnDT(User.status),
ColumnDT(cast(User.last_login_date, Text))]
here = os.path.abspath(os.path.dirname(__file__))
my_templates = os.path.join(here, 'templates', 'user')
search_path = [my_templates, deform_templates]
my_renderer = ZPTRendererFactory(search_path)
@view_config(
route_name='user-grid', renderer='templates/user/grid.pt',
route_name='theme1-user', renderer='templates/user/list.pt',
permission='user-edit')
def view_list(request):
return dict(title=_('Users'))
......@@ -38,7 +48,27 @@ def view_list(request):
@view_config(route_name='user-act', renderer='json', permission='user-edit')
def view_act(request):
if request.matchdict['act'] == 'grid':
log.debug(f'{request.path}')
q = DBSession.query().select_from(User)
dt = DataTables(request.GET, q, columns)
return dt.output_result()
class View(BaseView):
def render(self, form): # Override
form.my_widgets = render(form, 'templates/user/edit.yml')
return form.render()
def get_form(self, schema, user=None): # Override
return super().get_form(schema, user, my_renderer)
@view_config(
route_name='theme1-user-add', renderer='templates/user/add.pt',
permission='user-edit')
def add(self):
return super().add()
@view_config(
route_name='theme1-user-edit', renderer='templates/user/edit.pt',
permission='user-edit')
def edit(self):
return super().edit()
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!