From 707d7abf6debaea196d8ac5f45a8d825bcc0b409 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Tue, 13 Aug 2013 13:04:27 +0200 Subject: [PATCH 1/8] make sure part is encoded string (BeautifulSoup sometimes does not encode input --- util/html.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/html.py b/util/html.py index 25cf8f2..a8d9e4e 100644 --- a/util/html.py +++ b/util/html.py @@ -115,4 +115,6 @@ def extractFirstPart(value): else: text = stripAll(value) part = sentencePattern.split(text)[0] + if isinstance(part, unicode): + part = part.encode('UTF-8') return ('

%s

' % part).decode('utf8') From 2c09a682dbb184159ee7a769b778512705e24323 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Fri, 16 Aug 2013 15:44:45 +0200 Subject: [PATCH 2/8] preserve information on client context and - for records/grid - row index in field instances --- composer/schema/field.py | 4 +++- composer/schema/grid/field.py | 23 ++++++++++++++--------- composer/schema/instance.py | 5 +++-- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/composer/schema/field.py b/composer/schema/field.py index e000e02..0eb88f2 100644 --- a/composer/schema/field.py +++ b/composer/schema/field.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2011 Helmut Merz helmutm@cy55.de +# Copyright (c) 2013 Helmut Merz helmutm@cy55.de # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -185,8 +185,10 @@ class FieldInstance(object): adapts(IField) clientInstance = None + clientContext = None value = undefined request = None + index = None def __init__(self, context): self.context = context diff --git a/composer/schema/grid/field.py b/composer/schema/grid/field.py index dd69c90..aefd2d2 100644 --- a/composer/schema/grid/field.py +++ b/composer/schema/grid/field.py @@ -50,8 +50,11 @@ class GridFieldInstance(ListFieldInstance): for f in self.columnTypes: instanceName = (f.instance_name or f.getFieldTypeInfo().instanceName) - result.append(component.getAdapter(f, IFieldInstance, - name=instanceName)) + fi = component.getAdapter(f, IFieldInstance, name=instanceName) + fi.clientInstance = self.clientInstance + fi.clientContext = self.clientContext + fi.request = self.request + result.append(fi) return result def marshall(self, value): @@ -89,8 +92,8 @@ class GridFieldInstance(ListFieldInstance): return [] result = [] rows = json.loads(value)['items'] - for row in rows: - item = self.unmarshallRow(row) + for idx, row in enumerate(rows): + item = self.unmarshallRow(row, idx) if item: result.append(item) return result @@ -111,9 +114,11 @@ class GridFieldInstance(ListFieldInstance): result.append(item) return result - def unmarshallRow(self, row): + def unmarshallRow(self, row, idx=None): item = {} for fi in self.columnFieldInstances: + if idx is not None: + fi.index = idx value = fi.unmarshall(row.get(fi.name) or u'') if isinstance(value, basestring): value = value.strip() @@ -143,8 +148,8 @@ class RecordsFieldInstance(GridFieldInstance): if not value: value = [] result = [] - for row in value: - item = self.unmarshallRow(row) + for idx, row in enumerate(value): + item = self.unmarshallRow(row, idx) if item: result.append(item) return result @@ -187,8 +192,8 @@ class KeyTableFieldInstance(RecordsFieldInstance): if not value: value = {} result = {} - for row in value: - item = self.unmarshallRow(row) + for idx, row in enumerate(value): + item = self.unmarshallRow(row, idx) if item: result[item.pop(self.keyName)] = [item.get(name) or u'' for name in self.dataNames] diff --git a/composer/schema/instance.py b/composer/schema/instance.py index 1c5ffbf..cb1cd87 100644 --- a/composer/schema/instance.py +++ b/composer/schema/instance.py @@ -68,7 +68,8 @@ class Instance(BaseInstance): template = self.template if template is not None: for f in template.components: - fieldInstances[f.name] = f.getFieldInstance(self) + fieldInstances[f.name] = f.getFieldInstance(self, + context=self.context) return fieldInstances @Lazy @@ -122,7 +123,7 @@ class Editor(BaseInstance): for f in self.template.components: if f.readonly: continue - fi = f.getFieldInstance(self) + fi = f.getFieldInstance(self, context=self.context) #value = data.get(f.name) value = fi.getRawValue(data, f.name) fi.validate(value, data) From 8cbe50a90be62cdb63337c0b2a71be0925a690be Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Sat, 17 Aug 2013 15:10:15 +0200 Subject: [PATCH 3/8] avoid overwriting of filename information in file fields when no file is uploaded --- composer/schema/grid/field.py | 17 ++++++++++++++++- composer/schema/instance.py | 6 ++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/composer/schema/grid/field.py b/composer/schema/grid/field.py index aefd2d2..2188d7e 100644 --- a/composer/schema/grid/field.py +++ b/composer/schema/grid/field.py @@ -148,10 +148,25 @@ class RecordsFieldInstance(GridFieldInstance): if not value: value = [] result = [] + oldValue = getattr(self.clientContext, self.name, []) for idx, row in enumerate(value): item = self.unmarshallRow(row, idx) if item: - result.append(item) + if len(oldValue) > idx: + oldItem = oldValue[idx] + for k, v in item.items(): + if v != '__no_change__': + oldItem[k] = v + #if oldItem.get(k) == '__no_change__': + # del oldItem[k] + #if oldItem: + result.append(oldItem) + else: + for k, v in item.items(): + if v == '__no_change__': + del item[k] + if item: + result.append(item) return result diff --git a/composer/schema/instance.py b/composer/schema/instance.py index cb1cd87..11d2718 100644 --- a/composer/schema/instance.py +++ b/composer/schema/instance.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2007 Helmut Merz helmutm@cy55.de +# Copyright (c) 2013 Helmut Merz helmutm@cy55.de # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -18,8 +18,6 @@ """ Instance adapter classes for schemas. - -$Id$ """ from BTrees.OOBTree import OOBTree @@ -110,7 +108,7 @@ class Editor(BaseInstance): fieldHandlers[ftype](context, value, fi, formState) else: oldValue = getattr(context, name, None) - if value != oldValue: + if value not in ('__no_change__', oldValue): setattr(context, name, value) fi.change = (oldValue, value) formState.changed = True From b82bf879fd62c6543563d9716a61704ab269c9af Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Mon, 9 Sep 2013 17:03:33 +0200 Subject: [PATCH 4/8] fix records field: accept None as (old) value --- composer/schema/grid/field.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer/schema/grid/field.py b/composer/schema/grid/field.py index 2188d7e..78f57a2 100644 --- a/composer/schema/grid/field.py +++ b/composer/schema/grid/field.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2011 Helmut Merz helmutm@cy55.de +# Copyright (c) 2013 Helmut Merz helmutm@cy55.de # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -148,7 +148,7 @@ class RecordsFieldInstance(GridFieldInstance): if not value: value = [] result = [] - oldValue = getattr(self.clientContext, self.name, []) + oldValue = getattr(self.clientContext, self.name, None) or [] for idx, row in enumerate(value): item = self.unmarshallRow(row, idx) if item: From 4f54192ae47a95898d00c614d42a9fdbf3ec8f6e Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Mon, 7 Oct 2013 14:39:19 +0200 Subject: [PATCH 5/8] allow for skipping certain fields when checking for empty rows --- composer/schema/grid/field.py | 6 +++++- composer/schema/grid/interfaces.py | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/composer/schema/grid/field.py b/composer/schema/grid/field.py index 78f57a2..3630f82 100644 --- a/composer/schema/grid/field.py +++ b/composer/schema/grid/field.py @@ -127,7 +127,11 @@ class GridFieldInstance(ListFieldInstance): continue if value: item[fi.name] = value - return item + ignoreInCheckOnEmpty = getattr(self.context, 'ignoreInCheckOnEmpty', []) + for k, v in item.items(): + if k not in ignoreInCheckOnEmpty and v != '__no_change__': + return item + return {} class RecordsFieldInstance(GridFieldInstance): diff --git a/composer/schema/grid/interfaces.py b/composer/schema/grid/interfaces.py index 1979be0..bc521d9 100644 --- a/composer/schema/grid/interfaces.py +++ b/composer/schema/grid/interfaces.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2011 Helmut Merz helmutm@cy55.de +# Copyright (c) 2013 Helmut Merz helmutm@cy55.de # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -38,6 +38,7 @@ class Grid(schema.List): instanceName='grid')) column_types = [] + ignoreInCheckOnEmpty = [] cardinality = None From a1e2d52967437ccbad772345f9229aff5a4ddd74 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Fri, 11 Oct 2013 11:20:43 +0200 Subject: [PATCH 6/8] always display/process all rows within cardinality if this is set --- composer/schema/grid/field.py | 66 +++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/composer/schema/grid/field.py b/composer/schema/grid/field.py index 3630f82..ab441b5 100644 --- a/composer/schema/grid/field.py +++ b/composer/schema/grid/field.py @@ -79,11 +79,15 @@ class GridFieldInstance(ListFieldInstance): def display(self, value): headers = [fi.context.title for fi in self.columnFieldInstances] rows = [] - for item in value or []: - row = [] - for fi in self.columnFieldInstances: - row.append(fi.display(item.get(fi.name))) - rows.append(row) + value = value or [] + cardinality = getattr(self.context, 'cardinality', None) + for item in value: + rows.append([fi.display(item.get(fi.name)) + for fi in self.columnFieldInstances]) + if cardinality > len(value): + for item in range(len(value), self.context.cardinality): + rows.append([fi.display(fi.default) + for fi in self.columnFieldInstances]) return dict(headers=headers, rows=rows) def unmarshall(self, value): @@ -116,17 +120,21 @@ class GridFieldInstance(ListFieldInstance): def unmarshallRow(self, row, idx=None): item = {} + cardinality = getattr(self.context, 'cardinality', None) for fi in self.columnFieldInstances: if idx is not None: fi.index = idx value = fi.unmarshall(row.get(fi.name) or u'') if isinstance(value, basestring): value = value.strip() - if fi.default is not None: - if value == fi.default: - continue - if value: + if idx < cardinality: item[fi.name] = value + else: + if fi.default is not None: + if value == fi.default: + continue + if value: + item[fi.name] = value ignoreInCheckOnEmpty = getattr(self.context, 'ignoreInCheckOnEmpty', []) for k, v in item.items(): if k not in ignoreInCheckOnEmpty and v != '__no_change__': @@ -141,11 +149,19 @@ class RecordsFieldInstance(GridFieldInstance): def marshall(self, value): result = [] - for row in value or []: + value = value or [] + cardinality = getattr(self.context, 'cardinality', None) + for row in value: item = {} for fi in self.columnFieldInstances: item[fi.name] = fi.marshall(row.get(fi.name)) result.append(item) + if cardinality > len(value): + for row in range(len(value), cardinality): + item = {} + for fi in self.columnFieldInstances: + item[fi.name] = fi.marshall(fi.default) + result.append(item) return result def unmarshall(self, value): @@ -156,23 +172,29 @@ class RecordsFieldInstance(GridFieldInstance): for idx, row in enumerate(value): item = self.unmarshallRow(row, idx) if item: + oldItem = {} if len(oldValue) > idx: oldItem = oldValue[idx] - for k, v in item.items(): - if v != '__no_change__': - oldItem[k] = v - #if oldItem.get(k) == '__no_change__': - # del oldItem[k] - #if oldItem: - result.append(oldItem) - else: - for k, v in item.items(): - if v == '__no_change__': + for k, v in item.items(): + if v == '__no_change__': + if k in oldItem: + item[k] = oldItem[k] + else: del item[k] - if item: - result.append(item) + if item: + result.append(item) return result + def validate(self, value, data=None): + if not value: + if self.context.required: + self.setError('required_missing') + else: + return + for row in value: + for fi in self.columnFieldInstances: + fi.validate(row.get(fi.name) or u'') + class KeyTableFieldInstance(RecordsFieldInstance): From cad5ba5ca35626e8d67e007e6b5b0a95c8a0abd6 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Sat, 19 Oct 2013 17:31:59 +0200 Subject: [PATCH 7/8] give empty records a corresponding flag on display as basis for better UI information --- composer/schema/grid/field.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer/schema/grid/field.py b/composer/schema/grid/field.py index ab441b5..c893ce4 100644 --- a/composer/schema/grid/field.py +++ b/composer/schema/grid/field.py @@ -88,7 +88,8 @@ class GridFieldInstance(ListFieldInstance): for item in range(len(value), self.context.cardinality): rows.append([fi.display(fi.default) for fi in self.columnFieldInstances]) - return dict(headers=headers, rows=rows) + empty = not rows or (len(rows) == 1 and not [v for v in rows[0] if v]) + return dict(headers=headers, rows=rows, empty=empty) def unmarshall(self, value): value = toUnicode(value.strip()) From 165f23cc0b87c5a47e90e82e8a29e177251fb239 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Sat, 19 Oct 2013 20:00:42 +0200 Subject: [PATCH 8/8] do not automatically ignore file upload fields when checking for empty rows --- composer/schema/grid/field.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer/schema/grid/field.py b/composer/schema/grid/field.py index c893ce4..32b101e 100644 --- a/composer/schema/grid/field.py +++ b/composer/schema/grid/field.py @@ -138,7 +138,7 @@ class GridFieldInstance(ListFieldInstance): item[fi.name] = value ignoreInCheckOnEmpty = getattr(self.context, 'ignoreInCheckOnEmpty', []) for k, v in item.items(): - if k not in ignoreInCheckOnEmpty and v != '__no_change__': + if k not in ignoreInCheckOnEmpty: #and v != '__no_change__': return item return {}