Forms for attachments work, VERY HACKISH Hardcodedness™
This commit is contained in:
parent
5bd2c101fe
commit
3cf71a365f
@ -43,7 +43,6 @@ node_type_asset = {
|
||||
},
|
||||
'form_schema': {
|
||||
'content_type': {'visible': False},
|
||||
'attachments': {'visible': False},
|
||||
'order': {'visible': False},
|
||||
'tags': {'visible': False},
|
||||
'categories': {'visible': False}
|
||||
|
@ -4,9 +4,11 @@ import re
|
||||
from bson import ObjectId
|
||||
import flask
|
||||
import pillarsdk
|
||||
import wtforms
|
||||
|
||||
from pillar.api.node_types import ATTACHMENT_SLUG_REGEX
|
||||
from pillar.web.utils import system_util
|
||||
from pillar.web.utils.forms import build_file_select_form, CustomFormField
|
||||
|
||||
shortcode_re = re.compile(r'@\[(%s)\]' % ATTACHMENT_SLUG_REGEX)
|
||||
log = logging.getLogger(__name__)
|
||||
@ -86,3 +88,51 @@ def render_attachment_file_image(sdk_file):
|
||||
variations = {var.size: var for var in sdk_file.variations}
|
||||
return flask.render_template('nodes/attachments/file_image.html',
|
||||
file=sdk_file, vars=variations)
|
||||
|
||||
|
||||
def attachment_form_group_create(schema_prop):
|
||||
"""Creates a wtforms.FieldList for attachments."""
|
||||
|
||||
file_select_form_group = _attachment_build_single_field(schema_prop)
|
||||
field = wtforms.FieldList(CustomFormField(file_select_form_group), min_entries=1)
|
||||
|
||||
return field
|
||||
|
||||
|
||||
def _attachment_build_single_field(schema_prop):
|
||||
# Ugly hard-coded schema.
|
||||
fake_schema = {
|
||||
'slug': schema_prop['propertyschema'],
|
||||
'oid': schema_prop['valueschema']['schema']['oid'],
|
||||
}
|
||||
file_select_form_group = build_file_select_form(fake_schema)
|
||||
return file_select_form_group
|
||||
|
||||
|
||||
def attachment_form_group_set_data(db_prop_value, schema_prop, field_list):
|
||||
"""Populates the attachment form group with data from MongoDB."""
|
||||
|
||||
assert isinstance(db_prop_value, dict)
|
||||
|
||||
# Extra entries are caused by min_entries=1 in the form creation.
|
||||
while len(field_list):
|
||||
field_list.pop_entry()
|
||||
|
||||
for slug, att_data in sorted(db_prop_value.iteritems()):
|
||||
file_select_form_group = _attachment_build_single_field(schema_prop)
|
||||
subform = file_select_form_group()
|
||||
|
||||
# Even uglier hard-coded
|
||||
subform.slug = slug
|
||||
subform.oid = att_data['oid']
|
||||
|
||||
field_list.append_entry(subform)
|
||||
|
||||
|
||||
def attachment_form_parse_post_data(data):
|
||||
"""Returns a dict that can be stored in the node.properties.attachments."""
|
||||
|
||||
# Moar ugly hardcodedness.
|
||||
attachments = {allprops['slug']: {'oid': allprops['oid']}
|
||||
for allprops in data}
|
||||
return attachments
|
||||
|
@ -19,14 +19,30 @@ from wtforms import FieldList
|
||||
from wtforms.validators import DataRequired
|
||||
from pillar.web.utils import system_util
|
||||
from pillar.web.utils.forms import FileSelectField
|
||||
from pillar.web.utils.forms import ProceduralFileSelectForm
|
||||
from pillar.web.utils.forms import CustomFormField
|
||||
from pillar.web.utils.forms import build_file_select_form
|
||||
|
||||
from . import attachments
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def add_form_properties(form_class, node_schema, form_schema, prefix=''):
|
||||
def iter_node_properties(node_type):
|
||||
"""Generator, iterates over all node properties with form schema."""
|
||||
|
||||
node_schema = node_type['dyn_schema'].to_dict()
|
||||
form_schema = node_type['form_schema'].to_dict()
|
||||
|
||||
for prop_name, prop_schema in node_schema.iteritems():
|
||||
prop_fschema = form_schema.get(prop_name, {})
|
||||
|
||||
if not prop_fschema.get('visible', True):
|
||||
continue
|
||||
|
||||
yield prop_name, prop_schema, prop_fschema
|
||||
|
||||
|
||||
def add_form_properties(form_class, node_type):
|
||||
"""Add fields to a form based on the node and form schema provided.
|
||||
:type node_schema: dict
|
||||
:param node_schema: the validation schema used by Cerberus
|
||||
@ -37,33 +53,16 @@ def add_form_properties(form_class, node_schema, form_schema, prefix=''):
|
||||
show and hide)
|
||||
"""
|
||||
|
||||
for prop, schema_prop in node_schema.iteritems():
|
||||
form_prop = form_schema.get(prop, {})
|
||||
if prop == 'items':
|
||||
continue
|
||||
if not form_prop.get('visible', True):
|
||||
continue
|
||||
prop_name = "{0}{1}".format(prefix, prop)
|
||||
for prop_name, schema_prop, form_prop in iter_node_properties(node_type):
|
||||
|
||||
# Recursive call if detects a dict
|
||||
field_type = schema_prop['type']
|
||||
if field_type == 'dict':
|
||||
# This works if the dictionary schema is hardcoded.
|
||||
# If we define it using propertyschema and valueschema, this
|
||||
# validation pattern does not work and crahses.
|
||||
add_form_properties(form_class, schema_prop['schema'],
|
||||
form_prop['schema'], "{0}__".format(prop_name))
|
||||
continue
|
||||
|
||||
if field_type == 'list':
|
||||
if prop == 'attachments':
|
||||
# class AttachmentForm(Form):
|
||||
# pass
|
||||
# AttachmentForm.file = FileSelectField('file')
|
||||
# AttachmentForm.size = StringField()
|
||||
# AttachmentForm.slug = StringField()
|
||||
field = FieldList(CustomFormField(ProceduralFileSelectForm))
|
||||
elif prop == 'files':
|
||||
if field_type == 'dict':
|
||||
assert prop_name == 'attachments'
|
||||
field = attachments.attachment_form_group_create(schema_prop)
|
||||
elif field_type == 'list':
|
||||
if prop_name == 'files':
|
||||
schema = schema_prop['schema']['schema']
|
||||
file_select_form = build_file_select_form(schema)
|
||||
field = FieldList(CustomFormField(file_select_form),
|
||||
@ -112,8 +111,6 @@ def get_node_form(node_type):
|
||||
class ProceduralForm(Form):
|
||||
pass
|
||||
|
||||
node_schema = node_type['dyn_schema'].to_dict()
|
||||
form_prop = node_type['form_schema'].to_dict()
|
||||
parent_prop = node_type['parent']
|
||||
|
||||
ProceduralForm.name = StringField('Name', validators=[DataRequired()])
|
||||
@ -126,7 +123,7 @@ def get_node_form(node_type):
|
||||
ProceduralForm.picture = FileSelectField('Picture', file_format='image')
|
||||
ProceduralForm.node_type = HiddenField(default=node_type['name'])
|
||||
|
||||
add_form_properties(ProceduralForm, node_schema, form_prop)
|
||||
add_form_properties(ProceduralForm, node_type)
|
||||
|
||||
return ProceduralForm()
|
||||
|
||||
@ -166,59 +163,44 @@ def process_node_form(form, node_id=None, node_type=None, user=None):
|
||||
if form.parent.data != "":
|
||||
node.parent = form.parent.data
|
||||
|
||||
def update_data(node_schema, form_schema, prefix=""):
|
||||
for pr in node_schema:
|
||||
schema_prop = node_schema[pr]
|
||||
form_prop = form_schema.get(pr, {})
|
||||
if pr == 'items':
|
||||
continue
|
||||
if 'visible' in form_prop and not form_prop['visible']:
|
||||
continue
|
||||
prop_name = "{0}{1}".format(prefix, pr)
|
||||
if schema_prop['type'] == 'dict':
|
||||
update_data(
|
||||
schema_prop['schema'],
|
||||
form_prop['schema'],
|
||||
"{0}__".format(prop_name))
|
||||
continue
|
||||
data = form[prop_name].data
|
||||
if schema_prop['type'] == 'dict':
|
||||
if data == 'None':
|
||||
continue
|
||||
elif schema_prop['type'] == 'integer':
|
||||
if data == '':
|
||||
data = 0
|
||||
else:
|
||||
data = int(form[prop_name].data)
|
||||
elif schema_prop['type'] == 'datetime':
|
||||
data = datetime.strftime(data,
|
||||
app.config['RFC1123_DATE_FORMAT'])
|
||||
elif schema_prop['type'] == 'list':
|
||||
if pr == 'attachments':
|
||||
# data = json.loads(data)
|
||||
data = [dict(field='description', files=data)]
|
||||
elif pr == 'files':
|
||||
# Only keep those items that actually refer to a file.
|
||||
data = [file_item for file_item in data
|
||||
if file_item.get('file')]
|
||||
# elif pr == 'tags':
|
||||
# data = [tag.strip() for tag in data.split(',')]
|
||||
elif schema_prop['type'] == 'objectid':
|
||||
if data == '':
|
||||
# Set empty object to None so it gets removed by the
|
||||
# SDK before node.update()
|
||||
data = None
|
||||
for prop_name, schema_prop, form_prop in iter_node_properties(node_type):
|
||||
data = form[prop_name].data
|
||||
if schema_prop['type'] == 'dict':
|
||||
data = attachments.attachment_form_parse_post_data(data)
|
||||
elif schema_prop['type'] == 'integer':
|
||||
if data == '':
|
||||
data = 0
|
||||
else:
|
||||
if pr in form:
|
||||
data = form[prop_name].data
|
||||
path = prop_name.split('__')
|
||||
if len(path) > 1:
|
||||
recursive_prop = recursive(
|
||||
path, node.properties.to_dict(), data)
|
||||
node.properties = recursive_prop
|
||||
data = int(form[prop_name].data)
|
||||
elif schema_prop['type'] == 'datetime':
|
||||
data = datetime.strftime(data, current_app.config['RFC1123_DATE_FORMAT'])
|
||||
elif schema_prop['type'] == 'list':
|
||||
if prop_name == 'files':
|
||||
# Only keep those items that actually refer to a file.
|
||||
data = [file_item for file_item in data
|
||||
if file_item.get('file')]
|
||||
else:
|
||||
node.properties[prop_name] = data
|
||||
update_data(node_schema, form_schema)
|
||||
log.warning('Ignoring property %s of type %s',
|
||||
prop_name, schema_prop['type'])
|
||||
# elif pr == 'tags':
|
||||
# data = [tag.strip() for tag in data.split(',')]
|
||||
elif schema_prop['type'] == 'objectid':
|
||||
if data == '':
|
||||
# Set empty object to None so it gets removed by the
|
||||
# SDK before node.update()
|
||||
data = None
|
||||
else:
|
||||
if prop_name in form:
|
||||
data = form[prop_name].data
|
||||
path = prop_name.split('__')
|
||||
assert len(path) == 1
|
||||
if len(path) > 1:
|
||||
recursive_prop = recursive(
|
||||
path, node.properties.to_dict(), data)
|
||||
node.properties = recursive_prop
|
||||
else:
|
||||
node.properties[prop_name] = data
|
||||
|
||||
ok = node.update(api=api)
|
||||
if not ok:
|
||||
log.warning('Unable to update node: %s', node.error)
|
||||
|
@ -22,7 +22,6 @@ from wtforms import SelectMultipleField
|
||||
from flask_login import login_required
|
||||
from jinja2.exceptions import TemplateNotFound
|
||||
|
||||
import pillar.web.nodes.attachments
|
||||
from pillar.web.utils import caching
|
||||
from pillar.web.nodes.forms import get_node_form
|
||||
from pillar.web.nodes.forms import process_node_form
|
||||
@ -35,7 +34,7 @@ from pillar.web.utils.forms import ProceduralFileSelectForm
|
||||
from pillar.web.utils.forms import build_file_select_form
|
||||
from pillar.web import system_util
|
||||
|
||||
from . import finders
|
||||
from . import finders, attachments
|
||||
|
||||
blueprint = Blueprint('nodes', __name__)
|
||||
log = logging.getLogger(__name__)
|
||||
@ -261,7 +260,7 @@ def _view_handler_asset(node, template_path, template_action, link_allowed):
|
||||
# Treat it as normal file (zip, blend, application, etc)
|
||||
asset_type = 'file'
|
||||
|
||||
node['description'] = pillar.web.nodes.attachments.render_attachments(node, node['description'])
|
||||
node['description'] = attachments.render_attachments(node, node['description'])
|
||||
|
||||
template_path = os.path.join(template_path, asset_type)
|
||||
|
||||
@ -315,27 +314,18 @@ def edit(node_id):
|
||||
"""Generic node editing form
|
||||
"""
|
||||
|
||||
def set_properties(dyn_schema, form_schema, node_properties, form,
|
||||
prefix="",
|
||||
set_data=True):
|
||||
def set_properties(dyn_schema, form_schema, node_properties, form, set_data,
|
||||
prefix=""):
|
||||
"""Initialize custom properties for the form. We run this function once
|
||||
before validating the function with set_data=False, so that we can set
|
||||
any multiselect field that was originally specified empty and fill it
|
||||
with the current choices.
|
||||
"""
|
||||
for prop in dyn_schema:
|
||||
schema_prop = dyn_schema[prop]
|
||||
form_prop = form_schema.get(prop, {})
|
||||
prop_name = "{0}{1}".format(prefix, prop)
|
||||
|
||||
if schema_prop['type'] == 'dict':
|
||||
set_properties(
|
||||
schema_prop['schema'],
|
||||
form_prop['schema'],
|
||||
node_properties[prop_name],
|
||||
form,
|
||||
"{0}__".format(prop_name))
|
||||
continue
|
||||
log.debug('set_properties(..., prefix=%r, set_data=%r) called', prefix, set_data)
|
||||
|
||||
for prop, schema_prop in dyn_schema.iteritems():
|
||||
prop_name = "{0}{1}".format(prefix, prop)
|
||||
|
||||
if prop_name not in form:
|
||||
continue
|
||||
@ -359,49 +349,32 @@ def edit(node_id):
|
||||
form[prop_name].choices = [(d, d) for d in db_prop_value]
|
||||
# Choices should be a tuple with value and name
|
||||
|
||||
if not set_data:
|
||||
continue
|
||||
|
||||
# Assign data to the field
|
||||
if set_data:
|
||||
if prop_name == 'attachments':
|
||||
for attachment_collection in db_prop_value:
|
||||
for a in attachment_collection['files']:
|
||||
attachment_form = ProceduralFileSelectForm()
|
||||
attachment_form.file = a['file']
|
||||
attachment_form.slug = a['slug']
|
||||
attachment_form.size = 'm'
|
||||
form[prop_name].append_entry(attachment_form)
|
||||
if prop_name == 'attachments':
|
||||
attachments.attachment_form_group_set_data(db_prop_value, schema_prop,
|
||||
form[prop_name])
|
||||
elif prop_name == 'files':
|
||||
subschema = schema_prop['schema']['schema']
|
||||
# Extra entries are caused by min_entries=1 in the form
|
||||
# creation.
|
||||
field_list = form[prop_name]
|
||||
while len(field_list) > len(db_prop_value):
|
||||
field_list.pop_entry()
|
||||
|
||||
elif prop_name == 'files':
|
||||
schema = schema_prop['schema']['schema']
|
||||
# Extra entries are caused by min_entries=1 in the form
|
||||
# creation.
|
||||
field_list = form[prop_name]
|
||||
if len(db_prop_value) > 0:
|
||||
while len(field_list):
|
||||
field_list.pop_entry()
|
||||
for file_data in db_prop_value:
|
||||
file_form_class = build_file_select_form(subschema)
|
||||
subform = file_form_class()
|
||||
for key, value in file_data.iteritems():
|
||||
setattr(subform, key, value)
|
||||
field_list.append_entry(subform)
|
||||
|
||||
for file_data in db_prop_value:
|
||||
file_form_class = build_file_select_form(schema)
|
||||
subform = file_form_class()
|
||||
for key, value in file_data.iteritems():
|
||||
setattr(subform, key, value)
|
||||
field_list.append_entry(subform)
|
||||
|
||||
# elif prop_name == 'tags':
|
||||
# form[prop_name].data = ', '.join(data)
|
||||
else:
|
||||
form[prop_name].data = db_prop_value
|
||||
# elif prop_name == 'tags':
|
||||
# form[prop_name].data = ', '.join(data)
|
||||
else:
|
||||
# Default population of multiple file form list (only if
|
||||
# we are getting the form)
|
||||
if request.method == 'POST':
|
||||
continue
|
||||
if prop_name == 'attachments':
|
||||
if not db_prop_value:
|
||||
attachment_form = ProceduralFileSelectForm()
|
||||
attachment_form.file = 'file'
|
||||
attachment_form.slug = ''
|
||||
attachment_form.size = ''
|
||||
form[prop_name].append_entry(attachment_form)
|
||||
form[prop_name].data = db_prop_value
|
||||
|
||||
api = system_util.pillar_api()
|
||||
node = Node.find(node_id, api=api)
|
||||
@ -446,7 +419,7 @@ def edit(node_id):
|
||||
if node.parent:
|
||||
form.parent.data = node.parent
|
||||
|
||||
set_properties(dyn_schema, form_schema, node_properties, form)
|
||||
set_properties(dyn_schema, form_schema, node_properties, form, set_data=True)
|
||||
|
||||
# Get previews
|
||||
node.picture = get_file(node.picture, api=api) if node.picture else None
|
||||
|
@ -158,8 +158,8 @@ class CustomFormFieldWidget(object):
|
||||
|
||||
|
||||
class CustomFormField(FormField):
|
||||
def __init__(self, name, **kwargs):
|
||||
super(CustomFormField, self).__init__(name, **kwargs)
|
||||
def __init__(self, form_class, **kwargs):
|
||||
super(CustomFormField, self).__init__(form_class, **kwargs)
|
||||
self.widget = CustomFormFieldWidget()
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user