#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# Isomer - The distributed application framework
# ==============================================
# Copyright (C) 2011-2020 Heiko 'riot' Weinen <riot@c-base.org> and others.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from isomer.logger import isolog, warn # , verbose
from isomer.misc.std import colors
from pycountry import countries, subdivisions # , currencies, languages,
from random import randint, choice
"""
Module defaultform
==================
A default form listing all object elements with submit button.
"""
savebutton = {
"type": "button",
"title": "Save Object",
"condition": "$ctrl.readonly === false",
"onClick": "$ctrl.submitObject()",
}
createnewbutton = {
"type": "button",
"title": "Save & Create new",
"condition": "$ctrl.readonly === false",
"onClick": "$ctrl.save_createObject()",
}
deletebutton = {
"type": "button",
"title": "Delete Object",
"condition": "$ctrl.readonly === false",
"onClick": "$ctrl.deleteObject()",
}
editbuttons = {
"type": "actions",
"condition": "$ctrl.readonly === false",
"items": [savebutton, createnewbutton, deletebutton],
}
defaultform = ["*", editbuttons]
changeonlyform = [
"*",
{"type": "actions", "condition": "$ctrl.readonly === false", "items": [savebutton]},
]
readonlyform = ["*"]
noform = []
[docs]def lookup_field(
key,
lookup_type=None,
placeholder=None,
html_class="div",
select_type="strapselect",
mapping="uuid",
search_filter=None,
):
"""Generates a lookup field for form definitions"""
if lookup_type is None:
lookup_type = key
if placeholder is None:
placeholder = "Select a " + lookup_type
result = {
"key": key,
"htmlClass": html_class,
"type": select_type,
"placeholder": placeholder,
"options": {
"type": lookup_type,
"asyncCallback": "$ctrl.getFormData",
"map": {"valueProperty": mapping, "nameProperty": "name"},
},
}
if search_filter is not None:
result["options"]["search_filter"] = search_filter
return result
[docs]def lookup_field_multiple(
key,
subkey=None,
button="Add",
lookup_type=None,
placeholder=None,
html_class="div",
select_type="strapselect",
mapping="uuid",
):
if subkey is None:
subkey = key + "[]"
return {
"key": key,
"add": button,
"htmlClass": html_class,
"startEmpty": True,
"style": {"add": "btn-success"},
"items": [
{
"key": subkey,
"type": select_type,
"placeholder": placeholder,
"options": {
"type": lookup_type,
"asyncCallback": "$ctrl.getFormData",
"map": {"valueProperty": mapping, "nameProperty": "name"},
},
}
],
}
[docs]def lookup_object(key, lookup_type=None, actions=None):
"""Returns a lookup button to inspect a selected object"""
if lookup_type is None:
lookup_type = key
if actions is None:
actions = ["edit", "create"]
template = ""
for action in actions:
uuid_key = "{{model.%s}}" % key
condition = 'ng-show="model.%s != null"' % key
if action == "edit":
icon = "pencil"
button_class = "success"
elif action == "view":
icon = "search"
button_class = "info"
elif action == "create":
icon = "plus"
condition = ""
button_class = "info"
uuid_key = ""
elif action == "unset":
# TODO: This needs to change the link to unset the model attribute
icon = "plus"
condition = ""
button_class = "info"
elif action == "delete":
icon = "trash"
button_class = "danger"
else:
icon = "question"
condition = ""
button_class = "info"
template += (
'<a %s class="btn btn-%s btn-sm"'
'href="/#!/editor/%s/%s/%s">'
'<span class="fa fa-%s"></span>'
"</a>" % (condition, button_class, lookup_type, uuid_key, action, icon)
)
result = {"key": "lookup_" + key, "type": "template", "template": template}
return result
[docs]def create_object(key, lookup_type):
"""Returns a lookup button to inspect a selected object"""
result = {
"key": "create_" + key,
"type": "template",
"template": '<a href="/#!/editor/' + lookup_type + '//create">Create new</a>',
}
return result
[docs]def fieldset(title, items, options=None):
"""A field set with a title and sub items"""
result = {"title": title, "type": "fieldset", "items": items}
if options is not None:
result.update(options)
return result
[docs]def section(rows: int, columns: int, items: list,
label: str = None, condition: str = None):
"""A section consisting of rows and columns
:param rows: Number of rows
:param columns: Number of columns
:param items: Section items
:param label: Optional label - if you use this, unpack the section with
``*section(.., label="foo")`` in your form
:param condition: A angular-schema-form model condition
:return: A complex form section object
"""
if label is None:
label = "Section " + choice(colors) + " " + str(randint(0, 500))
label_widget = None
else:
label_widget = {'type': 'help', 'helpvalue': '<h2>' + label + '</h2>'}
sections = []
column_class = "section-column col-sm-%i" % (12 / columns)
items_total = 0
items_count = 0
for row in items:
items_total += len(row)
for vertical in range(columns):
column_items = []
for horizontal in range(rows):
try:
item = items[horizontal][vertical]
column_items.append(item)
items_count += 1
except IndexError:
# No item in this part of the form, doesn't matter
pass
column = {"type": "section", "htmlClass": column_class, "items": column_items}
sections.append(column)
if items_count < items_total:
isolog(
items_total - items_count,
"field(s) in",
label,
"omitted, due to missing row/column:",
lvl=warn,
emitter="FORMS",
tb=True,
frame=2,
)
result = {"type": "section", "htmlClass": "row", "items": sections}
if condition is not None:
result["condition"] = condition
if label_widget is not None:
result = [label_widget, result]
return result
[docs]def emptyArray(key, add_label=None):
"""An array that starts empty"""
result = {"key": key, "startEmpty": True}
if add_label is not None:
result["add"] = add_label
result["style"] = {"add": "btn-success"}
return result
[docs]def tabset(titles, contents):
"""A tabbed container widget"""
tabs = []
for no, title in enumerate(titles):
tab = {"title": title}
content = contents[no]
if isinstance(content, list):
tab["items"] = content
else:
tab["items"] = [content]
tabs.append(tab)
result = {"type": "tabs", "tabs": tabs}
return result
# def collapsible(key, elements, label=None):
# """Widget for a collapsible section"""
#
# if not label:
# label = key
#
# result = {
# "type": "template",
# "template": '<h3 ng-click="form.'
# + key
# + "_collapsed = !form."
# + key
# + '_collapsed">'
# + label
# + "<span ng-class=\"{'fa-chevron-up': form."
# + key
# + "_collapsed,"
# " 'fa-chevron-down': !form." + key + '_collapsed}" class="fa">'
# "</span>"
# "</h3>",
# }
#
# return (
# result,
# {
# "type": "section",
# "condition": "form." + key + "_collapsed",
# "items": elements,
# },
# )
#
[docs]def country_field(key="country"):
"""Provides a select box for country selection"""
country_list = list(countries)
title_map = []
for item in country_list:
title_map.append({"value": item.alpha_3, "name": item.name})
widget = {"key": key, "type": "uiselect", "titleMap": title_map}
return widget
[docs]def area_field(key="area"):
"""Provides a select box for country selection"""
area_list = list(subdivisions)
title_map = []
for item in area_list:
title_map.append({"value": item.code, "name": item.name})
widget = {"key": key, "type": "uiselect", "titleMap": title_map}
return widget
[docs]def horizontal_divider():
"""Inserts a horizontal ruler/divider"""
widget = {"type": "help", "helpvalue": "<hr />"}
return widget
[docs]def test():
"""Development function to manually test all widgets"""
# TODO: Get rid of this and put it into testing
print("Hello")
from pprint import pprint
section_thing = section(2, 3, [["first", "second", "third"], ["fourth", "fifth"]])
pprint(section_thing)
fieldset_thing = fieldset("Fieldset", ["1", "2", "3"])
pprint(fieldset_thing)
thing = tabset(["First", "Second"], [section_thing, fieldset_thing])
pprint(thing)
if __name__ == "__main__":
test()