Commit 024c42aa authored by Jérome Perrin's avatar Jérome Perrin

Merge remote-tracking branch 'upstream/master' into zope4py3

parents 7bd7e01f bc9b6328
Pipeline #38547 failed with stage
in 0 seconds
...@@ -33,7 +33,9 @@ ...@@ -33,7 +33,9 @@
<item> <item>
<key> <string>group_list</string> </key> <key> <string>group_list</string> </key>
<value> <value>
<tuple/> <tuple>
<string>document</string>
</tuple>
</value> </value>
</item> </item>
<item> <item>
......
...@@ -19,6 +19,8 @@ preference.setTitle(translateString('Preference for ${name}', ...@@ -19,6 +19,8 @@ preference.setTitle(translateString('Preference for ${name}',
mapping=dict(name=context.getTitle()))) mapping=dict(name=context.getTitle())))
for assignment in context.contentValues(portal_type='Assignment'): for assignment in context.contentValues(portal_type='Assignment'):
if assignment.getValidationState() != 'open':
continue
group = assignment.getGroup(base=True) group = assignment.getGroup(base=True)
if group: if group:
preference.setPreferredSectionCategory(group) preference.setPreferredSectionCategory(group)
......
...@@ -1309,6 +1309,12 @@ class TestERP5Base(ERP5TypeTestCase): ...@@ -1309,6 +1309,12 @@ class TestERP5Base(ERP5TypeTestCase):
site='distibution/tokyo') site='distibution/tokyo')
self.assertNotEqual(None, assignment.getGroupValue()) self.assertNotEqual(None, assignment.getGroupValue())
assignment.open() assignment.open()
# unrelated assignment that should be ignored
person.newContent(
portal_type='Assignment',
group='nexedi/rentalinux',
site='distibution/new_york',
)
login = person.newContent( login = person.newContent(
portal_type="ERP5 Login", portal_type="ERP5 Login",
reference="user_login", reference="user_login",
......
...@@ -291,17 +291,14 @@ class TestDocumentConversionCache(TestDocumentMixin): ...@@ -291,17 +291,14 @@ class TestDocumentConversionCache(TestDocumentMixin):
def test_08_check_conversion_cache_with_portal_document_type_list(self): def test_08_check_conversion_cache_with_portal_document_type_list(self):
"""Check cache conversion for all Portal Document Types """Check cache conversion for all Portal Document Types
""" """
portal_type_list = list(self.portal.getPortalDocumentTypeList()) portal_type_set = set(self.portal.getPortalDocumentTypeList())
# Some conversions are not implemented
portal_type_set.discard('File')
portal_type_set.discard('Notification Message')
portal_type_set.discard('Web Illustration')
portal_type_set.discard('Web Table')
if 'File' in portal_type_list:
#File conversion is not implemented
portal_type_list.remove('File')
if 'Web Illustration' in portal_type_list:
#Web Illustration conversion is not implemented
portal_type_list.remove('Web Illustration')
if 'Web Table' in portal_type_list:
#Web Table conversion is not implemented
portal_type_list.remove('Web Table')
data_mapping = {'Drawing': 'TEST-en-002.sxd', data_mapping = {'Drawing': 'TEST-en-002.sxd',
'Text': 'TEST-en-002.doc', 'Text': 'TEST-en-002.doc',
'Spreadsheet': 'TEST-en-002.sxc', 'Spreadsheet': 'TEST-en-002.sxc',
...@@ -311,8 +308,8 @@ class TestDocumentConversionCache(TestDocumentMixin): ...@@ -311,8 +308,8 @@ class TestDocumentConversionCache(TestDocumentMixin):
'File': 'TEST-en-002.rtf', 'File': 'TEST-en-002.rtf',
'PDF': 'TEST-en-002.pdf'} 'PDF': 'TEST-en-002.pdf'}
#Check that all portal_types are handled by test #Check that all portal_types are handled by test
self.assertEqual(len(portal_type_list), len([pt for pt in portal_type_list if pt in data_mapping])) self.assertEqual(len(portal_type_set), len([pt for pt in portal_type_set if pt in data_mapping]))
for portal_type in portal_type_list: for portal_type in portal_type_set:
module = self.portal.getDefaultModule(portal_type=portal_type) module = self.portal.getDefaultModule(portal_type=portal_type)
upload_file = self.makeFileUpload(data_mapping[portal_type]) upload_file = self.makeFileUpload(data_mapping[portal_type])
document = module.newContent(portal_type=portal_type) document = module.newContent(portal_type=portal_type)
......
...@@ -69,6 +69,9 @@ ...@@ -69,6 +69,9 @@
schema.description = " (default: " + schema.default + ")"; schema.description = " (default: " + schema.default + ")";
} }
} }
if (schema.default !== undefined) {
delete schema.default;
}
return schema; return schema;
}; };
......
...@@ -96,11 +96,12 @@ class GoogleConnector(XMLObject): ...@@ -96,11 +96,12 @@ class GoogleConnector(XMLObject):
Used by Products.ERP5Security.ERP5ExternalOauth2ExtractionPlugin Used by Products.ERP5Security.ERP5ExternalOauth2ExtractionPlugin
""" """
refresh_token = token['refresh_token']
body = self._getOAuthlibClient().prepare_refresh_body( body = self._getOAuthlibClient().prepare_refresh_body(
client_id=self.getClientId(), client_id=self.getClientId(),
client_secret=self.getSecretKey(), client_secret=self.getSecretKey(),
access_type="offline", access_type="offline",
refresh_token=token['refresh_token'], refresh_token=refresh_token,
) )
resp = requests.post( resp = requests.post(
TOKEN_URL, TOKEN_URL,
...@@ -110,7 +111,9 @@ class GoogleConnector(XMLObject): ...@@ -110,7 +111,9 @@ class GoogleConnector(XMLObject):
) )
if not resp.ok: if not resp.ok:
return {} return {}
return self._getGoogleTokenFromJSONResponse(resp.json()) new_token = resp.json()
new_token.setdefault('refresh_token', refresh_token)
return self._getGoogleTokenFromJSONResponse(new_token)
@security.private @security.private
def getUserEntry(self, access_token): def getUserEntry(self, access_token):
......
...@@ -310,7 +310,7 @@ class TestGoogleLogin(GoogleLoginTestCase): ...@@ -310,7 +310,7 @@ class TestGoogleLogin(GoogleLoginTestCase):
resp = self.publish(self.portal.getPath(), env=env) resp = self.publish(self.portal.getPath(), env=env)
self.assertEqual(resp.getStatus(), six.moves.http_client.OK) self.assertEqual(resp.getStatus(), six.moves.http_client.OK)
def token_callback(request): def _check_token_callback_request(request, refresh_token):
self.assertEqual( self.assertEqual(
request.headers['Content-Type'], request.headers['Content-Type'],
'application/x-www-form-urlencoded') 'application/x-www-form-urlencoded')
...@@ -321,36 +321,81 @@ class TestGoogleLogin(GoogleLoginTestCase): ...@@ -321,36 +321,81 @@ class TestGoogleLogin(GoogleLoginTestCase):
'client_id': self.client_id, 'client_id': self.client_id,
'client_secret': self.secret_key, 'client_secret': self.secret_key,
'grant_type': 'refresh_token', 'grant_type': 'refresh_token',
'refresh_token': self.refresh_token, 'refresh_token': refresh_token,
} }
) )
def _userinfo_callback(request, access_token):
self.assertEqual(
request.headers['Authorization'],
'Bearer ' + access_token)
return 200, {}, json.dumps({
"first_name": "John",
"last_name": "Doe",
"email": self.default_google_login_email_address,
})
def token_callback_1(request):
# First refresh, the refresh token is still valid, so it is not
# included in the response, the client gets a new access_token
# and will re-use the same refresh_token
_check_token_callback_request(request, self.refresh_token)
return 200, {}, json.dumps({ return 200, {}, json.dumps({
'access_token': 'new' + self.access_token, 'access_token': 'new' + self.access_token,
'expires_in': 3600,
})
def userinfo_callback_1(request):
return _userinfo_callback(request, 'new' + self.access_token)
def token_callback_2(request):
# Second refresh, the refresh token is no longer valid and a new
# token is included in the response, the client gets a new access_token
# and and a new refresh_token
_check_token_callback_request(request, self.refresh_token)
return 200, {}, json.dumps({
'access_token': 'newnew' + self.access_token,
'refresh_token': 'new' + self.refresh_token, 'refresh_token': 'new' + self.refresh_token,
'expires_in': 3600, 'expires_in': 3600,
}) })
with mock.patch( def userinfo_callback_2(request):
'Products.ERP5Security.ERP5ExternalOauth2ExtractionPlugin.time.time', return _userinfo_callback(request, 'newnew' + self.access_token)
return_value=time.time() + 5000), \
responses.RequestsMock() as rsps: def token_callback_3(request):
rsps.add_callback( # Third refresh, we check that the refresh is made with the new refresh
responses.POST, # token from second refresh.
'https://accounts.google.com/o/oauth2/token', _check_token_callback_request(request, 'new' + self.refresh_token)
token_callback, return 200, {}, json.dumps({
) 'access_token': 'newnewnew' + self.access_token,
# refreshing the token calls userinfo again 'expires_in': 3600,
rsps.add( })
method='GET',
url='https://www.googleapis.com/oauth2/v1/userinfo', def userinfo_callback_3(request):
json={ return _userinfo_callback(request, 'newnewnew' + self.access_token)
"first_name": "John",
"last_name": "Doe", for i, (token_callback, userinfo_callback) in enumerate(
"email": self.default_google_login_email_address, zip(
} (token_callback_1, token_callback_2, token_callback_3),
) (userinfo_callback_1, userinfo_callback_2, userinfo_callback_3),
resp = self.publish(self.portal.getPath(), env=env) ), 1):
self.assertEqual(resp.getStatus(), six.moves.http_client.OK) with mock.patch(
'Products.ERP5Security.ERP5ExternalOauth2ExtractionPlugin.time.time',
return_value=time.time() + i * 5000), \
responses.RequestsMock() as rsps:
rsps.add_callback(
responses.POST,
'https://accounts.google.com/o/oauth2/token',
token_callback,
)
# refreshing the token calls userinfo again, with the new access token
rsps.add_callback(
responses.GET,
'https://www.googleapis.com/oauth2/v1/userinfo',
userinfo_callback,
)
resp = self.publish(self.portal.getPath(), env=env)
self.assertEqual(resp.getStatus(), six.moves.http_client.OK)
resp = self.publish(self.portal.getPath(), env=env) resp = self.publish(self.portal.getPath(), env=env)
self.assertEqual(resp.getStatus(), six.moves.http_client.OK) self.assertEqual(resp.getStatus(), six.moves.http_client.OK)
......
...@@ -57,7 +57,9 @@ ...@@ -57,7 +57,9 @@
</tal:block> </tal:block>
<!-- field value --> <!-- field value -->
<tal:block tal:define="value python: field.get_value('default'); style_prefix string:with_border_"> <tal:block tal:define="value python: field.get_value('default');
style_prefix string:with_border_;
merge_cells python: True">
<tal:block metal:use-macro="here/field_ods_macro/macros/cell_value" /> <tal:block metal:use-macro="here/field_ods_macro/macros/cell_value" />
</tal:block> </tal:block>
...@@ -74,7 +76,8 @@ ...@@ -74,7 +76,8 @@
<tal:block tal:define="is_list python:same_type(value, []) or same_type(value, ()); <tal:block tal:define="is_list python:same_type(value, []) or same_type(value, ());
is_float python: isinstance(value, modules['six'].integer_types + (float, ));"> is_float python: isinstance(value, modules['six'].integer_types + (float, ));">
<tal:block tal:condition="python: is_list"> <tal:block tal:condition="python: is_list">
<table:table-cell tal:attributes="table:style-name string:${style_prefix}text"> <table:table-cell tal:attributes="table:style-name string:${style_prefix}text;
table:number-columns-spanned python:exists('merge_cells') and max(column_len-1, 1)">
<tal:block tal:condition="python: field is None" tal:repeat="item value"> <tal:block tal:condition="python: field is None" tal:repeat="item value">
<text:p tal:content="item"/> <text:p tal:content="item"/>
</tal:block> </tal:block>
...@@ -87,7 +90,8 @@ ...@@ -87,7 +90,8 @@
<tal:block tal:condition="is_float"> <tal:block tal:condition="is_float">
<tal:block tal:condition="python: isinstance(value, modules['six'].integer_types)"> <tal:block tal:condition="python: isinstance(value, modules['six'].integer_types)">
<table:table-cell tal:attributes="office:value value; <table:table-cell tal:attributes="office:value value;
table:style-name string:${style_prefix}figure" table:style-name string:${style_prefix}figure;
table:number-columns-spanned python:exists('merge_cells') and max(column_len-1, 1)"
office:value-type="float" office:value-type="float"
table:style-name="figure"> table:style-name="figure">
<text:p tal:condition="python: field is None" <text:p tal:condition="python: field is None"
...@@ -107,13 +111,15 @@ ...@@ -107,13 +111,15 @@
"> ">
<table:table-cell tal:attributes="office:value value; <table:table-cell tal:attributes="office:value value;
table:style-name style_name; table:style-name style_name;
office:value-type python: ('%' in input_style) and 'percentage' or 'float'" office:value-type python: ('%' in input_style) and 'percentage' or 'float';
table:number-columns-spanned python:exists('merge_cells') and max(column_len-1, 1)"
table:style-name="figure"> table:style-name="figure">
<text:p tal:content="python: field.render_pdf(value)" /> <text:p tal:content="python: field.render_pdf(value)" />
</table:table-cell> </table:table-cell>
</tal:block> </tal:block>
<tal:block tal:condition="python:field.meta_type not in ['FloatField','IntegerField'] and field.meta_type!='ProxyField'"> <tal:block tal:condition="python:field.meta_type not in ['FloatField','IntegerField'] and field.meta_type!='ProxyField'">
<table:table-cell tal:attributes="table:style-name string:${style_prefix}string;" <table:table-cell tal:attributes="table:style-name string:${style_prefix}string;
table:number-columns-spanned python:exists('merge_cells') and max(column_len-1, 1)"
table:style-name="string" table:style-name="string"
office:value-type="string"> office:value-type="string">
<text:p>Error: field is not a FloatField nor an IntegerField</text:p> <text:p>Error: field is not a FloatField nor an IntegerField</text:p>
...@@ -123,7 +129,8 @@ ...@@ -123,7 +129,8 @@
</tal:block> </tal:block>
<tal:block tal:condition="python: field is None"> <tal:block tal:condition="python: field is None">
<table:table-cell tal:attributes="office:value value; <table:table-cell tal:attributes="office:value value;
table:style-name string:${style_prefix}figure" table:style-name string:${style_prefix}figure;
table:number-columns-spanned python:exists('merge_cells') and max(column_len-1, 1)"
office:value-type="float" office:value-type="float"
table:style-name="figure"> table:style-name="figure">
<text:p tal:condition="python: field is None" <text:p tal:condition="python: field is None"
...@@ -139,7 +146,8 @@ ...@@ -139,7 +146,8 @@
<tal:block tal:condition="python: isinstance(value, DateTime)"> <tal:block tal:condition="python: isinstance(value, DateTime)">
<tal:block tal:condition="python: field is None"> <tal:block tal:condition="python: field is None">
<table:table-cell tal:attributes="office:date-value python: context.ERP5Site_formatDateForODF(value); <table:table-cell tal:attributes="office:date-value python: context.ERP5Site_formatDateForODF(value);
table:style-name string:${style_prefix}date;" table:style-name string:${style_prefix}date;
table:number-columns-spanned python:exists('merge_cells') and max(column_len-1, 1)"
table:style-name="date" table:style-name="date"
office:value-type="date"> office:value-type="date">
<text:p tal:content="python: value"/> <text:p tal:content="python: value"/>
...@@ -149,7 +157,8 @@ ...@@ -149,7 +157,8 @@
<tal:block tal:condition="python:field.meta_type=='DateTimeField' or (field.meta_type == 'ProxyField' and field.getRecursiveTemplateField().meta_type == 'DateTimeField')"> <tal:block tal:condition="python:field.meta_type=='DateTimeField' or (field.meta_type == 'ProxyField' and field.getRecursiveTemplateField().meta_type == 'DateTimeField')">
<tal:block tal:condition="python:field.get_value('date_only')" tal:define="input_order python:field.get_value('input_order') or 'ymd'"> <tal:block tal:condition="python:field.get_value('date_only')" tal:define="input_order python:field.get_value('input_order') or 'ymd'">
<table:table-cell tal:attributes="office:date-value python: context.ERP5Site_formatDateForODF(value); <table:table-cell tal:attributes="office:date-value python: context.ERP5Site_formatDateForODF(value);
table:style-name string:${style_prefix}date_${input_order};" table:style-name string:${style_prefix}date_${input_order};
table:number-columns-spanned python:exists('merge_cells') and max(column_len-1, 1)"
table:style-name="date" table:style-name="date"
office:value-type="date"> office:value-type="date">
<text:p tal:content="python: field.render_pdf(value)"/> <text:p tal:content="python: field.render_pdf(value)"/>
...@@ -157,7 +166,8 @@ ...@@ -157,7 +166,8 @@
</tal:block> </tal:block>
<tal:block tal:condition="python:not field.get_value('date_only')" tal:define="input_order python:field.get_value('input_order') or 'ymd'"> <tal:block tal:condition="python:not field.get_value('date_only')" tal:define="input_order python:field.get_value('input_order') or 'ymd'">
<table:table-cell tal:attributes="office:date-value python: context.ERP5Site_formatDateForODF(value); <table:table-cell tal:attributes="office:date-value python: context.ERP5Site_formatDateForODF(value);
table:style-name string:${style_prefix}date_with_time_${input_order};" table:style-name string:${style_prefix}date_with_time_${input_order};
table:number-columns-spanned python:exists('merge_cells') and max(column_len-1, 1)"
table:style-name="date_with_time" table:style-name="date_with_time"
office:value-type="date"> office:value-type="date">
<text:p tal:content="python: field.render_pdf(value)"/> <text:p tal:content="python: field.render_pdf(value)"/>
...@@ -165,7 +175,8 @@ ...@@ -165,7 +175,8 @@
</tal:block> </tal:block>
</tal:block> </tal:block>
<tal:block tal:condition="python:field.meta_type!='DateTimeField' and field.meta_type!='ProxyField'"> <tal:block tal:condition="python:field.meta_type!='DateTimeField' and field.meta_type!='ProxyField'">
<table:table-cell tal:attributes="table:style-name string:${style_prefix}string;" <table:table-cell tal:attributes="table:style-name string:${style_prefix}string;
table:number-columns-spanned python:exists('merge_cells') and max(column_len-1, 1)"
table:style-name="string" table:style-name="string"
office:value-type="string"> office:value-type="string">
<text:p>Error: field is not a DateTimeField</text:p> <text:p>Error: field is not a DateTimeField</text:p>
...@@ -174,7 +185,8 @@ ...@@ -174,7 +185,8 @@
</tal:block> </tal:block>
</tal:block> </tal:block>
<tal:block tal:condition="python:not isinstance(value, DateTime)"> <tal:block tal:condition="python:not isinstance(value, DateTime)">
<table:table-cell tal:attributes="table:style-name string:${style_prefix}text" <table:table-cell tal:attributes="table:style-name string:${style_prefix}text;
table:number-columns-spanned python:exists('merge_cells') and max(column_len-1, 1)"
table:style-name="text"> table:style-name="text">
<text:p tal:condition="python: field is None" <text:p tal:condition="python: field is None"
tal:content="python: value"/> tal:content="python: value"/>
...@@ -187,7 +199,8 @@ ...@@ -187,7 +199,8 @@
</tal:block> </tal:block>
<tal:block tal:condition="python: value is None"> <tal:block tal:condition="python: value is None">
<table:table-cell office:value-type='string' <table:table-cell office:value-type='string'
tal:attributes="table:style-name string:${style_prefix}text" tal:attributes="table:style-name string:${style_prefix}text;
table:number-columns-spanned python:exists('merge_cells') and max(column_len-1, 1)"
table:style-name='text'> table:style-name='text'>
<text:p tal:content="python: ''"> <text:p tal:content="python: ''">
</text:p> </text:p>
......
...@@ -104,7 +104,7 @@ class TestSupportRequestCreateNewSupportRequest(SupportRequestTestCase): ...@@ -104,7 +104,7 @@ class TestSupportRequestCreateNewSupportRequest(SupportRequestTestCase):
p for p in self.portal.getPortalDocumentTypeList() \ p for p in self.portal.getPortalDocumentTypeList() \
if p not in ("Sound", "Video", "Web Page", 'Video', 'Web Illustration', if p not in ("Sound", "Video", "Web Page", 'Video', 'Web Illustration',
'Web Manifest', 'Web Page', 'Web Script', 'Web Style', 'Web Manifest', 'Web Page', 'Web Script', 'Web Style',
'Web Table', 'Notebook')] 'Web Table', 'Notebook', 'Notification Message')]
# Should not happens but we never know # Should not happens but we never know
assert portal_type_list, portal_type_list assert portal_type_list, portal_type_list
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Base Category" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_folders_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Copy_or_Move_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Delete_objects_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_count</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_mt_index</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>_tree</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>trade_condition_type</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>transformation_type</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Base Category</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Transformation Type</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Length" module="BTrees.Length"/>
</pickle>
<pickle> <int>0</int> </pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
</ZopeData>
...@@ -23,6 +23,9 @@ ...@@ -23,6 +23,9 @@
<portal_type id="Service Module"> <portal_type id="Service Module">
<item>business_application</item> <item>business_application</item>
</portal_type> </portal_type>
<portal_type id="Transformation">
<item>transformation_type</item>
</portal_type>
<portal_type id="Transformation Module"> <portal_type id="Transformation Module">
<item>business_application</item> <item>business_application</item>
</portal_type> </portal_type>
......
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
<list> <list>
<string>all_columns</string> <string>all_columns</string>
<string>columns</string> <string>columns</string>
<string>domain_root_list</string>
<string>domain_tree</string>
<string>portal_types</string> <string>portal_types</string>
<string>selection_name</string> <string>selection_name</string>
<string>sort</string> <string>sort</string>
...@@ -88,6 +90,14 @@ ...@@ -88,6 +90,14 @@
<string>translated_description</string> <string>translated_description</string>
<string>Translated Description</string> <string>Translated Description</string>
</tuple> </tuple>
<tuple>
<string>specialise_title</string>
<string>Transformation Template</string>
</tuple>
<tuple>
<string>transformation_type_translated_title</string>
<string>Transformation Type</string>
</tuple>
<tuple> <tuple>
<string>owner_title</string> <string>owner_title</string>
<string>Owner</string> <string>Owner</string>
...@@ -136,6 +146,21 @@ ...@@ -136,6 +146,21 @@
</list> </list>
</value> </value>
</item> </item>
<item>
<key> <string>domain_root_list</string> </key>
<value>
<list>
<tuple>
<string>transformation_type</string>
<string>Transformation Type</string>
</tuple>
</list>
</value>
</item>
<item>
<key> <string>domain_tree</string> </key>
<value> <int>1</int> </value>
</item>
<item> <item>
<key> <string>field_id</string> </key> <key> <string>field_id</string> </key>
<value> <string>my_list_mode_listbox</string> </value> <value> <string>my_list_mode_listbox</string> </value>
......
...@@ -112,7 +112,7 @@ ...@@ -112,7 +112,7 @@
<value> <value>
<list> <list>
<string>my_title</string> <string>my_title</string>
<string>my_reference</string> <string>my_transformation_type</string>
<string>my_resource_title</string> <string>my_resource_title</string>
<string>my_template_transformation_title_list</string> <string>my_template_transformation_title_list</string>
</list> </list>
...@@ -122,6 +122,7 @@ ...@@ -122,6 +122,7 @@
<key> <string>right</string> </key> <key> <string>right</string> </key>
<value> <value>
<list> <list>
<string>my_reference</string>
<string>my_version</string> <string>my_version</string>
<string>my_variation_base_category_list</string> <string>my_variation_base_category_list</string>
<string>my_variation_category_list</string> <string>my_variation_category_list</string>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_transformation_type</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_view_mode_category</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Transformation Type</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -15,5 +15,6 @@ pricing ...@@ -15,5 +15,6 @@ pricing
segment segment
shape shape
tariff_nomenclature tariff_nomenclature
transformation_type
variation variation
visual_pattern visual_pattern
\ No newline at end of file
...@@ -6,4 +6,5 @@ Quantity Unit Conversion Group | quantity_unit ...@@ -6,4 +6,5 @@ Quantity Unit Conversion Group | quantity_unit
Quantity Unit Conversion Module | business_application Quantity Unit Conversion Module | business_application
Sale Supply Module | business_application Sale Supply Module | business_application
Service Module | business_application Service Module | business_application
Transformation Module | business_application Transformation Module | business_application
\ No newline at end of file Transformation | transformation_type
\ No newline at end of file
...@@ -28,23 +28,16 @@ ...@@ -28,23 +28,16 @@
############################################################################## ##############################################################################
from warnings import warn from warnings import warn
import six
if six.PY2:
from base64 import decodestring as decodebytes
else:
from base64 import decodebytes
from zLOG import LOG from zLOG import LOG, WARNING
from AccessControl import ClassSecurityInfo, getSecurityManager from AccessControl import ClassSecurityInfo, getSecurityManager
from AccessControl.SecurityManagement import newSecurityManager from AccessControl.SecurityManagement import newSecurityManager
from Products.CMFCore.utils import getToolByName
from erp5.component.mixin.ExtensibleTraversableMixin import ExtensibleTraversableMixin from erp5.component.mixin.ExtensibleTraversableMixin import ExtensibleTraversableMixin
from Products.ERP5Type.Cache import getReadOnlyTransactionCache from Products.ERP5Type.Cache import getReadOnlyTransactionCache
from Products.ERP5Type.Globals import InitializeClass from Products.ERP5Type.Globals import InitializeClass
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions
from Products.ERP5Type.Globals import get_request from Products.ERP5Type.Globals import get_request
from Products.ERP5Type.Utils import str2bytes, bytes2str
# XXX: these duplicate ones in ERP5.Document # XXX: these duplicate ones in ERP5.Document
_MARKER = [] _MARKER = []
...@@ -71,73 +64,74 @@ class BaseExtensibleTraversableMixin(ExtensibleTraversableMixin): ...@@ -71,73 +64,74 @@ class BaseExtensibleTraversableMixin(ExtensibleTraversableMixin):
def _forceIdentification(self, request): def _forceIdentification(self, request):
# force identification (usable for extensible content) # force identification (usable for extensible content)
# This is normally called from __bobo_traverse__ during publication.
# Publication works in two phases (for what matters here):
# - phase 1: traverse the entire path (request URL path)
# - phase 2: locate a user folder and find a user allowed to access
# published document
# This does not work for extensible traversable: here, we must look the
# document up, typically using catalog, and we need to have an
# authenticated user to do so.
# Because we do not expect to have multiple layers of user_folders (only
# one at the portal and one at zope root), and we are below both, we can
# already reliably look for the user up, authenticating the request and
# setting up a reasonable security context to use in catalog lookup
# (or executing other restricted code).
# XXX: this is certainly not the most elegant way of doing so.
old_manager = getSecurityManager()
cache = getReadOnlyTransactionCache() cache = getReadOnlyTransactionCache()
if cache is not None: if cache is None:
key = ('__bobo_traverse__', self, 'user') cache = {}
try: key = ('__bobo_traverse__', self, 'user')
user = cache[key] try:
except KeyError: user = cache[key]
user = _MARKER except KeyError:
else: old_user = old_manager.getUser()
user = _MARKER
old_user = getSecurityManager().getUser()
if user is _MARKER:
user = None # By default, do nothing user = None # By default, do nothing
if old_user is None or old_user.getUserName() == 'Anonymous User': if old_user is None or old_user.getUserName() == 'Anonymous User':
portal_membership = getToolByName(self.getPortalObject(), try:
'portal_membership') auth = request._auth
if portal_membership is not None: except AttributeError:
# This kind of error happens with unrestrictedTraverse,
# because the request object is a fake, and it is just
# a dict object. Nothing can be done with such an object.
user = None
else:
need_published = 'PUBLISHED' not in request.other
try: try:
if request.get('PUBLISHED', _MARKER) is _MARKER: if need_published:
# request['PUBLISHED'] is required by validate # request['PUBLISHED'] is required by validate
request['PUBLISHED'] = self request['PUBLISHED'] = self
has_published = False portal = self.getPortalObject()
else: # XXX: this is a simplification of the user validation logic from ZPublisher
has_published = True # - only two specific locations are checked for user folder existence
try: # - user folder name is hardcoded (instead of going though __allow_groups__)
auth = request._auth # - no request.role handling
except AttributeError: for user_folder_parent in (
# This kind of error happens with unrestrictedTraverse, portal,
# because the request object is a fake, and it is just portal.aq_parent,
# a dict object. ):
user = None user = user_folder_parent.acl_users.validate(request, auth)
else: if user is not None:
name = None if user.getUserName() == 'Anonymous User':
acl_users = self.getPortalObject().acl_users # If the user which is connected is anonymous, do not try to change SecurityManager
user_list = acl_users._extractUserIds(request, acl_users.plugins) user = None
if len(user_list) > 0: continue
name = user_list[0][0] break
else:
# this logic is copied from identify() in
# AccessControl.User.BasicUserFolder.
if auth and auth.lower().startswith('basic '):
name = bytes2str(decodebytes(str2bytes(auth.split(' ')[-1]))).split(':', 1)[0]
if name is not None:
user = portal_membership._huntUser(name, self)
else:
user = None
if not has_published:
try:
del request.other['PUBLISHED']
except AttributeError:
# The same here as above. unrestrictedTraverse provides
# just a plain dict, so request.other does not exist.
del request['PUBLISHED']
except Exception: except Exception:
LOG("ERP5 WARNING",0, LOG("ERP5 WARNING", WARNING,
"Failed to retrieve user in __bobo_traverse__ of WebSection %s" % self.getPath(), "Failed to retrieve user in __bobo_traverse__ of WebSection %s" % self.getPath(),
error=True) error=True)
user = None user = None
if user is not None and user.getUserName() == 'Anonymous User': finally:
user = None # If the user which is connected is anonymous, if need_published:
# do not try to change SecurityManager del request.other['PUBLISHED']
if cache is not None: cache[key] = user
cache[key] = user
if user is None:
old_manager = None old_manager = None
if user is not None: else:
# We need to perform identification # We need to perform identification
old_manager = getSecurityManager()
newSecurityManager(get_request(), user) newSecurityManager(get_request(), user)
return old_manager, user return old_manager, user
......
...@@ -3,6 +3,7 @@ Set up an Workflow with defaults variables needed by ERP5 ...@@ -3,6 +3,7 @@ Set up an Workflow with defaults variables needed by ERP5
""" """
state = context.newContent(portal_type='Workflow State', reference='draft', title='Draft') state = context.newContent(portal_type='Workflow State', reference='draft', title='Draft')
context.setSourceValue(state) context.setSourceValue(state)
context.setManagerBypass(True)
for v, property_dict in ( for v, property_dict in (
('action', { ('action', {
......
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