Commit 9f1ce1ba authored by Arnaud Fontaine's avatar Arnaud Fontaine

WIP

parent 75309541
# -*- coding: utf-8 -*-
#############################################################################
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
# Jean-Paul Smets-Solanes <jp@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 the Free Software Foundation; either version 2
# 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import hashlib
from copy import deepcopy
from Products.Formulator.Form import BasicForm, ZMIForm
from Products.Formulator.Errors import FormValidationError, ValidationError
from Products.Formulator.DummyField import fields
from Products.Formulator.XMLToForm import XMLToForm
from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
from Products.CMFCore.utils import _checkPermission, getToolByName
from Products.CMFCore.exceptions import AccessControl_Unauthorized
from Products.ERP5Type import PropertySheet, Permissions
from urllib import quote
from Products.ERP5Type.Globals import DTMLFile, get_request
from AccessControl import Unauthorized, ClassSecurityInfo
from DateTime import DateTime
from ZODB.POSException import ConflictError
from zExceptions import Redirect
from Acquisition import aq_base
from Products.PageTemplates.Expressions import SecureModuleImporter
from zExceptions import Forbidden
from Products.ERP5Type.PsycoWrapper import psyco
from Products.ERP5Type.Base import Base
class FieldValueCacheDict(dict):
_last_sync = -1
def clear(self):
super(FieldValueCacheDict, self).clear()
from Products.ERP5.ERP5Site import getSite
try:
portal = getSite()
except IndexError:
pass
else:
portal.newCacheCookie('form_field_value_cache')
self._last_sync = portal.getCacheCookie('form_field_value_cache')
def __getitem__(self, cache_id):
from Products.ERP5.ERP5Site import getSite
try:
portal = getSite()
except IndexError:
pass
else:
cookie = portal.getCacheCookie('form_field_value_cache')
if cookie != self._last_sync:
LOG("ERP5Form.Form", 0, "Resetting form field value cache")
self._last_sync = cookie
super(FieldValueCacheDict, self).clear()
raise KeyError('Field cache is outdated and has been reset')
return super(FieldValueCacheDict, self).__getitem__(cache_id)
field_value_cache = FieldValueCacheDict()
# Patch the fiels methods to provide improved namespace handling
from Products.Formulator.Field import Field
from Products.Formulator.MethodField import Method, BoundMethod
from Products.Formulator.TALESField import TALESMethod
from zLOG import LOG, PROBLEM
def isCacheable(value):
value = aq_base(value)
if type(value) is BoundMethod:
return False
jar = getattr(value, '_p_jar', None)
if jar is not None:
return False
dic = getattr(value, '__dict__', None)
if dic is not None:
for i in dic.values():
jar = getattr(i, '_p_jar', None)
if jar is not None:
return False
return True
def copyMethod(value):
if type(aq_base(value)) is Method:
value = Method(value.method_name)
elif type(aq_base(value)) is TALESMethod:
value = TALESMethod(value._text)
return value
def getFieldDict(field, value_type):
result = {}
if field.meta_type=='ProxyField':
if value_type=='values':
get_method = getattr(field, 'get_recursive_orig_value')
elif value_type=='tales':
get_method = getattr(field, 'get_recursive_tales')
else:
raise ValueError, 'value_type must be values or tales'
template_field = field.getRecursiveTemplateField()
for ui_field_id in template_field.form.fields.keys():
result[ui_field_id] = get_method(ui_field_id)
else:
if value_type=='values':
get_method = getattr(field, 'get_orig_value')
elif value_type=='tales':
get_method = getattr(field, 'get_tales')
else:
raise ValueError, 'value_type must be values or tales'
for ui_field_id in field.form.fields.keys():
result[ui_field_id] = get_method(ui_field_id)
return result
class StaticValue:
"""
Encapsulated a static value in a class
(quite heavy, would be faster to store the
value as is)
"""
def __init__(self, value):
self.value = value
def __call__(self, field, id, **kw):
return self.returnValue(field, id, self.value)
def returnValue(self, field, id, value):
# if normal value is a callable itself, wrap it
if callable(value):
value = value.__of__(field)
#value=value() # Mising call ??? XXX Make sure compatible with listbox methods
if id == 'default':
# We make sure we convert values to empty strings
# for most fields (so that we do not get a 'value'
# message on screen)
# This can be overriden by using TALES in the field
if value is None: value = ''
return value
class TALESValue(StaticValue):
def __init__(self, tales_expr):
self.tales_expr = tales_expr
def __call__(self, field, id, **kw):
REQUEST = kw.get('REQUEST', get_request())
if REQUEST is not None:
# Proxyfield stores the "real" field in the request. Look if the
# corresponding field exists in request, and use it as field in the
# TALES context
field = REQUEST.get(
'field__proxyfield_%s_%s_%s' % (field.id, field._p_oid, id),
field)
kw['field'] = field
form = field.aq_parent # XXX (JPS) form for default is wrong apparently in listbox - double check
obj = getattr(form, 'aq_parent', None)
if obj is not None:
container = obj.aq_inner.aq_parent
else:
container = None
kw['form'] = form
kw['request'] = REQUEST
kw['here'] = obj
kw['context'] = obj
kw['modules'] = SecureModuleImporter
kw['container'] = container
try :
kw['preferences'] = obj.getPortalObject().portal_preferences
except AttributeError :
LOG('ERP5Form', PROBLEM,
'portal_preferences not put in TALES context (not installed?)')
# This allows to pass some pointer to the local object
# through the REQUEST parameter. Not very clean.
# Used by ListBox to render different items in a list
if kw.get('cell') is None:
request = kw.get('REQUEST')
if request is not None:
if getattr(request, 'cell', None) is not None:
kw['cell'] = request.cell
else:
kw['cell'] = request
if 'cell_index' not in kw and\
getattr(request, 'cell_index', None) is not None:
kw['cell_index'] = request.cell_index
elif getattr(REQUEST, 'cell', None) is not None:
kw['cell'] = REQUEST.cell
if 'cell_index' not in kw and \
getattr(REQUEST, 'cell_index', None) is not None:
kw['cell_index'] = REQUEST.cell_index
# on Zope 2.12, only path expressions can access the CONTEXTS name
# but ERP5 has many python expressions that try to access CONTEXTS, so
# we try to keep backward compatibility
if self.tales_expr._text.startswith("python:"):
kw['CONTEXTS'] = kw
try:
value = self.tales_expr.__of__(field)(**kw)
except (ConflictError, RuntimeError, Redirect):
raise
except:
# We add this safety exception to make sure we always get
# something reasonable rather than generate plenty of errors
LOG('ERP5Form', PROBLEM,
'Field.get_value %r [%s], exception on tales_expr: ' %
(field, id), error=True)
# field may be ProxyField
# here we avoid calling field.get_recursive_orig_value
# on all fields because it can be acquired from another
# field in context. ie, from a listbox field.
# So, test condition on meta_type attribute to avoid
# non desirable side effects.
if field.meta_type == 'ProxyField':
value = field.get_recursive_orig_value(id)
else:
value = field.get_orig_value(id)
return self.returnValue(field, id, value)
class OverrideValue(StaticValue):
def __init__(self, override):
self.override = override
def __call__(self, field, id, **kw):
return self.returnValue(field, id, self.override.__of__(field)())
class DefaultValue(StaticValue):
def __init__(self, field_id, value):
self.key = field_id.split('_', 1)[1]
self.value = value
def __call__(self, field, id, **kw):
REQUEST = kw.get('REQUEST', None) or get_request()
try:
form = field.aq_parent
ob = REQUEST.get('cell', getattr(form, 'aq_parent', None))
value = self.value
try:
if value not in (None, ''):
# If a default value is defined on the field, it has precedence
value = ob.getProperty(self.key, d=value)
else:
# else we should give a chance to the accessor to provide
# a default value (including None)
value = ob.getProperty(self.key)
except Unauthorized:
value = ob.getProperty(self.key, d=value, checked_permission='View')
if REQUEST is not None:
REQUEST.set('read_only_%s' % self.key, 1)
except (KeyError, AttributeError):
value = None
return self.returnValue(field, id, value)
class DefaultCheckBoxValue(DefaultValue):
def __call__(self, field, id, **kw):
try:
form = field.aq_parent
ob = getattr(form, 'aq_parent', None)
value = self.value
try:
value = ob.getProperty(self.key)
except Unauthorized:
value = ob.getProperty(self.key, d=value, checked_permission='View')
REQUEST = kw.get('REQUEST', get_request())
if REQUEST is not None:
REQUEST.set('read_only_%s' % self.key, 1)
except (KeyError, AttributeError):
value = None
return self.returnValue(field, id, value)
class EditableValue(StaticValue):
def __call__(self, field, id, **kw):
# By default, pages are editable and
# fields are editable if they are set to editable mode
# However, if the REQUEST defines editable_mode to 0
# then all fields become read only.
# This is useful to render ERP5 content as in a web site (ECommerce)
# editable_mode should be set for example by the page template
# which defines the current layout
REQUEST = kw.get('REQUEST', get_request())
if REQUEST is not None:
if not REQUEST.get('editable_mode', 1):
return 0
return self.value
def getFieldValue(self, field, id, **kw):
"""
Return a callable expression and cacheable boolean flag
"""
tales_expr = self.tales.get(id, "")
if tales_expr:
# TALESMethod is persistent object, so that we cannot cache original one.
# Becase if connection which original talesmethod uses is closed,
# RuntimeError must occurs in __setstate__.
tales_expr = copyMethod(tales_expr)
return TALESValue(tales_expr), isCacheable(tales_expr)
override = self.overrides.get(id, "")
if override:
override = copyMethod(override)
return OverrideValue(override), isCacheable(override)
# Get a normal value.
value = self.get_orig_value(id)
value = copyMethod(value)
cacheable = isCacheable(value)
field_id = field.id
if id == 'default' and (field_id.startswith('my_') or
field_id.startswith('listbox_')):
if field.meta_type == 'ProxyField' and \
field.getRecursiveTemplateField().meta_type == 'CheckBoxField' or \
self.meta_type == 'CheckBoxField':
return DefaultCheckBoxValue(field_id, value), cacheable
return DefaultValue(field_id, value), cacheable
# For the 'editable' value, we try to get a default value
if id == 'editable':
return EditableValue(value), cacheable
# Return default value in callable mode
if callable(value):
return StaticValue(value), cacheable
# Return default value in non callable mode
return_value = StaticValue(value)(field, id, **kw)
return return_value, isCacheable(return_value)
def get_value(self, id, REQUEST=None, **kw):
if REQUEST is None:
REQUEST = get_request()
if REQUEST is not None:
field = REQUEST.get(
'field__proxyfield_%s_%s_%s' % (self.id, self._p_oid, id),
self)
else:
field = self
cache_id = ('Form.get_value',
self._p_oid,
field._p_oid,
id)
try:
value = field_value_cache[cache_id]
except KeyError:
# either returns non callable value (ex. "Title")
# or a FieldValue instance of appropriate class
value, cacheable = getFieldValue(self, field, id, **kw)
# Do not cache if the field is not stored in zodb,
# because such field must be used for editing field in ZMI
# and caching sometimes break these field settings at initialization.
# As the result, we would see broken field editing screen in ZMI.
if cacheable and self._p_oid:
field_value_cache[cache_id] = value
if callable(value):
return value(field, id, REQUEST=REQUEST, **kw)
return value
psyco.bind(get_value)
def om_icons(self):
"""Return a list of icon URLs to be displayed by an ObjectManager"""
icons = ({'path': self.icon,
'alt': self.meta_type, 'title': self.meta_type},)
return icons
def _get_default(self, key, value, REQUEST):
if value is not None:
return value
try:
value = self._get_user_input_value(key, REQUEST)
except (KeyError, AttributeError):
# fall back on default
return self.get_value('default', REQUEST=REQUEST) # It was missing on Formulator
# if we enter a string value while the field expects unicode,
# convert to unicode first
# this solves a problem when re-rendering a sticky form with
# values from request
if (self.has_value('unicode') and self.get_value('unicode') and
type(value) == type('')):
return unicode(value, self.get_form_encoding())
else:
return value
# Dynamic Patch
Field.get_value = get_value
Field._get_default = _get_default
Field.om_icons = om_icons
# Constructors
manage_addForm = DTMLFile("dtml/form_add", globals())
def addERP5Form(self, id, title="", REQUEST=None):
"""Add form to folder.
id -- the id of the new form to add
title -- the title of the form to add
Result -- empty string
"""
# add actual object
type_info = self.getPortalObject().portal_types.getTypeInfo('ERP5 Form')
type_info.constructInstance(container=self, id=id, title=title)
# respond to the add_and_edit button if necessary
add_and_edit(self, id, REQUEST)
return ''
def add_and_edit(self, id, REQUEST):
"""Helper method to point to the object's management screen if
'Add and Edit' button is pressed.
id -- id of the object we just added
"""
if REQUEST is None:
return
try:
u = self.DestinationURL()
except AttributeError:
u = REQUEST['URL1']
if REQUEST['submit'] == " Add and Edit ":
u = "%s/%s" % (u, quote(id))
REQUEST.RESPONSE.redirect(u+'/manage_main')
def initializeForm(field_registry, form_class=None):
"""Sets up ZMIForm with fields from field_registry.
"""
if form_class is None: form_class = ERP5Form
meta_types = []
for meta_type, field in field_registry.get_field_classes().items():
# don't set up in form if this is a field for internal use only
if field.internal_field:
continue
# set up individual add dictionaries for meta_types
dict = { 'name': field.meta_type,
'permission': 'Add Formulator Fields',
'action':
'manage_addProduct/Formulator/manage_add%sForm' % meta_type }
meta_types.append(dict)
# set up add method
setattr(form_class,
'manage_add%sForm' % meta_type,
DTMLFile('dtml/fieldAdd', globals(), fieldname=meta_type))
# set up meta_types that can be added to form
form_class._meta_types = tuple(meta_types)
# set up settings form
form_class.settings_form._realize_fields()
# Special Settings
def create_settings_form():
"""Create settings form for ZMIForm.
"""
form = BasicForm('manage_settings')
title = fields.StringField('title',
title="Title",
required=0,
default="")
description = fields.TextAreaField('description',
title="Description",
required=0,
default="")
row_length = fields.IntegerField('row_length',
title='Number of groups in row (in order tab)',
required=1,
default=4)
name = fields.StringField('name',
title="Form name",
required=0,
default="")
pt = fields.StringField('pt',
title="Page Template",
required=0,
default="")
action = fields.StringField('action',
title='Form action',
required=0,
default="")
action_title = fields.StringField('action_title',
title="Action Title",
required=0,
default="")
update_action = fields.StringField('update_action',
title='Form update action',
required=0,
default="")
update_action_title = fields.StringField('update_action_title',
title="Update Action Title",
required=0,
default="")
method = fields.ListField('method',
title='Form method',
items=[('POST', 'POST'),
('GET', 'GET')],
required=1,
size=1,
default='POST')
enctype = fields.ListField('enctype',
title='Form enctype',
items=[('No enctype', ""),
('application/x-www-form-urlencoded',
'application/x-www-form-urlencoded'),
('multipart/form-data',
'multipart/form-data')],
required=0,
size=1,
default=None)
encoding = fields.StringField('encoding',
title='Encoding of pages the form is in',
default="UTF-8",
required=1)
stored_encoding = fields.StringField('stored_encoding',
title='Encoding of form properties',
default='UTF-8',
required=1)
unicode_mode = fields.CheckBoxField('unicode_mode',
title='Form properties are unicode',
default=0,
required=0)
edit_order = fields.LinesField('edit_order',
title='Setters for these properties should be'
'<br /> called by edit() in the defined order')
form.add_fields([title, description, row_length, name, pt, action, action_title, update_action, update_action_title,
method, enctype, encoding, stored_encoding, unicode_mode, edit_order])
return form
from OFS.Cache import filterCacheTab
class ERP5Form(Base, ZMIForm, ZopePageTemplate):
"""
A Formulator form with a built-in rendering parameter based
on page templates or DTML.
"""
meta_type = "ERP5 Form"
portal_type = "ERP5 Form"
icon = "www/Form.png"
# Declarative Security
security = ClassSecurityInfo()
# Tabs in ZMI
manage_options = (ZMIForm.manage_options[:5] +
({'label':'Proxify', 'action':'formProxify'},
{'label':'UnProxify', 'action':'formUnProxify'},
{'label':'RelatedProxy',
'action':'formShowRelatedProxyFields'},
{'label': 'Cache',
'action': 'ZCacheable_manage',
'filter': filterCacheTab,
'help': ('OFSP', 'Cacheable-properties.stx')}
)+
ZMIForm.manage_options[5:])
# Declarative properties
property_sheets = ( PropertySheet.Base
, PropertySheet.SimpleItem
, PropertySheet.Folder
, PropertySheet.CategoryCore
)
# Constructors
constructors = (manage_addForm, addERP5Form)
# This is a patched dtml formOrder
security.declareProtected('View management screens', 'formOrder')
formOrder = DTMLFile('dtml/formOrder', globals())
# Proxify form
security.declareProtected('View management screens', 'formProxify')
formProxify = DTMLFile('dtml/formProxify', globals())
# Proxify form
security.declareProtected('View management screens', 'formUnProxify')
formUnProxify = DTMLFile('dtml/formUnProxify', globals())
# Related Proxy Fields
security.declareProtected('View management screens',
'formShowRelatedProxyFields')
formShowRelatedProxyFields = DTMLFile('dtml/formShowRelatedProxyFields',
globals())
# Default Attributes
pt = 'form_view'
action_title = ''
update_action = ''
update_action_title = ''
edit_order = []
# Special Settings
settings_form = create_settings_form()
manage_main = ZMIForm.manage_main
objectIds = ZMIForm.objectIds
objectItems = ZMIForm.objectItems
objectValues = ZMIForm.objectValues
# If content_type is not text/html ZopePageTemplate will check that the
# source is well formed XML, but this does not really applies to Forms,
# they don't have source. By setting content_type here we make sure we
# don't get ERP5Type's Base default content_type.
content_type = ZopePageTemplate.content_type
def __init__(self, id, title='', unicode_mode=0, encoding='UTF-8',
stored_encoding='UTF-8'):
"""Initialize form.
id -- id of form
title -- the title of the form
"""
ZMIForm.inheritedAttribute('__init__')(self, "", "POST", "", id,
encoding, stored_encoding,
unicode_mode)
self.id = id
self.title = title
self.row_length = 4
self.group_list = ["left", "right", "center", "bottom", "hidden"]
groups = {}
for group in self.group_list:
groups[group] = []
self.groups = groups
# Proxy method to PageTemplate
def __call__(self, *args, **kwargs):
# Security
#
# The minimal action consists in checking that
# we have View permission on the current object
# before rendering a form. Otherwise, object with
# AccessContentInformation can be viewed by invoking
# a form directly.
#
# What would be better is to prevent calling certain
# forms to render objects. This can not be done
# through actions since we are using sometimes forms
# to render the results of a report dialog form.
# An a appropriate solutions could consist in adding
# a permission field to the form. Another solutions
# is the use of REFERER in the rendering process.
#
# Both solutions are not perfect if the goal is, for
# example, to prevent displaying private information of
# staff. The only real solution is to use a special
# permission (ex. AccessPrivateInformation) for those
# properties which are sensitive.
kwargs.setdefault('args', args)
key_prefix = kwargs.pop('key_prefix', None)
obj = getattr(self, 'aq_parent', None)
if obj is not None:
container = obj.aq_inner.aq_parent
if not _checkPermission(Permissions.View, obj):
raise AccessControl_Unauthorized('This document is not authorized for view.')
else:
container = None
pt = getattr(self,self.pt)
extra_context = dict( container=container,
template=self,
form=self,
key_prefix=key_prefix,
options=kwargs,
here=obj,
context=obj,
)
return pt.pt_render(extra_context=extra_context)
def _exec(self, bound_names, args, kw):
pt = getattr(self,self.pt)
return pt._exec(self, bound_names, args, kw)
def manage_renameObject(self, id, new_id, REQUEST=None):
# overriden to keep the order of a field after rename
groups = deepcopy(self.groups)
ret = ZMIForm.manage_renameObject(self, id, new_id, REQUEST=REQUEST)
for group_id, field_id_list in groups.items():
if id in field_id_list:
index = field_id_list.index(id)
field_id_list.pop(index)
field_id_list.insert(index, new_id)
groups[group_id] = field_id_list
self.groups = groups
return ret
# Utilities
security.declareProtected('View', 'ErrorFields')
def ErrorFields(self, validation_errors):
"""
Create a dictionnary of validation_errors
with field id as key
"""
ef = {}
for e in validation_errors.errors:
ef[e.field_id] = e
return ef
def om_icons(self):
"""Return a list of icon URLs to be displayed by an ObjectManager"""
icons = ({'path': 'misc_/ERP5Form/Form.png',
'alt': self.meta_type, 'title': self.meta_type},)
return icons
# Pached validate_all to support ListBox validation
security.declareProtected('View', 'validate_all')
def validate_all(self, REQUEST, key_prefix=None):
"""Validate all enabled fields in this form, catch any ValidationErrors
if they occur and raise a FormValidationError in the end if any
Validation Errors occured.
"""
result = {}
errors = []
for group in self.get_groups():
if group.lower() == 'hidden':
continue
for field in self.get_fields_in_group(group):
# skip any field we don't need to validate
if not field.need_validate(REQUEST, key_prefix=key_prefix):
continue
if not (field.get_value('editable',REQUEST=REQUEST)):
continue
try:
value = field.validate(REQUEST, key_prefix=key_prefix)
# store under id
result[field.id] = value
# store as alternate name as well if necessary
alternate_name = field.get_value('alternate_name')
if alternate_name:
result[alternate_name] = value
except FormValidationError, e: # XXX JPS Patch for listbox
errors.extend(e.errors)
result.update(e.result)
except ValidationError, err:
errors.append(err)
except KeyError, err:
LOG('ERP5Form/Form.py:validate_all', 0, 'KeyError : %s' % (err, ))
if len(errors) > 0:
raise FormValidationError(errors, result)
return result
security.declareProtected('View', 'hash_validated_data')
def hash_validated_data(self, validated_data):
return hashlib.sha256(
"".join(
str(validated_data[key])
for key in sorted(validated_data.keys())
if isinstance(validated_data[key], (str, unicode, int, long, float, DateTime)))
).hexdigest()
# FTP/DAV Access
manage_FTPget = ZMIForm.get_xml
def PUT(self, REQUEST, RESPONSE):
"""Handle HTTP PUT requests."""
self.dav__init(REQUEST, RESPONSE)
self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)
if REQUEST.environ['REQUEST_METHOD'] != 'PUT':
raise Forbidden, 'REQUEST_METHOD should be PUT.'
body=REQUEST.get('BODY', '')
# Empty the form (XMLToForm is unable to empty things before reopening)
for k in self.get_field_ids():
try:
self._delObject(k)
except AttributeError:
pass
self.groups = {}
self.group_list = []
# And reimport
XMLToForm(body, self)
self.ZCacheable_invalidate()
RESPONSE.setStatus(204)
return RESPONSE
manage_FTPput = PUT
security.declarePrivate('getSimilarSkinFolderIdList')
def getSimilarSkinFolderIdList(self):
"""
Find other skins id installed in the same time
"""
portal = self.getPortalObject()
folder_id = self.aq_parent.id
# Find a business template which manages the context skin folder.
folder_id_set = {folder_id}
for template in portal.portal_templates.getInstalledBusinessTemplateList():
template_skin_id_list = template.getTemplateSkinIdList()
if folder_id in template_skin_id_list:
folder_id_set.update(template_skin_id_list)
# Find folders which can be surcharged by this skin folder
if '_' in folder_id:
surcharged_folder_id = 'erp5_%s' % folder_id.split('_')[-1]
if (surcharged_folder_id != folder_id) and \
(getattr(portal.portal_skins, surcharged_folder_id, None) \
is not None):
folder_id_set.add(surcharged_folder_id)
break
return list(folder_id_set)
#Methods for Proxify tab.
security.declareProtected('View management screens', 'getFormFieldList')
def getFormFieldList(self):
"""
find fields and forms which name ends with 'FieldLibrary' in
the same business template or in erp5_core.
"""
form_list = []
def iterate(obj):
for i in obj.objectValues():
if (i.meta_type=='ERP5 Form' and
i.id.startswith('Base_view') and
i.id.endswith('FieldLibrary') and
'_view' in i.getId()):
form_id = i.getId()
form_path = '%s.%s' % (obj.getId(), form_id)
field_list = []
form_list.append({'form_path':form_path,
'form_id':form_id,
'field_list':field_list})
for field in i.objectValues():
field_type, proxy_flag = get_field_meta_type_and_proxy_flag(field)
if proxy_flag:
field_type = '%s(Proxy)' % field_type
field_list.append({'field_object':field,
'field_type':field_type,
'proxy_flag':proxy_flag})
if i.meta_type=='Folder':
iterate(i)
skins_tool = self.portal_skins
folder_id = self.aq_parent.id
# for skin_folder_id in self.getSimilarSkinFolderIdList():
for skin_folder_id in self.getPortalObject().portal_skins.objectIds():
iterate(getattr(skins_tool, skin_folder_id))
iterate(skins_tool.erp5_core)
return form_list
security.declareProtected('View management screens', 'getProxyableFieldList')
def getProxyableFieldList(self, field, form_field_list=None):
""""""
def extract_keyword(name):
keyword_list = [i for i in name.split('_') if not i in \
('my', 'default', 'listbox', 'your')]
if len(keyword_list) == 0:
# This means that the name is one of the exception keywords,
# so we have to keep it
keyword_list = [name]
return keyword_list
def check_keyword_list(name, keyword_list):
count = 0
for i in keyword_list:
if i in name:
count += 1
return count/float(len(keyword_list))
def match(field_data):
if not field_data['field_type'].startswith(field.meta_type):
return 0
field_object = field_data['field_object']
if field_object.aq_base is field.aq_base:
return 0
field_id = field_object.getId()
# All proxy fields in field libraries should define their
# technical context
# XXX Theses 3 following lines will need to be uncommented
# as soon as proxy guideline is fully validated on erp5_trade
#if field.meta_type == 'ProxyField' and \
# re.match('my_.*_mode', field_id) is None:
# return 0
# XXX keyword match is not useful anymore.Need different approach.
keyword_match_rate = check_keyword_list(field_id, extract_keyword(id_))
if keyword_match_rate>0.3:
return keyword_match_rate
else:
def split(string):
result = []
temporary = []
for char in string:
if char.isupper():
if temporary:
result.append(''.join(temporary))
temporary = []
temporary.append(char)
result.append(''.join(temporary))
return result
if ''.join(field_id.split('_')[1:]).startswith(
split(field.meta_type)[0].lower()):
# At least it seems a generic template field of the meta_type.
return 0.1
def make_dict_list_append_function(dic, order_list):
def append(key, item):
if not key in order_list:
order_list.append(key)
dic[key] = []
dic[key].append(item)
return append
def add_default_field_library():
portal_url = getToolByName(self, 'portal_url')
portal = portal_url.getPortalObject()
portal_skins = getToolByName(self, 'portal_skins')
default_field_library_path = portal.getProperty(
'erp5_default_field_library_path',
'erp5_core.Base_viewFieldLibrary')
if (not default_field_library_path or
len(default_field_library_path.split('.'))!=2):
return
skinfolder_id, form_id = default_field_library_path.split('.')
skinfolder = getattr(portal_skins, skinfolder_id, None)
default_field_library = getattr(skinfolder, form_id, None)
if default_field_library is None:
return
for i in default_field_library.objectValues():
field_meta_type, proxy_flag = get_field_meta_type_and_proxy_flag(i)
if meta_type==field_meta_type:
if proxy_flag:
field_meta_type = '%s(Proxy)' % field_meta_type
matched_item = {'form_id':form_id,
'field_type':field_meta_type,
'field_object':i,
'proxy_flag':proxy_flag,
'matched_rate':0
}
if not i in [item['field_object']
for item in matched.get(default_field_library_path, ())]:
matched_append(default_field_library_path, matched_item)
if not i in [item['field_object']
for item in perfect_matched.get(default_field_library_path, ())]:
perfect_matched_append(default_field_library_path, matched_item)
id_ = field.getId()
meta_type = field.meta_type
matched = {}
form_order = []
matched_append = make_dict_list_append_function(matched, form_order)
perfect_matched = {}
perfect_matched_form_order = []
perfect_matched_append = make_dict_list_append_function(perfect_matched, perfect_matched_form_order)
if form_field_list is None:
form_field_list = self.getFormFieldList()
for i in form_field_list:
for data in i['field_list']:
tmp = []
matched_rate = match(data)
if matched_rate>0:
form_path = i['form_path']
form_id = i['form_id']
field_type = data['field_type']
field_object = data['field_object']
proxy_flag = data['proxy_flag']
matched_item = {'form_id':form_id,
'field_type':field_type,
'field_object':field_object,
'proxy_flag':proxy_flag,
'matched_rate':matched_rate
}
if matched_rate==1:
perfect_matched_append(form_path, matched_item)
elif not perfect_matched:
matched_append(form_path, matched_item)
if perfect_matched:
perfect_matched_form_order.sort()
add_default_field_library()
return perfect_matched_form_order, perfect_matched
form_order.sort()
add_default_field_library()
return form_order, matched
security.declareProtected('View management screens', 'getUnProxyableFieldList')
def getUnProxyableFieldList(self):
"""
Return ProxyFields
"""
return sorted([f for f in self.objectValues() \
if f.meta_type == 'ProxyField'], key = lambda x: x.id)
security.declareProtected('View management screens',
'getRelatedProxyFieldDictList')
def getRelatedProxyFieldDictList(self, **kw):
"""
Retrieve all proxy using proxy in this form
"""
form_id = self.id
proxy_dict = {}
for document in self.objectValues():
short_path = "%s.%s" % (form_id, document.id)
proxy_dict[short_path] = {'proxy': document,
'short_path': short_path,
'related_proxy_list': []}
def iterate(document):
for i in document.objectValues():
if i.meta_type == 'ERP5 Form':
for field in i.objectValues():
if field.meta_type == 'ProxyField':
key = "%s.%s" % (field.get_value('form_id'),
field.get_value('field_id'))
if proxy_dict.has_key(key):
proxy_dict[key]['related_proxy_list'].append(
{'short_path': "%s.%s" % \
(field.aq_parent.id, field.id),
'proxy': field})
if i.meta_type == 'Folder':
iterate(i)
skins_tool = self.portal_skins
proxy_dict_list = []
if len(proxy_dict):
# for skin_folder_id in self.getSimilarSkinFolderIdList():
for skin_folder_id in self.getPortalObject().portal_skins.objectIds():
iterate(getattr(skins_tool, skin_folder_id))
proxy_dict_list = proxy_dict.values()
proxy_dict_list.sort(key=lambda x: x['short_path'])
for item in proxy_dict_list:
item['related_proxy_list'].sort(key=lambda x: x['short_path'])
return proxy_dict_list
_proxy_copy_type_list = (bytes, unicode, int, long, float, bool, list,
tuple, dict, DateTime)
security.declareProtected('Change Formulator Forms', 'proxifyField')
def proxifyField(self, field_dict=None, force_delegate=False,
keep_empty_value=False, REQUEST=None):
"""Convert fields to proxy fields
If the field value is not empty and different from the proxyfield
value, the value is kept on the proxyfield, otherwise it is delegated.
If you specify force_delegate, values will be delegated even if they
are different. And if you specify keep_empty_value, then empty values
will not be delegated(force_delegate option is high priority).
"""
def copy(field, value_type):
new_dict = {}
for key, value in getFieldDict(field, value_type).iteritems():
if isinstance(aq_base(value), (Method, TALESMethod)):
value = copyMethod(value)
elif not (value is None or
isinstance(value, self._proxy_copy_type_list)):
raise ValueError('%s:%r' % (type(value), value))
elif not (keep_empty_value or value):
continue
new_dict[key] = value
return new_dict
def is_equal(a, b):
type_a = type(a)
type_b = type(b)
if type_a is not type_b:
return False
elif type_a is Method:
return a.method_name==b.method_name
elif type_a is TALESMethod:
return a._text==b._text
else:
return a==b
def remove_same_value(new_dict, target_dict):
for key, value in new_dict.items():
target_value = target_dict.get(key)
if force_delegate or is_equal(value, target_value):
del new_dict[key]
return new_dict
def get_group_and_position(field_id):
for i in self.groups.keys():
if field_id in self.groups[i]:
return i, self.groups[i].index(field_id)
def set_group_and_position(group, position, field_id):
self.field_removed(field_id)
self.groups[group].insert(position, field_id)
# Notify changes explicitly.
self.groups = self.groups
if field_dict is None:
return
for field_id in field_dict.keys():
target = field_dict[field_id]
target_form_id, target_field_id = target.split('.')
# keep current group and position.
group, position = get_group_and_position(field_id)
# create proxy field
old_field = getattr(self, field_id)
self.manage_delObjects(field_id)
self.manage_addField(id=field_id, title='', fieldname='ProxyField')
proxy_field = getattr(self, field_id)
proxy_field.values['form_id'] = target_form_id
proxy_field.values['field_id'] = target_field_id
target_field = proxy_field.getTemplateField()
if target_field is None:
raise ValueError("Unable to find template : %s.%s" % (
target_form_id, target_field_id))
# copy data
new_values = remove_same_value(copy(old_field, 'values'),
getFieldDict(target_field, 'values'))
new_tales = remove_same_value(copy(old_field, 'tales'),
getFieldDict(target_field, 'tales'))
if target_field.meta_type=='ProxyField':
for i in new_values.keys():
if not i in target_field.delegated_list:
# obsolete variable check
try:
target_field.get_recursive_orig_value(i)
except KeyError:
# then `i` is obsolete!
del new_values[i]
else:
if is_equal(target_field.get_recursive_orig_value(i),
new_values[i]):
del new_values[i]
for i in new_tales.keys():
if not i in target_field.delegated_list:
# obsolete variable check
try:
target_field.get_recursive_tales(i)
except KeyError:
# then `i` is obsolete!
del new_tales[i]
else:
if is_equal(target_field.get_recursive_tales(i),
new_tales[i]):
del new_tales[i]
delegated_list = []
for i in (new_values.keys()+new_tales.keys()):
if not i in delegated_list:
delegated_list.append(i)
proxy_field.values.update(new_values)
proxy_field.tales.update(new_tales)
proxy_field.delegated_list = delegated_list
# move back to the original group and position.
set_group_and_position(group, position, field_id)
if REQUEST is not None:
return self.formProxify(manage_tabs_message='Changed')
psyco.bind(__call__)
psyco.bind(_exec)
security.declareProtected('Change Formulator Forms', 'unProxifyField')
def unProxifyField(self, field_dict=None, copy_delegated_values=False,
REQUEST=None):
"""
Convert proxy fields to fields
"""
def copy(field, value_type):
new_dict = {}
for key, value in getFieldDict(field, value_type).iteritems():
if isinstance(aq_base(value), (Method, TALESMethod)):
value = copyMethod(value)
elif not (value is None or
isinstance(value, self._proxy_copy_type_list)):
raise ValueError('%s:%r' % (type(value), value))
new_dict[key] = value
return new_dict
def is_equal(a, b):
type_a = type(a)
type_b = type(b)
if type_a is not type_b:
return False
elif type_a is Method:
return a.method_name==b.method_name
elif type_a is TALESMethod:
return a._text==b._text
else:
return a==b
def remove_same_value(new_dict, target_dict):
for key, value in new_dict.items():
target_value = target_dict.get(key)
if is_equal(value, target_value):
del new_dict[key]
return new_dict
def get_group_and_position(field_id):
for i in self.groups.keys():
if field_id in self.groups[i]:
return i, self.groups[i].index(field_id)
def set_group_and_position(group, position, field_id):
self.field_removed(field_id)
self.groups[group].insert(position, field_id)
# Notify changes explicitly.
self.groups = self.groups
if field_dict is None:
return
for field_id in field_dict.keys():
# keep current group and position.
group, position = get_group_and_position(field_id)
# create field
old_proxy_field = getattr(self, field_id)
delegated_field = old_proxy_field.getRecursiveTemplateField()
if delegated_field is None:
break
self.manage_delObjects(field_id)
self.manage_addField(id=field_id,
title='',
fieldname=delegated_field.meta_type)
field = getattr(self, field_id)
# copy data
new_values = remove_same_value(copy(old_proxy_field, 'values'),
field.values)
new_tales = remove_same_value(copy(old_proxy_field, 'tales'),
field.tales)
field.values.update(new_values)
field.tales.update(new_tales)
# move back to the original group and position.
set_group_and_position(group, position, field_id)
if REQUEST is not None:
return self.formUnProxify(manage_tabs_message='Changed')
# Overload of the Form method
# Use the include_disabled parameter since
# we should consider all fields to render the group tab
# moreoever, listbox rendering fails whenever enabled
# is based on the cell parameter.
security.declareProtected('View', 'get_largest_group_length')
def get_largest_group_length(self):
"""Get the largest group length available; necessary for
'order' screen user interface.
XXX - Copyright issue
"""
max = 0
for group in self.get_groups(include_empty=1):
fields = self.get_fields_in_group(group, include_disabled=1)
if len(fields) > max:
max = len(fields)
return max
security.declareProtected('View', 'get_groups')
def get_groups(self, include_empty=0):
"""Get a list of all groups, in display order.
If include_empty is false, suppress groups that do not have
enabled fields.
XXX - Copyright issue
"""
if include_empty:
return self.group_list
return [group for group in self.group_list
if self.get_fields_in_group(group, include_disabled=1)]
# Find support in ZMI. This is useful for development.
def PrincipiaSearchSource(self):
return str((self.pt, self.name, self.action, self.update_action,
self.encoding, self.stored_encoding, self.enctype))
# utility function
def get_field_meta_type_and_proxy_flag(field):
if field.meta_type=='ProxyField':
try:
return field.getRecursiveTemplateField().meta_type, True
except AttributeError:
raise AttributeError, 'The proxy target of %s.%s field does not '\
'exists. Please check the field setting.' % \
(field.aq_parent.id, field.getId())
else:
return field.meta_type, False
# More optimizations
#psyco.bind(ERP5Field)
# XXX Not useful, as we patch those methods in FormulatorPatch
psyco.bind(Field.render)
psyco.bind(Field._render_helper)
psyco.bind(Field.get_value)
#from Products.PageTemplates.PageTemplate import PageTemplate
#from TAL import TALInterpreter
#psyco.bind(TALInterpreter.TALInterpreter)
#psyco.bind(TALInterpreter.TALInterpreter.interpret)
#psyco.bind(PageTemplate.pt_render)
#psyco.bind(PageTemplate.pt_macros)
#from Products.CMFCore.ActionsTool import ActionsTool
#psyco.bind(ActionsTool.listFilteredActionsFor)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Module Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>Form</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value> <string>Products.ERP5Form.Form</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>module.erp5.Form</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Module Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple>
<string>W:116, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:117, 0: Bad indentation. Found 6 spaces, expected 4 (bad-indentation)</string>
<string>W:118, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:119, 0: Bad indentation. Found 6 spaces, expected 4 (bad-indentation)</string>
<string>W:120, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:123, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:124, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:125, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:126, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:127, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:128, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:129, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:130, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:131, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:132, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:133, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:134, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:135, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:136, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:137, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:138, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:139, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:140, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:141, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:142, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:143, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:192, 0: Bad indentation. Found 8 spaces, expected 6 (bad-indentation)</string>
<string>W:194, 0: Bad indentation. Found 8 spaces, expected 6 (bad-indentation)</string>
<string>W:224, 0: Bad indentation. Found 8 spaces, expected 6 (bad-indentation)</string>
<string>W:399, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:400, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:402, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:406, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:407, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:408, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:409, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:410, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:412, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:418, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:420, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:421, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:422, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:435, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:441, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:442, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:444, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:445, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:448, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:452, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:453, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:454, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:455, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:456, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:457, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:458, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:459, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:460, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:463, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:465, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:467, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:468, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:470, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:471, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:474, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:478, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:480, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:485, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:488, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:493, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:495, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:497, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:501, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:505, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:509, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:513, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:517, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:521, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:525, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:529, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:533, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:540, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:550, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:554, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:558, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:562, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:566, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:568, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:574, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:578, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:579, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:580, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:583, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:586, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:599, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:606, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:609, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:610, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:613, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:614, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:617, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:618, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:621, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:623, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:627, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:628, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:629, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:630, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:631, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:634, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:636, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:637, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:638, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:639, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:645, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:647, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:649, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:653, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:656, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:657, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:658, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:659, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:660, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:661, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:662, 0: Bad indentation. Found 10 spaces, expected 6 (bad-indentation)</string>
<string>W:663, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:666, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:688, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:689, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:690, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:691, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:692, 0: Bad indentation. Found 10 spaces, expected 6 (bad-indentation)</string>
<string>W:693, 0: Bad indentation. Found 10 spaces, expected 6 (bad-indentation)</string>
<string>W:694, 0: Bad indentation. Found 12 spaces, expected 8 (bad-indentation)</string>
<string>W:695, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:696, 0: Bad indentation. Found 10 spaces, expected 6 (bad-indentation)</string>
<string>W:697, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:698, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:706, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:708, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:709, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:710, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:712, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:714, 0: Bad indentation. Found 6 spaces, expected 4 (bad-indentation)</string>
<string>W:715, 0: Bad indentation. Found 6 spaces, expected 4 (bad-indentation)</string>
<string>W:716, 0: Bad indentation. Found 6 spaces, expected 4 (bad-indentation)</string>
<string>W:717, 0: Bad indentation. Found 8 spaces, expected 6 (bad-indentation)</string>
<string>W:718, 0: Bad indentation. Found 10 spaces, expected 8 (bad-indentation)</string>
<string>W:719, 0: Bad indentation. Found 10 spaces, expected 8 (bad-indentation)</string>
<string>W:720, 0: Bad indentation. Found 10 spaces, expected 8 (bad-indentation)</string>
<string>W:721, 0: Bad indentation. Found 10 spaces, expected 8 (bad-indentation)</string>
<string>W:722, 0: Bad indentation. Found 6 spaces, expected 4 (bad-indentation)</string>
<string>W:723, 0: Bad indentation. Found 6 spaces, expected 4 (bad-indentation)</string>
<string>W:726, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:727, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:728, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:732, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:733, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:734, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:735, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:737, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:738, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:739, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:741, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:744, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:745, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:746, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:750, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:751, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:752, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:753, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:754, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:755, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:757, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:758, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:759, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:760, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:761, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:762, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:764, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:766, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:767, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:768, 0: Bad indentation. Found 24 spaces, expected 12 (bad-indentation)</string>
<string>W:769, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:770, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:771, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:772, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:773, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:774, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:775, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:777, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:778, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:779, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:781, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:782, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:783, 0: Bad indentation. Found 6 spaces, expected 4 (bad-indentation)</string>
<string>W:791, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:793, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:794, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:795, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:796, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:797, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:798, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:799, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:801, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:802, 0: Bad indentation. Found 10 spaces, expected 6 (bad-indentation)</string>
<string>W:803, 0: Bad indentation. Found 12 spaces, expected 8 (bad-indentation)</string>
<string>W:804, 0: Bad indentation. Found 10 spaces, expected 6 (bad-indentation)</string>
<string>W:805, 0: Bad indentation. Found 12 spaces, expected 8 (bad-indentation)</string>
<string>W:806, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:807, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:809, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:810, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:811, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:812, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:814, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:816, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:817, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:818, 0: Bad indentation. Found 6 spaces, expected 4 (bad-indentation)</string>
<string>W:821, 0: Bad indentation. Found 6 spaces, expected 4 (bad-indentation)</string>
<string>W:822, 0: Bad indentation. Found 6 spaces, expected 4 (bad-indentation)</string>
<string>W:824, 0: Bad indentation. Found 6 spaces, expected 4 (bad-indentation)</string>
<string>W:825, 0: Bad indentation. Found 6 spaces, expected 4 (bad-indentation)</string>
<string>W:826, 0: Bad indentation. Found 8 spaces, expected 6 (bad-indentation)</string>
<string>W:827, 0: Bad indentation. Found 8 spaces, expected 6 (bad-indentation)</string>
<string>W:828, 0: Bad indentation. Found 10 spaces, expected 8 (bad-indentation)</string>
<string>W:831, 0: Bad indentation. Found 10 spaces, expected 8 (bad-indentation)</string>
<string>W:832, 0: Bad indentation. Found 12 spaces, expected 10 (bad-indentation)</string>
<string>W:833, 0: Bad indentation. Found 12 spaces, expected 10 (bad-indentation)</string>
<string>W:836, 0: Bad indentation. Found 14 spaces, expected 12 (bad-indentation)</string>
<string>W:838, 0: Bad indentation. Found 10 spaces, expected 8 (bad-indentation)</string>
<string>W:839, 0: Bad indentation. Found 6 spaces, expected 4 (bad-indentation)</string>
<string>W:842, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:843, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:844, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:848, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:849, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:850, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:851, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:855, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:856, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:857, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:858, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:861, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:862, 0: Bad indentation. Found 24 spaces, expected 12 (bad-indentation)</string>
<string>W:863, 0: Bad indentation. Found 24 spaces, expected 12 (bad-indentation)</string>
<string>W:864, 0: Bad indentation. Found 28 spaces, expected 14 (bad-indentation)</string>
<string>W:865, 0: Bad indentation. Found 24 spaces, expected 12 (bad-indentation)</string>
<string>W:868, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:869, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:871, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:872, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:874, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:875, 0: Bad indentation. Found 10 spaces, expected 6 (bad-indentation)</string>
<string>W:876, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:877, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:879, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:880, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:881, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:882, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:883, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:885, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:888, 0: Bad indentation. Found 14 spaces, expected 8 (bad-indentation)</string>
<string>W:889, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:891, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:892, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:893, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:894, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:895, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:896, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:898, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:899, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:900, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:901, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:902, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:903, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:904, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:913, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:914, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:915, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:916, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:917, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:918, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:919, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:920, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:921, 0: Bad indentation. Found 24 spaces, expected 12 (bad-indentation)</string>
<string>W:922, 0: Bad indentation. Found 28 spaces, expected 14 (bad-indentation)</string>
<string>W:923, 0: Bad indentation. Found 32 spaces, expected 16 (bad-indentation)</string>
<string>W:924, 0: Bad indentation. Found 28 spaces, expected 14 (bad-indentation)</string>
<string>W:925, 0: Bad indentation. Found 24 spaces, expected 12 (bad-indentation)</string>
<string>W:926, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:927, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:929, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:932, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:934, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:935, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:936, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:937, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:938, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:939, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:940, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:942, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:943, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:944, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:945, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:947, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:950, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:952, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:954, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:956, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:957, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:958, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:959, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:960, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:961, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:962, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:963, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:964, 0: Bad indentation. Found 24 spaces, expected 12 (bad-indentation)</string>
<string>W:965, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:972, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:974, 0: Bad indentation. Found 22 spaces, expected 12 (bad-indentation)</string>
<string>W:975, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:977, 0: Bad indentation. Found 22 spaces, expected 12 (bad-indentation)</string>
<string>W:979, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:980, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:982, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:983, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:984, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:986, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:987, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:988, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:990, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:991, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:993, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:994, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:995, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:996, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:997, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:998, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:999, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:1000, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:1001, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:1002, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:1004, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:1010, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:1011, 0: Bad indentation. Found 24 spaces, expected 12 (bad-indentation)</string>
<string>W:1012, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:1013, 0: Bad indentation. Found 24 spaces, expected 12 (bad-indentation)</string>
<string>W:1015, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1016, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1017, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1018, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1020, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1021, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1022, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1024, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:1025, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:1026, 0: Bad indentation. Found 6 spaces, expected 4 (bad-indentation)</string>
<string>W:1029, 0: Bad indentation. Found 6 spaces, expected 4 (bad-indentation)</string>
<string>W:1032, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:1034, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:1035, 0: Bad indentation. Found 6 spaces, expected 4 (bad-indentation)</string>
<string>W:1038, 0: Bad indentation. Found 6 spaces, expected 4 (bad-indentation)</string>
<string>W:1039, 0: Bad indentation. Found 6 spaces, expected 4 (bad-indentation)</string>
<string>W:1040, 0: Bad indentation. Found 6 spaces, expected 4 (bad-indentation)</string>
<string>W:1041, 0: Bad indentation. Found 8 spaces, expected 6 (bad-indentation)</string>
<string>W:1042, 0: Bad indentation. Found 8 spaces, expected 6 (bad-indentation)</string>
<string>W:1045, 0: Bad indentation. Found 6 spaces, expected 4 (bad-indentation)</string>
<string>W:1046, 0: Bad indentation. Found 8 spaces, expected 6 (bad-indentation)</string>
<string>W:1047, 0: Bad indentation. Found 10 spaces, expected 8 (bad-indentation)</string>
<string>W:1048, 0: Bad indentation. Found 12 spaces, expected 10 (bad-indentation)</string>
<string>W:1049, 0: Bad indentation. Found 14 spaces, expected 12 (bad-indentation)</string>
<string>W:1050, 0: Bad indentation. Found 16 spaces, expected 14 (bad-indentation)</string>
<string>W:1052, 0: Bad indentation. Found 16 spaces, expected 14 (bad-indentation)</string>
<string>W:1053, 0: Bad indentation. Found 18 spaces, expected 16 (bad-indentation)</string>
<string>W:1057, 0: Bad indentation. Found 10 spaces, expected 8 (bad-indentation)</string>
<string>W:1058, 0: Bad indentation. Found 12 spaces, expected 10 (bad-indentation)</string>
<string>W:1060, 0: Bad indentation. Found 6 spaces, expected 4 (bad-indentation)</string>
<string>W:1061, 0: Bad indentation. Found 6 spaces, expected 4 (bad-indentation)</string>
<string>W:1062, 0: Bad indentation. Found 6 spaces, expected 4 (bad-indentation)</string>
<string>W:1064, 0: Bad indentation. Found 8 spaces, expected 6 (bad-indentation)</string>
<string>W:1065, 0: Bad indentation. Found 10 spaces, expected 8 (bad-indentation)</string>
<string>W:1066, 0: Bad indentation. Found 8 spaces, expected 6 (bad-indentation)</string>
<string>W:1067, 0: Bad indentation. Found 8 spaces, expected 6 (bad-indentation)</string>
<string>W:1068, 0: Bad indentation. Found 8 spaces, expected 6 (bad-indentation)</string>
<string>W:1069, 0: Bad indentation. Found 10 spaces, expected 8 (bad-indentation)</string>
<string>W:1071, 0: Bad indentation. Found 6 spaces, expected 4 (bad-indentation)</string>
<string>W:1073, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:1076, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:1077, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:1079, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1086, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1087, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1088, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1089, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:1090, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:1091, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:1093, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:1094, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:1095, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:1096, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:1097, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1099, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1100, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1101, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1102, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1103, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:1104, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1105, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:1106, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1107, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:1108, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1109, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:1111, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1112, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1113, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:1114, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:1115, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:1116, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1118, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1119, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1120, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:1121, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:1123, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1124, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1125, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1127, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1129, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1130, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1132, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1133, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1134, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1137, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1140, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1141, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1142, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1143, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1144, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1145, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1147, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1148, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1149, 0: Bad indentation. Found 14 spaces, expected 8 (bad-indentation)</string>
<string>W:1153, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1155, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1158, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1159, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:1160, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:1162, 0: Bad indentation. Found 24 spaces, expected 12 (bad-indentation)</string>
<string>W:1163, 0: Bad indentation. Found 28 spaces, expected 14 (bad-indentation)</string>
<string>W:1164, 0: Bad indentation. Found 24 spaces, expected 12 (bad-indentation)</string>
<string>W:1166, 0: Bad indentation. Found 28 spaces, expected 14 (bad-indentation)</string>
<string>W:1167, 0: Bad indentation. Found 24 spaces, expected 12 (bad-indentation)</string>
<string>W:1168, 0: Bad indentation. Found 28 spaces, expected 14 (bad-indentation)</string>
<string>W:1170, 0: Bad indentation. Found 32 spaces, expected 16 (bad-indentation)</string>
<string>W:1171, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:1172, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:1174, 0: Bad indentation. Found 24 spaces, expected 12 (bad-indentation)</string>
<string>W:1175, 0: Bad indentation. Found 28 spaces, expected 14 (bad-indentation)</string>
<string>W:1176, 0: Bad indentation. Found 24 spaces, expected 12 (bad-indentation)</string>
<string>W:1178, 0: Bad indentation. Found 28 spaces, expected 14 (bad-indentation)</string>
<string>W:1179, 0: Bad indentation. Found 24 spaces, expected 12 (bad-indentation)</string>
<string>W:1180, 0: Bad indentation. Found 28 spaces, expected 14 (bad-indentation)</string>
<string>W:1182, 0: Bad indentation. Found 32 spaces, expected 16 (bad-indentation)</string>
<string>W:1184, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1185, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1186, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:1187, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:1188, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1189, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1190, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1193, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1195, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1196, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1198, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:1199, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:1201, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:1202, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:1204, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1207, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1208, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1209, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1210, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:1211, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:1212, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:1214, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:1215, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:1216, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1218, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1219, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1220, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1221, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1222, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:1223, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1224, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:1225, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1226, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:1227, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1228, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:1230, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1231, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1232, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:1233, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:1234, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:1235, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1237, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1238, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1239, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:1240, 0: Bad indentation. Found 20 spaces, expected 10 (bad-indentation)</string>
<string>W:1242, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1243, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1244, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1246, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1248, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1249, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1251, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1253, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1256, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1257, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1258, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1259, 0: Bad indentation. Found 14 spaces, expected 8 (bad-indentation)</string>
<string>W:1260, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1261, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1264, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1266, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1268, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1271, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1272, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1275, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1277, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1278, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1285, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:1286, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:1287, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1291, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1292, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1293, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1294, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1295, 0: Bad indentation. Found 16 spaces, expected 8 (bad-indentation)</string>
<string>W:1296, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1298, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:1299, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:1300, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1306, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1307, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1308, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1312, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:1313, 0: Bad indentation. Found 6 spaces, expected 4 (bad-indentation)</string>
<string>W:1318, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:1319, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1320, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1321, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W:1322, 0: Bad indentation. Found 12 spaces, expected 6 (bad-indentation)</string>
<string>W:1325, 0: Bad indentation. Found 4 spaces, expected 2 (bad-indentation)</string>
<string>W:1326, 0: Bad indentation. Found 8 spaces, expected 4 (bad-indentation)</string>
<string>W: 99, 5: Using type() instead of isinstance() for a typecheck. (unidiomatic-typecheck)</string>
<string>W:116, 7: Using type() instead of isinstance() for a typecheck. (unidiomatic-typecheck)</string>
<string>W:118, 9: Using type() instead of isinstance() for a typecheck. (unidiomatic-typecheck)</string>
<string>W:155, 28: Redefining built-in \'id\' (redefined-builtin)</string>
<string>W:158, 31: Redefining built-in \'id\' (redefined-builtin)</string>
<string>W:174, 2: __init__ method from base class \'StaticValue\' is not called (super-init-not-called)</string>
<string>W:177, 28: Redefining built-in \'id\' (redefined-builtin)</string>
<string>W:234, 4: No exception type(s) specified (bare-except)</string>
<string>W:254, 2: __init__ method from base class \'StaticValue\' is not called (super-init-not-called)</string>
<string>W:257, 28: Redefining built-in \'id\' (redefined-builtin)</string>
<string>W:261, 2: __init__ method from base class \'StaticValue\' is not called (super-init-not-called)</string>
<string>W:265, 28: Redefining built-in \'id\' (redefined-builtin)</string>
<string>W:288, 28: Redefining built-in \'id\' (redefined-builtin)</string>
<string>W:306, 28: Redefining built-in \'id\' (redefined-builtin)</string>
<string>W:320, 31: Redefining built-in \'id\' (redefined-builtin)</string>
<string>W:364, 20: Redefining built-in \'id\' (redefined-builtin)</string>
<string>W:419, 8: Using type() instead of isinstance() for a typecheck. (unidiomatic-typecheck)</string>
<string>W:434, 22: Redefining built-in \'id\' (redefined-builtin)</string>
<string>W:447, 23: Redefining built-in \'id\' (redefined-builtin)</string>
<string>W:474, 8: Redefining built-in \'dict\' (redefined-builtin)</string>
<string>W:647, 23: Redefining built-in \'id\' (redefined-builtin)</string>
<string>W:647, 4: __init__ method from base class \'Base\' is not called (super-init-not-called)</string>
<string>W:647, 4: __init__ method from base class \'ZMIForm\' is not called (super-init-not-called)</string>
<string>W:647, 4: __init__ method from base class \'ZopePageTemplate\' is not called (super-init-not-called)</string>
<string>W:712, 34: Redefining built-in \'id\' (redefined-builtin)</string>
<string>W:712, 4: Signature differs from overridden \'manage_renameObject\' method (signature-differs)</string>
<string>W:745, 4: Arguments number differs from overridden \'validate_all\' method (arguments-differ)</string>
<string>W:872, 8: Unused variable \'folder_id\' (unused-variable)</string>
<string>W:995, 16: Unused variable \'tmp\' (unused-variable)</string>
<string>W:1291, 8: Redefining built-in \'max\' (redefined-builtin)</string>
<string>W:1293, 12: Redefining name \'fields\' from outer scope (line 35) (redefined-outer-name)</string>
</tuple>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>draft</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -2,6 +2,7 @@ module.erp5.DateUtils
module.erp5.DiffUtils
module.erp5.ExpandPolicy
module.erp5.ExplanationCache
module.erp5.Form
module.erp5.GeneratedAmountList
module.erp5.Log
module.erp5.MovementCollectionDiff
......
from Products.ERP5Type import Globals
from AccessControl import ClassSecurityInfo
from Products.CMFCore.permissions import View, ViewManagementScreens
from Products.CMFCore.FSObject import FSObject
from Products.CMFCore.DirectoryView import registerFileExtension,\
registerMetaType
from Products.ERP5Form import _dtmldir
from Products.ERP5Form.Form import ERP5Form
from Products.Formulator.XMLToForm import XMLToForm
class ERP5FSForm(FSObject, ERP5Form):
"""FSForm."""
meta_type = 'ERP5 Filesystem Formulator Form'
manage_options = (
(
{'label':'Customize', 'action':'manage_main'},
{'label':'Test', 'action':'formTest'},
)
)
security = ClassSecurityInfo()
security.declareObjectProtected(View)
security.declareProtected(ViewManagementScreens, 'manage_main')
manage_main = Globals.DTMLFile('FSForm_customize', _dtmldir)
def __init__(self, id, filepath, fullname=None, properties=None):
FSObject.__init__(self, id, filepath, fullname, properties)
def _createZODBClone(self):
"""Create a ZODB (editable) equivalent of this object."""
type_info = self.getPortalObject().portal_types.getTypeInfo('ERP5 Form')
obj = type_info.constructInstance(container=self,
temp_object=True,
id=self.getId(),
title=self.title).aq_base
obj.set_xml(self.get_xml())
return obj
def _readFile(self, reparse):
f = open(self._filepath, 'rb')
# update the form with the xml data
try:
XMLToForm(f.read(), self)
except:
# bare except here, but I hope this is ok, as the
# exception should be reraised
# (except if the LOG raises another one ... should we be more paranoid here?)
import zLOG
zLOG.LOG('Formulator.FSForm',zLOG.ERROR,
'error reading form from file '+self._filepath)
raise
f.close()
Globals.InitializeClass(ERP5FSForm)
registerFileExtension('form', ERP5FSForm)
registerMetaType('FSForm', ERP5FSForm)
......@@ -26,31 +26,19 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import hashlib
from copy import deepcopy
from Products.Formulator.Form import BasicForm, ZMIForm
from Products.Formulator.Errors import FormValidationError, ValidationError
from Products.Formulator.Form import BasicForm
from Products.Formulator.DummyField import fields
from Products.Formulator.XMLToForm import XMLToForm
from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
from Products.CMFCore.utils import _checkPermission, getToolByName
from Products.CMFCore.exceptions import AccessControl_Unauthorized
from Products.ERP5Type import PropertySheet, Permissions
from urllib import quote
from Products.ERP5Type.Globals import DTMLFile, get_request
from AccessControl import Unauthorized, ClassSecurityInfo
from DateTime import DateTime
from AccessControl import Unauthorized
from ZODB.POSException import ConflictError
from zExceptions import Redirect
from Acquisition import aq_base
from Products.PageTemplates.Expressions import SecureModuleImporter
from zExceptions import Forbidden
from Products.ERP5Type.PsycoWrapper import psyco
from Products.ERP5Type.Base import Base
class FieldValueCacheDict(dict):
_last_sync = -1
......@@ -567,752 +555,6 @@ def create_settings_form():
method, enctype, encoding, stored_encoding, unicode_mode, edit_order])
return form
from OFS.Cache import filterCacheTab
class ERP5Form(Base, ZMIForm, ZopePageTemplate):
"""
A Formulator form with a built-in rendering parameter based
on page templates or DTML.
"""
meta_type = "ERP5 Form"
portal_type = "ERP5 Form"
icon = "www/Form.png"
# Declarative Security
security = ClassSecurityInfo()
# Tabs in ZMI
manage_options = (ZMIForm.manage_options[:5] +
({'label':'Proxify', 'action':'formProxify'},
{'label':'UnProxify', 'action':'formUnProxify'},
{'label':'RelatedProxy',
'action':'formShowRelatedProxyFields'},
{'label': 'Cache',
'action': 'ZCacheable_manage',
'filter': filterCacheTab,
'help': ('OFSP', 'Cacheable-properties.stx')}
)+
ZMIForm.manage_options[5:])
# Declarative properties
property_sheets = ( PropertySheet.Base
, PropertySheet.SimpleItem
, PropertySheet.Folder
, PropertySheet.CategoryCore
)
# Constructors
constructors = (manage_addForm, addERP5Form)
# This is a patched dtml formOrder
security.declareProtected('View management screens', 'formOrder')
formOrder = DTMLFile('dtml/formOrder', globals())
# Proxify form
security.declareProtected('View management screens', 'formProxify')
formProxify = DTMLFile('dtml/formProxify', globals())
# Proxify form
security.declareProtected('View management screens', 'formUnProxify')
formUnProxify = DTMLFile('dtml/formUnProxify', globals())
# Related Proxy Fields
security.declareProtected('View management screens',
'formShowRelatedProxyFields')
formShowRelatedProxyFields = DTMLFile('dtml/formShowRelatedProxyFields',
globals())
# Default Attributes
pt = 'form_view'
action_title = ''
update_action = ''
update_action_title = ''
edit_order = []
# Special Settings
settings_form = create_settings_form()
manage_main = ZMIForm.manage_main
objectIds = ZMIForm.objectIds
objectItems = ZMIForm.objectItems
objectValues = ZMIForm.objectValues
# If content_type is not text/html ZopePageTemplate will check that the
# source is well formed XML, but this does not really applies to Forms,
# they don't have source. By setting content_type here we make sure we
# don't get ERP5Type's Base default content_type.
content_type = ZopePageTemplate.content_type
def __init__(self, id, title='', unicode_mode=0, encoding='UTF-8',
stored_encoding='UTF-8'):
"""Initialize form.
id -- id of form
title -- the title of the form
"""
ZMIForm.inheritedAttribute('__init__')(self, "", "POST", "", id,
encoding, stored_encoding,
unicode_mode)
self.id = id
self.title = title
self.row_length = 4
self.group_list = ["left", "right", "center", "bottom", "hidden"]
groups = {}
for group in self.group_list:
groups[group] = []
self.groups = groups
# Proxy method to PageTemplate
def __call__(self, *args, **kwargs):
# Security
#
# The minimal action consists in checking that
# we have View permission on the current object
# before rendering a form. Otherwise, object with
# AccessContentInformation can be viewed by invoking
# a form directly.
#
# What would be better is to prevent calling certain
# forms to render objects. This can not be done
# through actions since we are using sometimes forms
# to render the results of a report dialog form.
# An a appropriate solutions could consist in adding
# a permission field to the form. Another solutions
# is the use of REFERER in the rendering process.
#
# Both solutions are not perfect if the goal is, for
# example, to prevent displaying private information of
# staff. The only real solution is to use a special
# permission (ex. AccessPrivateInformation) for those
# properties which are sensitive.
kwargs.setdefault('args', args)
key_prefix = kwargs.pop('key_prefix', None)
obj = getattr(self, 'aq_parent', None)
if obj is not None:
container = obj.aq_inner.aq_parent
if not _checkPermission(Permissions.View, obj):
raise AccessControl_Unauthorized('This document is not authorized for view.')
else:
container = None
pt = getattr(self,self.pt)
extra_context = dict( container=container,
template=self,
form=self,
key_prefix=key_prefix,
options=kwargs,
here=obj,
context=obj,
)
return pt.pt_render(extra_context=extra_context)
def _exec(self, bound_names, args, kw):
pt = getattr(self,self.pt)
return pt._exec(self, bound_names, args, kw)
def manage_renameObject(self, id, new_id, REQUEST=None):
# overriden to keep the order of a field after rename
groups = deepcopy(self.groups)
ret = ZMIForm.manage_renameObject(self, id, new_id, REQUEST=REQUEST)
for group_id, field_id_list in groups.items():
if id in field_id_list:
index = field_id_list.index(id)
field_id_list.pop(index)
field_id_list.insert(index, new_id)
groups[group_id] = field_id_list
self.groups = groups
return ret
# Utilities
security.declareProtected('View', 'ErrorFields')
def ErrorFields(self, validation_errors):
"""
Create a dictionnary of validation_errors
with field id as key
"""
ef = {}
for e in validation_errors.errors:
ef[e.field_id] = e
return ef
def om_icons(self):
"""Return a list of icon URLs to be displayed by an ObjectManager"""
icons = ({'path': 'misc_/ERP5Form/Form.png',
'alt': self.meta_type, 'title': self.meta_type},)
return icons
# Pached validate_all to support ListBox validation
security.declareProtected('View', 'validate_all')
def validate_all(self, REQUEST, key_prefix=None):
"""Validate all enabled fields in this form, catch any ValidationErrors
if they occur and raise a FormValidationError in the end if any
Validation Errors occured.
"""
result = {}
errors = []
for group in self.get_groups():
if group.lower() == 'hidden':
continue
for field in self.get_fields_in_group(group):
# skip any field we don't need to validate
if not field.need_validate(REQUEST, key_prefix=key_prefix):
continue
if not (field.get_value('editable',REQUEST=REQUEST)):
continue
try:
value = field.validate(REQUEST, key_prefix=key_prefix)
# store under id
result[field.id] = value
# store as alternate name as well if necessary
alternate_name = field.get_value('alternate_name')
if alternate_name:
result[alternate_name] = value
except FormValidationError, e: # XXX JPS Patch for listbox
errors.extend(e.errors)
result.update(e.result)
except ValidationError, err:
errors.append(err)
except KeyError, err:
LOG('ERP5Form/Form.py:validate_all', 0, 'KeyError : %s' % (err, ))
if len(errors) > 0:
raise FormValidationError(errors, result)
return result
security.declareProtected('View', 'hash_validated_data')
def hash_validated_data(self, validated_data):
return hashlib.sha256(
"".join(
str(validated_data[key])
for key in sorted(validated_data.keys())
if isinstance(validated_data[key], (str, unicode, int, long, float, DateTime)))
).hexdigest()
# FTP/DAV Access
manage_FTPget = ZMIForm.get_xml
def PUT(self, REQUEST, RESPONSE):
"""Handle HTTP PUT requests."""
self.dav__init(REQUEST, RESPONSE)
self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)
if REQUEST.environ['REQUEST_METHOD'] != 'PUT':
raise Forbidden, 'REQUEST_METHOD should be PUT.'
body=REQUEST.get('BODY', '')
# Empty the form (XMLToForm is unable to empty things before reopening)
for k in self.get_field_ids():
try:
self._delObject(k)
except AttributeError:
pass
self.groups = {}
self.group_list = []
# And reimport
XMLToForm(body, self)
self.ZCacheable_invalidate()
RESPONSE.setStatus(204)
return RESPONSE
manage_FTPput = PUT
security.declarePrivate('getSimilarSkinFolderIdList')
def getSimilarSkinFolderIdList(self):
"""
Find other skins id installed in the same time
"""
portal = self.getPortalObject()
folder_id = self.aq_parent.id
# Find a business template which manages the context skin folder.
folder_id_set = {folder_id}
for template in portal.portal_templates.getInstalledBusinessTemplateList():
template_skin_id_list = template.getTemplateSkinIdList()
if folder_id in template_skin_id_list:
folder_id_set.update(template_skin_id_list)
# Find folders which can be surcharged by this skin folder
if '_' in folder_id:
surcharged_folder_id = 'erp5_%s' % folder_id.split('_')[-1]
if (surcharged_folder_id != folder_id) and \
(getattr(portal.portal_skins, surcharged_folder_id, None) \
is not None):
folder_id_set.add(surcharged_folder_id)
break
return list(folder_id_set)
#Methods for Proxify tab.
security.declareProtected('View management screens', 'getFormFieldList')
def getFormFieldList(self):
"""
find fields and forms which name ends with 'FieldLibrary' in
the same business template or in erp5_core.
"""
form_list = []
def iterate(obj):
for i in obj.objectValues():
if (i.meta_type=='ERP5 Form' and
i.id.startswith('Base_view') and
i.id.endswith('FieldLibrary') and
'_view' in i.getId()):
form_id = i.getId()
form_path = '%s.%s' % (obj.getId(), form_id)
field_list = []
form_list.append({'form_path':form_path,
'form_id':form_id,
'field_list':field_list})
for field in i.objectValues():
field_type, proxy_flag = get_field_meta_type_and_proxy_flag(field)
if proxy_flag:
field_type = '%s(Proxy)' % field_type
field_list.append({'field_object':field,
'field_type':field_type,
'proxy_flag':proxy_flag})
if i.meta_type=='Folder':
iterate(i)
skins_tool = self.portal_skins
folder_id = self.aq_parent.id
# for skin_folder_id in self.getSimilarSkinFolderIdList():
for skin_folder_id in self.getPortalObject().portal_skins.objectIds():
iterate(getattr(skins_tool, skin_folder_id))
iterate(skins_tool.erp5_core)
return form_list
security.declareProtected('View management screens', 'getProxyableFieldList')
def getProxyableFieldList(self, field, form_field_list=None):
""""""
def extract_keyword(name):
keyword_list = [i for i in name.split('_') if not i in \
('my', 'default', 'listbox', 'your')]
if len(keyword_list) == 0:
# This means that the name is one of the exception keywords,
# so we have to keep it
keyword_list = [name]
return keyword_list
def check_keyword_list(name, keyword_list):
count = 0
for i in keyword_list:
if i in name:
count += 1
return count/float(len(keyword_list))
def match(field_data):
if not field_data['field_type'].startswith(field.meta_type):
return 0
field_object = field_data['field_object']
if field_object.aq_base is field.aq_base:
return 0
field_id = field_object.getId()
# All proxy fields in field libraries should define their
# technical context
# XXX Theses 3 following lines will need to be uncommented
# as soon as proxy guideline is fully validated on erp5_trade
#if field.meta_type == 'ProxyField' and \
# re.match('my_.*_mode', field_id) is None:
# return 0
# XXX keyword match is not useful anymore.Need different approach.
keyword_match_rate = check_keyword_list(field_id, extract_keyword(id_))
if keyword_match_rate>0.3:
return keyword_match_rate
else:
def split(string):
result = []
temporary = []
for char in string:
if char.isupper():
if temporary:
result.append(''.join(temporary))
temporary = []
temporary.append(char)
result.append(''.join(temporary))
return result
if ''.join(field_id.split('_')[1:]).startswith(
split(field.meta_type)[0].lower()):
# At least it seems a generic template field of the meta_type.
return 0.1
def make_dict_list_append_function(dic, order_list):
def append(key, item):
if not key in order_list:
order_list.append(key)
dic[key] = []
dic[key].append(item)
return append
def add_default_field_library():
portal_url = getToolByName(self, 'portal_url')
portal = portal_url.getPortalObject()
portal_skins = getToolByName(self, 'portal_skins')
default_field_library_path = portal.getProperty(
'erp5_default_field_library_path',
'erp5_core.Base_viewFieldLibrary')
if (not default_field_library_path or
len(default_field_library_path.split('.'))!=2):
return
skinfolder_id, form_id = default_field_library_path.split('.')
skinfolder = getattr(portal_skins, skinfolder_id, None)
default_field_library = getattr(skinfolder, form_id, None)
if default_field_library is None:
return
for i in default_field_library.objectValues():
field_meta_type, proxy_flag = get_field_meta_type_and_proxy_flag(i)
if meta_type==field_meta_type:
if proxy_flag:
field_meta_type = '%s(Proxy)' % field_meta_type
matched_item = {'form_id':form_id,
'field_type':field_meta_type,
'field_object':i,
'proxy_flag':proxy_flag,
'matched_rate':0
}
if not i in [item['field_object']
for item in matched.get(default_field_library_path, ())]:
matched_append(default_field_library_path, matched_item)
if not i in [item['field_object']
for item in perfect_matched.get(default_field_library_path, ())]:
perfect_matched_append(default_field_library_path, matched_item)
id_ = field.getId()
meta_type = field.meta_type
matched = {}
form_order = []
matched_append = make_dict_list_append_function(matched, form_order)
perfect_matched = {}
perfect_matched_form_order = []
perfect_matched_append = make_dict_list_append_function(perfect_matched, perfect_matched_form_order)
if form_field_list is None:
form_field_list = self.getFormFieldList()
for i in form_field_list:
for data in i['field_list']:
tmp = []
matched_rate = match(data)
if matched_rate>0:
form_path = i['form_path']
form_id = i['form_id']
field_type = data['field_type']
field_object = data['field_object']
proxy_flag = data['proxy_flag']
matched_item = {'form_id':form_id,
'field_type':field_type,
'field_object':field_object,
'proxy_flag':proxy_flag,
'matched_rate':matched_rate
}
if matched_rate==1:
perfect_matched_append(form_path, matched_item)
elif not perfect_matched:
matched_append(form_path, matched_item)
if perfect_matched:
perfect_matched_form_order.sort()
add_default_field_library()
return perfect_matched_form_order, perfect_matched
form_order.sort()
add_default_field_library()
return form_order, matched
security.declareProtected('View management screens', 'getUnProxyableFieldList')
def getUnProxyableFieldList(self):
"""
Return ProxyFields
"""
return sorted([f for f in self.objectValues() \
if f.meta_type == 'ProxyField'], key = lambda x: x.id)
security.declareProtected('View management screens',
'getRelatedProxyFieldDictList')
def getRelatedProxyFieldDictList(self, **kw):
"""
Retrieve all proxy using proxy in this form
"""
form_id = self.id
proxy_dict = {}
for document in self.objectValues():
short_path = "%s.%s" % (form_id, document.id)
proxy_dict[short_path] = {'proxy': document,
'short_path': short_path,
'related_proxy_list': []}
def iterate(document):
for i in document.objectValues():
if i.meta_type == 'ERP5 Form':
for field in i.objectValues():
if field.meta_type == 'ProxyField':
key = "%s.%s" % (field.get_value('form_id'),
field.get_value('field_id'))
if proxy_dict.has_key(key):
proxy_dict[key]['related_proxy_list'].append(
{'short_path': "%s.%s" % \
(field.aq_parent.id, field.id),
'proxy': field})
if i.meta_type == 'Folder':
iterate(i)
skins_tool = self.portal_skins
proxy_dict_list = []
if len(proxy_dict):
# for skin_folder_id in self.getSimilarSkinFolderIdList():
for skin_folder_id in self.getPortalObject().portal_skins.objectIds():
iterate(getattr(skins_tool, skin_folder_id))
proxy_dict_list = proxy_dict.values()
proxy_dict_list.sort(key=lambda x: x['short_path'])
for item in proxy_dict_list:
item['related_proxy_list'].sort(key=lambda x: x['short_path'])
return proxy_dict_list
_proxy_copy_type_list = (bytes, unicode, int, long, float, bool, list,
tuple, dict, DateTime)
security.declareProtected('Change Formulator Forms', 'proxifyField')
def proxifyField(self, field_dict=None, force_delegate=False,
keep_empty_value=False, REQUEST=None):
"""Convert fields to proxy fields
If the field value is not empty and different from the proxyfield
value, the value is kept on the proxyfield, otherwise it is delegated.
If you specify force_delegate, values will be delegated even if they
are different. And if you specify keep_empty_value, then empty values
will not be delegated(force_delegate option is high priority).
"""
def copy(field, value_type):
new_dict = {}
for key, value in getFieldDict(field, value_type).iteritems():
if isinstance(aq_base(value), (Method, TALESMethod)):
value = copyMethod(value)
elif not (value is None or
isinstance(value, self._proxy_copy_type_list)):
raise ValueError('%s:%r' % (type(value), value))
elif not (keep_empty_value or value):
continue
new_dict[key] = value
return new_dict
def is_equal(a, b):
type_a = type(a)
type_b = type(b)
if type_a is not type_b:
return False
elif type_a is Method:
return a.method_name==b.method_name
elif type_a is TALESMethod:
return a._text==b._text
else:
return a==b
def remove_same_value(new_dict, target_dict):
for key, value in new_dict.items():
target_value = target_dict.get(key)
if force_delegate or is_equal(value, target_value):
del new_dict[key]
return new_dict
def get_group_and_position(field_id):
for i in self.groups.keys():
if field_id in self.groups[i]:
return i, self.groups[i].index(field_id)
def set_group_and_position(group, position, field_id):
self.field_removed(field_id)
self.groups[group].insert(position, field_id)
# Notify changes explicitly.
self.groups = self.groups
if field_dict is None:
return
for field_id in field_dict.keys():
target = field_dict[field_id]
target_form_id, target_field_id = target.split('.')
# keep current group and position.
group, position = get_group_and_position(field_id)
# create proxy field
old_field = getattr(self, field_id)
self.manage_delObjects(field_id)
self.manage_addField(id=field_id, title='', fieldname='ProxyField')
proxy_field = getattr(self, field_id)
proxy_field.values['form_id'] = target_form_id
proxy_field.values['field_id'] = target_field_id
target_field = proxy_field.getTemplateField()
if target_field is None:
raise ValueError("Unable to find template : %s.%s" % (
target_form_id, target_field_id))
# copy data
new_values = remove_same_value(copy(old_field, 'values'),
getFieldDict(target_field, 'values'))
new_tales = remove_same_value(copy(old_field, 'tales'),
getFieldDict(target_field, 'tales'))
if target_field.meta_type=='ProxyField':
for i in new_values.keys():
if not i in target_field.delegated_list:
# obsolete variable check
try:
target_field.get_recursive_orig_value(i)
except KeyError:
# then `i` is obsolete!
del new_values[i]
else:
if is_equal(target_field.get_recursive_orig_value(i),
new_values[i]):
del new_values[i]
for i in new_tales.keys():
if not i in target_field.delegated_list:
# obsolete variable check
try:
target_field.get_recursive_tales(i)
except KeyError:
# then `i` is obsolete!
del new_tales[i]
else:
if is_equal(target_field.get_recursive_tales(i),
new_tales[i]):
del new_tales[i]
delegated_list = []
for i in (new_values.keys()+new_tales.keys()):
if not i in delegated_list:
delegated_list.append(i)
proxy_field.values.update(new_values)
proxy_field.tales.update(new_tales)
proxy_field.delegated_list = delegated_list
# move back to the original group and position.
set_group_and_position(group, position, field_id)
if REQUEST is not None:
return self.formProxify(manage_tabs_message='Changed')
psyco.bind(__call__)
psyco.bind(_exec)
security.declareProtected('Change Formulator Forms', 'unProxifyField')
def unProxifyField(self, field_dict=None, copy_delegated_values=False,
REQUEST=None):
"""
Convert proxy fields to fields
"""
def copy(field, value_type):
new_dict = {}
for key, value in getFieldDict(field, value_type).iteritems():
if isinstance(aq_base(value), (Method, TALESMethod)):
value = copyMethod(value)
elif not (value is None or
isinstance(value, self._proxy_copy_type_list)):
raise ValueError('%s:%r' % (type(value), value))
new_dict[key] = value
return new_dict
def is_equal(a, b):
type_a = type(a)
type_b = type(b)
if type_a is not type_b:
return False
elif type_a is Method:
return a.method_name==b.method_name
elif type_a is TALESMethod:
return a._text==b._text
else:
return a==b
def remove_same_value(new_dict, target_dict):
for key, value in new_dict.items():
target_value = target_dict.get(key)
if is_equal(value, target_value):
del new_dict[key]
return new_dict
def get_group_and_position(field_id):
for i in self.groups.keys():
if field_id in self.groups[i]:
return i, self.groups[i].index(field_id)
def set_group_and_position(group, position, field_id):
self.field_removed(field_id)
self.groups[group].insert(position, field_id)
# Notify changes explicitly.
self.groups = self.groups
if field_dict is None:
return
for field_id in field_dict.keys():
# keep current group and position.
group, position = get_group_and_position(field_id)
# create field
old_proxy_field = getattr(self, field_id)
delegated_field = old_proxy_field.getRecursiveTemplateField()
if delegated_field is None:
break
self.manage_delObjects(field_id)
self.manage_addField(id=field_id,
title='',
fieldname=delegated_field.meta_type)
field = getattr(self, field_id)
# copy data
new_values = remove_same_value(copy(old_proxy_field, 'values'),
field.values)
new_tales = remove_same_value(copy(old_proxy_field, 'tales'),
field.tales)
field.values.update(new_values)
field.tales.update(new_tales)
# move back to the original group and position.
set_group_and_position(group, position, field_id)
if REQUEST is not None:
return self.formUnProxify(manage_tabs_message='Changed')
# Overload of the Form method
# Use the include_disabled parameter since
# we should consider all fields to render the group tab
# moreoever, listbox rendering fails whenever enabled
# is based on the cell parameter.
security.declareProtected('View', 'get_largest_group_length')
def get_largest_group_length(self):
"""Get the largest group length available; necessary for
'order' screen user interface.
XXX - Copyright issue
"""
max = 0
for group in self.get_groups(include_empty=1):
fields = self.get_fields_in_group(group, include_disabled=1)
if len(fields) > max:
max = len(fields)
return max
security.declareProtected('View', 'get_groups')
def get_groups(self, include_empty=0):
"""Get a list of all groups, in display order.
If include_empty is false, suppress groups that do not have
enabled fields.
XXX - Copyright issue
"""
if include_empty:
return self.group_list
return [group for group in self.group_list
if self.get_fields_in_group(group, include_disabled=1)]
# Find support in ZMI. This is useful for development.
def PrincipiaSearchSource(self):
return str((self.pt, self.name, self.action, self.update_action,
self.encoding, self.stored_encoding, self.enctype))
# utility function
def get_field_meta_type_and_proxy_flag(field):
if field.meta_type=='ProxyField':
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment