0001-general-don-t-flatten-to-invalid-keys-52579.patch
tests/backoffice_pages/test_all.py | ||
---|---|---|
4447 | 4447 |
assert 'form_user_f3' not in resp.text |
4448 | 4448 |
assert 'form_user_f_' not in resp.text |
4449 | 4449 | |
4450 |
# make sure workflow data itself is not displayed, as it's available in |
|
4451 |
# expanded variables |
|
4452 |
assert not pq('[title="form_workflow_data"]') |
|
4453 | ||
4454 |
# but do display it if it has some invalid contents |
|
4455 |
formdata.workflow_data['invalid key'] = 'foobar' |
|
4456 |
formdata.store() |
|
4457 |
resp = login(get_app(pub)).get(resp.request.url) |
|
4458 |
assert resp.pyquery('[title="form_workflow_data"]') |
|
4459 | ||
4450 | 4460 |
# check functions |
4451 | 4461 |
assert re.findall('Recipient.*foobar', resp.text) |
4452 | 4462 |
tests/test_formdata.py | ||
---|---|---|
546 | 546 |
assert substvars['foo_var_file_url'] |
547 | 547 | |
548 | 548 | |
549 |
def test_workflow_data_invalid_keys(pub, formdef): |
|
550 |
formdata = formdef.data_class()() |
|
551 |
formdata.store() |
|
552 |
formdata.workflow_data = { |
|
553 |
'valid_key': {'invalid key': 'foo', 'valid_key': 'bar'}, |
|
554 |
'invalid key': 'baz', |
|
555 |
} |
|
556 |
substvars = formdata.get_substitution_variables() |
|
557 |
assert 'form_workflow_data_valid_key' in substvars |
|
558 |
assert 'form_workflow_data_invalid key' not in substvars |
|
559 |
assert 'form_workflow_data_valid_key_valid_key' in substvars |
|
560 |
assert 'form_workflow_data_valid_key_invalid key' not in substvars |
|
561 |
assert substvars['form_workflow_data_valid_key_valid_key'] == 'bar' |
|
562 |
with pytest.raises(KeyError): |
|
563 |
# noqa pylint: disable=pointless-statement |
|
564 |
substvars['form_workflow_data_invalid key'] |
|
565 | ||
566 | ||
549 | 567 |
def test_evolution_get_status(pub): |
550 | 568 |
Workflow.wipe() |
551 | 569 |
workflow = Workflow(name='test') |
wcs/backoffice/management.py | ||
---|---|---|
3279 | 3279 |
'inspect_url': v['form'].backoffice_url + 'inspect', |
3280 | 3280 |
'display_name': v['form_display_name'], |
3281 | 3281 |
} |
3282 |
elif hasattr(v, 'inspect_keys') or isinstance(v, dict):
|
|
3283 |
# skip expanded
|
|
3282 |
elif hasattr(v, 'inspect_keys'): |
|
3283 |
# skip as there are expanded identifiers
|
|
3284 | 3284 |
continue |
3285 | 3285 |
else: |
3286 |
if isinstance(v, dict): |
|
3287 |
# only display dictionaries if they have invalid keys |
|
3288 |
# (otherwise the expanded identifiers are a better way |
|
3289 |
# to get to the values). |
|
3290 |
if all(CompatibilityNamesDict.valid_key_regex.match(k) for k in v): |
|
3291 |
continue |
|
3286 | 3292 |
r += htmltext('<li><code title="%s">%s</code>') % (k, k) |
3287 | 3293 |
r += htmltext(' <div class="value"><span>%s</span>') % ellipsize(safe(v), 10000) |
3288 | 3294 |
if not isinstance(v, str): |
wcs/formdata.py | ||
---|---|---|
29 | 29 |
from .qommon.evalutils import make_datetime |
30 | 30 |
from .qommon.publisher import get_cfg |
31 | 31 |
from .qommon.storage import Contains, Intersects, Null, StorableObject |
32 |
from .qommon.substitution import Substitutions, invalidate_substitution_cache |
|
32 |
from .qommon.substitution import CompatibilityNamesDict, Substitutions, invalidate_substitution_cache
|
|
33 | 33 |
from .qommon.template import Template |
34 | 34 | |
35 | 35 | |
... | ... | |
908 | 908 |
def get_substitution_variables(self, minimal=False): |
909 | 909 |
from wcs.workflows import AttachmentsSubstitutionProxy |
910 | 910 | |
911 |
from .qommon.substitution import CompatibilityNamesDict |
|
912 | ||
913 | 911 |
variables = CompatibilityNamesDict( |
914 | 912 |
{ |
915 | 913 |
'form': self.get_as_lazy(), |
... | ... | |
949 | 947 | |
950 | 948 |
d = copy.deepcopy(d) |
951 | 949 |
flatten_dict(d) |
952 |
variables.update(d)
|
|
950 |
variables.update({k: v for k, v in d.items() if CompatibilityNamesDict.valid_key_regex.match(k)})
|
|
953 | 951 | |
954 | 952 |
return variables |
955 | 953 |
wcs/qommon/substitution.py | ||
---|---|---|
14 | 14 |
# You should have received a copy of the GNU General Public License |
15 | 15 |
# along with this program; if not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 |
import re |
|
17 | 18 |
from contextlib import contextmanager |
18 | 19 | |
19 | 20 |
from quixote import get_publisher |
... | ... | |
161 | 162 |
class CompatibilityNamesDict(dict): |
162 | 163 |
# custom dictionary that provides automatic fallback to legacy variable |
163 | 164 |
# names (namespaced with underscores) |
165 | ||
166 |
valid_key_regex = re.compile(r'^[a-zA-Z][a-zA-Z0-9_]*$') |
|
167 | ||
164 | 168 |
def get(self, key, default=None): |
165 | 169 |
try: |
166 | 170 |
return self.__getitem__(key) |
... | ... | |
178 | 182 |
if hasattr(item, 'inspect_keys'): |
179 | 183 |
sub_keys = item.inspect_keys() |
180 | 184 |
elif isinstance(item, dict): |
181 |
sub_keys = item.keys()
|
|
185 |
sub_keys = [x for x in item.keys() if self.valid_key_regex.match(x)]
|
|
182 | 186 |
else: |
183 | 187 |
return |
184 | 188 |
for sub_key in sub_keys: |
... | ... | |
226 | 230 |
raise KeyError |
227 | 231 | |
228 | 232 |
def __getitem__(self, key): |
233 |
if not self.valid_key_regex.match(key): |
|
234 |
raise KeyError(key) |
|
229 | 235 |
parts = key.split('_') |
230 | 236 |
try: |
231 | 237 |
return self.get_path(self, parts) |
232 |
- |