Commit 27717221 authored by Jérome Perrin's avatar Jérome Perrin

Merge remote-tracking branch 'nexedi/master' into zope4py2

parents cc2a8658 b7a60478
......@@ -33,7 +33,7 @@ from Products.ERP5Type import Permissions, PropertySheet, interfaces
from Products.ERP5Type.XMLObject import XMLObject
class Login(XMLObject, LoginAccountProviderMixin, EncryptedPasswordMixin):
class Login(EncryptedPasswordMixin, XMLObject, LoginAccountProviderMixin):
meta_type = 'ERP5 Login'
portal_type = 'Login'
add_permission = Permissions.AddPortalContent
......
......@@ -60,7 +60,7 @@ class UserExistsError(
super(UserExistsError, self).__init__('user id %s already exists' % (user_id, ))
class Person(Node, LoginAccountProviderMixin, EncryptedPasswordMixin, ERP5UserMixin):
class Person(EncryptedPasswordMixin, Node, LoginAccountProviderMixin, ERP5UserMixin):
"""
An Person object holds the information about
an person (ex. you, me, someone in the company,
......
......@@ -39,37 +39,45 @@ class IEncryptedPassword(Interface):
def checkPassword(value):
"""
Check the password, usefull when changing password
Check the password `value` match the current password, usefull when changing password.
"""
def checkPasswordValueAcceptable(value):
"""
Check if the password value is acceptable - i.e. follows site rules.
Check if the password `value` is acceptable in regard to password policy.
"""
def setEncodedPassword(value, format='default'): # pylint: disable=redefined-builtin
def checkUserCanChangePassword():
"""
Set an already encoded password.
Check if the current logged in user have permission to change password, on this
IEncryptedPassword.
Raise Products.CMFCore.exceptions.AccessControl_Unauthorized in case they don't
have the permission.
This method is deprecated and is not used by IEncryptedPassword internally.
"""
def _forceSetPassword(value):
def setPassword(value):
"""
Because both _setPassword and setPassword are considered as
public method (they are callable from user directly or through edit method)
_forceSetPassword is needed to reset password without security check by
Password Tool. This method is not callable through edit method as it not
begins with _set*
Set the password to `value` (a string holding the password in clear text).
Passing an empty value (such as None or empty string) will erase previously defined
password, which usually prevent login with this password.
"""
def checkUserCanChangePassword():
def setEncodedPassword(value, format='default'): # pylint: disable=redefined-builtin
"""
check user have permission to change his password. Raise in case he cannot.
Set an already encoded password.
"""
def setPassword(value) :
def edit(self, **kw):
"""
Set the password, only if the password is not empty and if
checkUserCanChangePassword don't raise any error
Edit the password and other properties of the documents through user interface.
This method is responsible for supporting the case where a IEncryptedPassword is
edited with a my_password field that is empty by default and not resetting the password
when edited with password=None.
"""
def getPassword(*args, **kw):
......@@ -83,7 +91,7 @@ class IEncryptedPassword(Interface):
Default: None
format (string)
String defining the format in which the password is expected.
If passowrd is not available in that format, KeyError will be
If password is not available in that format, KeyError will be
raised.
Default: 'default'
"""
......@@ -39,7 +39,7 @@ from Products.ERP5Type.Globals import PersistentMapping
from Products.CMFCore.utils import _checkPermission
from Products.CMFCore.exceptions import AccessControl_Unauthorized
class EncryptedPasswordMixin:
class EncryptedPasswordMixin(object):
# Declarative security
security = ClassSecurityInfo()
......@@ -68,7 +68,7 @@ class EncryptedPasswordMixin:
usage.
"""
if not self.getPortalObject().portal_preferences.isAuthenticationPolicyEnabled():
# not a policy so basically all passwords are accceptable
# not policy enabled, so basically all passwords are accceptable
return True
if not self.isPasswordValid(value):
raise ValueError("Password does not comply with password policy")
......@@ -87,7 +87,7 @@ class EncryptedPasswordMixin:
password = self.password = PersistentMapping()
self.password[format] = value
security.declarePublic('setEncodedPassword')
security.declareProtected(Permissions.SetOwnPassword, 'setEncodedPassword')
def setEncodedPassword(
self,
value,
......@@ -95,27 +95,20 @@ class EncryptedPasswordMixin:
):
"""
"""
self.checkUserCanChangePassword()
self._setEncodedPassword(value, format=format)
self.reindexObject()
def _forceSetPassword(self, value):
# this method is kept for backward compatibility, as there might be interaction
# workflows on this method.
self.password = PersistentMapping()
self._setEncodedPassword(pw_encrypt(value))
if value:
self._setEncodedPassword(pw_encrypt(value))
def _setPassword(self, value):
self.checkUserCanChangePassword()
self.checkPasswordValueAcceptable(value)
self._forceSetPassword(value)
security.declarePublic('setPassword')
def setPassword(self, value) :
"""
"""
if value is not None:
self._setPassword(value)
self.reindexObject()
security.declareProtected(Permissions.AccessContentsInformation, 'getPassword')
def getPassword(self, *args, **kw):
"""
......@@ -140,4 +133,17 @@ class EncryptedPasswordMixin:
password = default_password
return password
security.declareProtected(Permissions.ModifyPortalContent, 'edit')
def edit(self, *args, **kw):
"""edit, with support for empty password for the user interface.
In the user interface, we can have a my_password field, that will not
be pre-filled with the current password, but will be empty. To accomodate
this case, we don't edit the password if it is empty.
"""
if kw.get('password') is None:
kw.pop('password', None)
return super(EncryptedPasswordMixin, self).edit(*args, **kw)
InitializeClass(EncryptedPasswordMixin)
......@@ -181,13 +181,19 @@ class PresencePeriod(Movement, PeriodicityMixin):
timezone = self._getTimezone(next_start_date)
next_start_date = self._getNextDay(next_start_date, timezone)
while 1:
# We use 366*28 below, because gregorian calendar repeat itself every 28 years, so we
# don't need to loop more than this if we don't find a date, because it might be an
# impossible combination of week and month (eg. week number 30 can not be in January)
for _ in range(366 * 28):
if (self._validateDay(next_start_date)) and \
(self._validateWeek(next_start_date)) and \
(self._validateMonth(next_start_date)):
break
else:
next_start_date = self._getNextDay(next_start_date, timezone)
else:
return None
return DateTime(
next_start_date.year(),
......
......@@ -1937,6 +1937,48 @@ class TestCalendar(ERP5ReportTestCase):
self.assertEqual([], assignment.asMovementList())
def test_GroupCalendarPeriodWeeksAndMonthPeriodicity(self):
"""Tests that combinations of periodicity weeks and months are handled correctly.
"""
node = self.portal.organisation_module.newContent(portal_type='Organisation',)
group_calendar = self.portal.group_calendar_module.newContent(
portal_type='Group Calendar')
group_calendar_period = group_calendar.newContent(
portal_type='Group Presence Period')
group_calendar_period.setStartDate('2000/01/01 08:00:00 UTC')
group_calendar_period.setStopDate('2000/01/01 09:00:00 UTC')
group_calendar_period.setQuantity(10)
group_calendar_period.setResourceValue(
self.portal.portal_categories.calendar_period_type.type1)
# this group calendar repeats every days of the first week of the year and the second
# months, which is impossible.
group_calendar_period.setPeriodicityWeekList((1, ))
group_calendar_period.setPeriodicityMonthList((2, ))
self.tic()
assignment = self.portal.group_calendar_assignment_module.newContent(
specialise_value=group_calendar,
resource_value=self.portal.portal_categories.calendar_period_type.type1,
start_date=DateTime('2000/01/01 08:00:00 UTC'),
stop_date=DateTime('2010/01/01 18:00:00 UTC'),
destination_value=node)
assignment.confirm()
self.tic()
self.assertFalse(assignment.asMovementList()) # ... and no infinite loop
# edge case, repeat every Friday of week 9 in February, this does not happen every year
group_calendar_period.setPeriodicityWeekList((9, ))
group_calendar_period.setPeriodicityMonthList((2, ))
group_calendar_period.setPeriodicityWeekDayList(['Friday'])
self.assertEqual(
[m.getStartDate() for m in assignment.asMovementList()],
[DateTime('2003/02/28 09:00:00 UTC'),
DateTime('2004/02/27 09:00:00 UTC'),
DateTime('2008/02/29 09:00:00 UTC'),
DateTime('2009/02/27 09:00:00 UTC'),])
def test_PersonModule_viewLeaveRequestReport(self):
# in this test, type1 is the type for presences, type2 & type3 are types
# for leaves.
......
......@@ -4,7 +4,6 @@ from Products.ERP5Type import Permissions
class Person(ERP5Person):
security = ClassSecurityInfo()
security.declarePublic('getCertificate')
def _getCertificateLoginDocument(self):
for _erp5_login in self.objectValues(
......@@ -50,6 +49,7 @@ class Person(ERP5Person):
return self.getPortalObject().portal_certificate_authority\
.revokeCertificateByCommonName(self._getCertificateLoginDocument().getReference())
security.declarePublic('getCertificate')
def getCertificate(self):
"""Returns new SSL certificate"""
self._checkCertificateRequest()
......
......@@ -63,27 +63,32 @@ class PortalTypeConfiguratorItem(ConfiguratorItemMixin, XMLObject):
# arguments:
# * target_portal_type
# * add_propertysheet_list
type_information = getattr(portal.portal_types, self.target_portal_type)
portal_type_value = portal.portal_types[self.target_portal_type]
property_sheet_list = portal_type_value.getTypePropertySheetList()
extra_property_sheet_list = []
for name in self.add_propertysheet_list:
if not name in type_information.property_sheet_list:
new_property_sheet_list = list(type_information.property_sheet_list)
new_property_sheet_list.append(name)
type_information.property_sheet_list = tuple(new_property_sheet_list)
business_configuration = self.getBusinessConfigurationValue()
bt5_obj = business_configuration.getSpecialiseValue()
old_property_sheet_list = bt5_obj.getTemplatePortalTypePropertySheetList()
new_property_sheet_list = (list(old_property_sheet_list) +
['%s | %s' % (self.target_portal_type, name)
for name in self.add_propertysheet_list]
)
if fixit:
bt5_obj.edit(
template_portal_type_property_sheet_list=new_property_sheet_list)
#
# TODO:This class must support many other features we can use in ZMI.
#
return ['Property Sheets configuration should be added in %s' % \
bt5_obj.getTitle(),]
if name not in property_sheet_list:
extra_property_sheet_list.append(name)
# TODO: This class must support many other portal types features.
business_template_value = self.getBusinessConfigurationValue().getSpecialiseValue()
if extra_property_sheet_list:
if fixit:
portal_type_value.setTypePropertySheetList(
property_sheet_list + extra_property_sheet_list,
)
business_template_value.edit(
template_portal_type_property_sheet_list=list(
business_template_value.getTemplatePortalTypePropertySheetList()
) + [
'%s | %s' % (self.target_portal_type, name)
for name in extra_property_sheet_list
],
)
return [
'Associate Property Sheets %r to %r in %r' % (
extra_property_sheet_list,
self.target_portal_type,
business_template_value.getTitle(),
),
]
return []
......@@ -5,7 +5,9 @@ keep_bt5_id_list = []
bt5_update_catalog_list = ('erp5_ingestion_mysql_innodb_catalog', 'erp5_full_text_mroonga_catalog')
bt5_installation_list = bt5_update_catalog_list + (
bt5_installation_list = tuple(
context.getPortalObject().getCoreBusinessTemplateList(),
) + bt5_update_catalog_list + (
'erp5_configurator_standard',
'erp5_upgrader_officejs_sdk',
'erp5_administration',
......
......@@ -183,7 +183,7 @@ class TestOfficeJSSDKConfigurator(SecurityTestCase):
""" Make sure Installed business Templates are
what it is expected. """
expected_business_template_list = [
expected_business_template_list = self.portal.getCoreBusinessTemplateList() + [
'erp5_accounting',
'erp5_administration',
'erp5_base',
......@@ -191,7 +191,6 @@ class TestOfficeJSSDKConfigurator(SecurityTestCase):
'erp5_code_mirror',
'erp5_configurator',
'erp5_configurator_standard',
'erp5_core',
'erp5_core_proxy_field_legacy',
'erp5_crm',
'erp5_dms',
......@@ -218,7 +217,6 @@ class TestOfficeJSSDKConfigurator(SecurityTestCase):
'erp5_minipaint',
'erp5_monaco_editor',
'erp5_multimedia',
'erp5_mysql_innodb_catalog',
'erp5_notebook',
'erp5_officejs',
'erp5_officejs_connector',
......@@ -228,7 +226,6 @@ class TestOfficeJSSDKConfigurator(SecurityTestCase):
'erp5_only_office',
'erp5_pdm',
'erp5_project',
'erp5_property_sheets',
'erp5_run_my_doc',
'erp5_simulation',
'erp5_slideshow_style',
......@@ -246,7 +243,6 @@ class TestOfficeJSSDKConfigurator(SecurityTestCase):
'erp5_web_renderjs_ui_test',
'erp5_web_renderjs_ui_test_core',
'erp5_web_service',
'erp5_xhtml_style',
'officejs_todomvc'
]
self.assertSameSet(expected_business_template_list,
......
......@@ -276,6 +276,16 @@ class TestAlarm(ERP5TypeTestCase):
self.tic()
self.checkDate(alarm, right_first_date, right_second_date, right_third_date,right_fourth_date)
def test_week_and_month_impossible_combination(self):
alarm = self.newAlarm(enabled=True)
alarm.setPeriodicityStartDate(DateTime(2000, 1, 1))
# week 41 can not be in January
alarm.setPeriodicityWeekList((41, ))
alarm.setPeriodicityMonthList((1, ))
self.tic()
# next alarm date never advance
self.checkDate(alarm, DateTime(2000, 1, 1), DateTime(2000, 1, 1), DateTime(2000, 1, 1),)
def test_12_Every5Minutes(self):
alarm = self.newAlarm(enabled=True)
now = DateTime()
......
......@@ -441,13 +441,30 @@ class TestERP5Type(PropertySheetTestCase, LogInterceptor):
checkRelationUnset(self)
# Test _setRegion doesn't reindex the object.
person_object._setRegion(category_id)
def getTitleFromCatalog():
row, = self.portal.portal_catalog(
select_list=['title'],
uid=person_object.getUid(),
)
return row.title
modified_title = getTitleFromCatalog() + '_not_reindexed'
catalog_connection = self.getSQLConnection()()
catalog_connection.query(
'UPDATE catalog SET title=%s WHERE uid=%i' % (
catalog_connection.string_literal(modified_title),
person_object.getUid(),
),
)
self.commit()
self.assertFalse(person_object.hasActivity())
# sanity check
self.assertEqual(getTitleFromCatalog(), modified_title)
person_object._setRegion(category_id)
self.tic()
self.assertEqual(getTitleFromCatalog(), modified_title)
person_object.setRegion(None)
self.commit()
self.assertTrue(person_object.hasActivity())
self.tic()
self.assertNotEqual(getTitleFromCatalog(), modified_title)
# category tool, base categories, properties
# are likely to be handled specifically for accessor generation,
......
......@@ -32,6 +32,7 @@ import mock
from AccessControl.SecurityManagement import newSecurityManager
from AccessControl import Unauthorized
from AccessControl.ZopeGuards import guarded_getattr
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from Products.ERP5Type import Permissions
......@@ -238,25 +239,36 @@ class TestPerson(ERP5TypeTestCase):
self.assertTrue(person.getUserId())
self.assertFalse(self.portal.person_module._p_changed)
def testSetPasswordSecurity(self):
p = self._makeOne(id='person')
p.manage_permission(Permissions.SetOwnPassword, [], 0)
self.assertRaises(Unauthorized, p.setPassword, 'secret')
self.assertRaises(Unauthorized, p.edit, password='secret')
# setPassword(None) has no effect, because in the user interface we always
# show an empty field for password. Note that it also does not require any
# specific permission.
p.setPassword(None)
self.assertFalse(p.getPassword())
def testSetPasswordSecurityOnPerson(self):
self._testSetPasswordSecurity(
self._makeOne())
def testSetPasswordSecurityOnERP5Login(self):
self._testSetPasswordSecurity(
self._makeOne().newContent(portal_type='ERP5 Login'))
def _testSetPasswordSecurity(self, login):
login.manage_permission(Permissions.SetOwnPassword, [], 0)
with self.assertRaises(Unauthorized):
guarded_getattr(login, 'setPassword')('secret')
with self.assertRaises(Unauthorized):
guarded_getattr(login, 'edit')(password='secret')
# edit(password=None) has no effect. It's a special case, because in the user interface
# we show an empty field for password.
login.edit(password=None)
self.assertFalse(login.getPassword())
# Make sure that edit method cannot call __setPasswordByForce and nothing
# changes.
p.edit(password_by_force='waaa')
self.assertFalse(p.getPassword())
p.manage_permission(Permissions.SetOwnPassword, ['Anonymous'], 0)
p.setPassword('secret')
self.assertTrue(p.getPassword())
login.edit(password_by_force='waaa')
self.assertFalse(login.getPassword())
login.manage_permission(Permissions.SetOwnPassword, ['Anonymous'], 0)
login.setPassword('secret')
password = login.getPassword()
self.assertTrue(password)
login.edit(password=None)
self.assertEqual(login.getPassword(), password)
def testSetUserIdSecurity(self):
# Changing an already set user id needs "manage users" permissions,
......
......@@ -220,5 +220,10 @@ class TestDedup(ERP5TypeTestCase):
whl._p_deactivate()
finally:
Workflow.dedupStrings = orig_dedupStrings
self.assertEqual(deduped, [35])
self.assertEqual(len(list(whl)), 36)
# A single deduplication should have happened, as the loop exits on bucket
# split.
new_obj_length, = deduped # pylint: disable=unbalanced-tuple-unpacking
# The exact boundary does not matter much, but it should be greater than
# some arbitrary value considered satisfying.
self.assertGreaterEqual(new_obj_length, 24)
self.assertEqual(len(list(whl)), new_obj_length + 1)
......@@ -156,19 +156,19 @@ if book_include_reference_table:
image_link_list = book.WebPage_createImageOverview(book_content)
for referenced_document in book_link_list.get("reference_list", []):
book_reference_list.append(referenced_document.get("item"))
book_content = book_content.replace(referenced_document.get("input"), referenced_document.get("output"),1)
book_content = book_content.replace(referenced_document.get("input"), referenced_document.get("output"))
for applicable_document in book_link_list.get("applicable_list", []):
book_applicable_document_list.append(applicable_document.get("item"))
book_content = book_content.replace(applicable_document.get("input"), applicable_document.get("output"),1)
book_content = book_content.replace(applicable_document.get("input"), applicable_document.get("output"))
for abbreviation in book_link_list.get("abbreviation_list", []):
book_abbreviation_list.append(abbreviation.get("item"))
book_content = book_content.replace(abbreviation.get("input"), abbreviation.get("output"),1)
book_content = book_content.replace(abbreviation.get("input"), abbreviation.get("output"))
for figure in image_link_list.get("figure_list", []):
book_image_list.append(figure.get("item"))
book_content = book_content.replace(figure.get("input"), figure.get("output"), 1)
for table in table_link_list.get("table_list", []):
book_table_list.append(table.get("item"))
book_content = book_content.replace(table.get("input"), table.get("output"), 1)
book_content = book_content.replace(table.get("input"), table.get("output"))
# in order for the reference tables to be in the table of content, they must
# be added beforehand to content
......
......@@ -2,117 +2,128 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Worklist" module="erp5.portal_type"/>
<global name="Image" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_identity_criterion</string> </key>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>action</string> </key>
<value> <string encoding="cdata"><![CDATA[
ERP5Site_viewDocumentList?send_state=%(send_state)s&local_roles=%(local_roles)s&portal_type=%(portal_type)s&reset=1
]]></string> </value>
</item>
<item>
<key> <string>action_name</string> </key>
<value> <string>Documents Failed To Send (%(count)s)</string> </value>
<key> <string>_Change_local_roles_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>action_type/global</string>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>criterion_property</string> </key>
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>send_state</string>
<string>local_roles</string>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>guard_expression</string> </key>
<key> <string>_count</string> </key>
<value>
<none/>
<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>content_md5</string> </key>
<value> <string>6f858aff4a47a4f505f1391940cf1eaf</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>image/png</string> </value>
</item>
<item>
<key> <string>height</string> </key>
<value> <int>842</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>worklist_failed_to_send</string> </value>
<value> <string>template_test_book_citation_bmp</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Worklist</string> </value>
<value> <string>Image</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>failed_to_send</string> </value>
<key> <string>width</string> </key>
<value> <int>595</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
<global name="Length" module="BTrees.Length"/>
</pickle>
<pickle> <int>0</int> </pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>local_roles</string> </key>
<value>
<list>
<string>Assignor</string>
</list>
</value>
</item>
<item>
<key> <string>send_state</string> </key>
<value>
<list>
<string>failed</string>
</list>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
<none/>
</pickle>
</record>
</ZopeData>
<p>${WebPage_insertTableOfReferences}</p>
<h1>Test Citation</h1>
<p> TEST RD </p>
<p>Hello [<a href="http://www.nexedi.com" title="Nexedi">RD</a>]</p>
<p>Hello [<a href="http://www.nexedi.com" title="Nexedi">RD</a>]</p>
<p>Hello [<a href="http://www.nexedi.cn" title="Nexedi">RD</a>]</p>
<table align="center" border="1" cellpadding="1" cellspacing="1">
<caption>Companies, technologies and users</caption>
<thead>
<tr>
<th scope="col">Company</th>
<th scope="col">Country</th>
<th scope="col">Technology</th>
<th scope="col">Sample users</th>
</tr>
</thead>
</table>
<table align="center" border="1" cellpadding="1" cellspacing="1">
<caption>Companies, technologies and users</caption>
<thead>
<tr>
<th scope="col">Company</th>
<th scope="col">Country</th>
<th scope="col">Technology</th>
<th scope="col">Sample users</th>
</tr>
</thead>
</table>
<table align="center" border="1" cellpadding="1" cellspacing="1">
<caption>Company</caption>
<thead>
<tr>
<th scope="col">Company</th>
<th scope="col">Country</th>
</tr>
</thead>
</table>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Web Page" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_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>_Change_local_roles_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>_View_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>content_md5</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>template_test_book_citation_html</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Web Page</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -1644,6 +1644,24 @@ class TestCorporateIdentityTemplateList(ERP5TypeTestCase):
)
)
@changeSkin('Book')
def test_pdfBookCitation(self):
"""
"""
self.runPdfTestPattern(
"template_test_book_citation_html",
"template_test_book_citation_bmp",
"template_test_image_source_pdf",
**dict(
page_number=3,
use_skin="Book",
test_method="WebPage_exportAsBook",
format="pdf",
override_revision=1,
include_reference_table = 1
)
)
@changeSkin('Book')
def test_pdfBookPrint(self):
"""
......
......@@ -32,7 +32,7 @@ from erp5.component.document.Ticket import Ticket
from erp5.component.mixin.EncryptedPasswordMixin import EncryptedPasswordMixin
class CredentialRequest(Ticket, EncryptedPasswordMixin):
class CredentialRequest(EncryptedPasswordMixin, Ticket):
"""
"""
......@@ -54,11 +54,6 @@ class CredentialRequest(Ticket, EncryptedPasswordMixin):
, PropertySheet.Url
)
def checkUserCanChangePassword(self):
# every body can change a password of a credential request as annonymous
# should be able to do it
pass
def checkPasswordValueAcceptable(self, value):
# all passwords are acceptable on Credential Request
pass
......
translate = context.Base_translateString
item_list = [("", "")] + [
(translate(x), y) for x,y in [
("Draft", "draft"), ("Shared", "shared"), ("Released", "released"),
]
]
if context.getTypeBasedMethod('getPreferredAttachedDocumentPublicationState')() == "published":
item_list.append((translate("Published"), "published"))
return item_list
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Base_getAttachedDocumentPublicationStateItemList</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -142,7 +142,7 @@
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python: [("", ""),] + [(here.Base_translateString(x), y) for x,y in [("Draft", "draft"), ("Shared", "shared"), ("Released", "released"),]]</string> </value>
<value> <string>here/Base_getAttachedDocumentPublicationStateItemList</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -22,7 +22,7 @@
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
......@@ -151,7 +151,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>998.1956.58639.53486</string> </value>
<value> <string>998.1960.1198.56217</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -169,7 +169,7 @@
</tuple>
<state>
<tuple>
<float>1644230881.1</float>
<float>1644930593.2</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -21,7 +21,7 @@ kw['conversion_dict'] = dict(
header_spacing=5
)
kw['css_path']="payslip_css/payslip"
kw['document_language']=target_language
kw['document_language']=target_language or 'fr'
kw['start_date']=context.getStartDate() or None
kw['stop_date']=context.getStopDate() or None
kw['get_doc_after_save'] = send_to_maileva
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Zuite" module="Products.Zelenium.zuite"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>renderjs_ui_report_language_zuite</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ZopePageTemplate" module="Products.PageTemplates.ZopePageTemplate"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>expand</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>testGeneralPrint</string> </value>
</item>
<item>
<key> <string>output_encoding</string> </key>
<value> <string>utf-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <unicode></unicode> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<html xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test General Print</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr><td rowspan="1" colspan="3">Test General Print</td></tr>
</thead><tbody>
<tal:block metal:use-macro="here/Zuite_CommonTemplate/macros/init" />
<!-- Initialize -->
<tr>
<td>open</td>
<td>${base_url}/web_site_module/renderjs_runner/#/sale_order_module</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_app_loaded" />
<tal:block tal:define="click_configuration python: {'text': 'Add'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/click_on_header_link" />
</tal:block>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/submit_dialog" />
<tal:block tal:define="notification_configuration python: {'class': 'success',
'text': 'Object created.'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_notification" />
</tal:block>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tr>
<td>waitForElementPresent</td>
<td>//div[contains(@data-gadget-url, 'gadget_erp5_panel.html')]//div[contains(@data-gadget-url, 'gadget_erp5_field_multicheckbox.html')]//label</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//div[contains(@data-gadget-url, 'gadget_erp5_panel.html')]//div[contains(@data-gadget-url, 'gadget_erp5_field_multicheckbox.html')]//label</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//a[@data-i18n="Export"]</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[@data-i18n="Export"]</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//ul[@class="document-listview"]//a[text()='Print']</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//ul[@class="document-listview"]//a[text()='Print']</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//select[@id="field_your_format"]</td>
<td></td>
</tr>
<tr>
<td>type</td>
<td>//select[@id="field_your_format"]</td>
<td>html</td>
</tr>
<tr>
<td>type</td>
<td>//select[@id="field_your_target_language"]</td>
<td>fr</td>
</tr>
<!-- overwrite createObjectURL to read server response and append result to Dom -->
<tr>
<td>getEval</td>
<td>
window.URL.createObjectURL = function (blob) {
window.jIO.util.readBlobAsText(blob)
.then(function (answer) {
text_node = document.createElement('div');
text_node.innerHTML = answer.target.result;
text_node.setAttribute("id", "text_node_for_blob");
document.getElementById("selenium_myiframe").contentWindow.document.body.appendChild(text_node);
})
.then(undefined, function(error) {console.log(error);});
return '';
};
</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//input[@value="Print"]</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//button[text()='Data received.']</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//div[@id='text_node_for_blob']</td>
<td></td>
</tr>
<tr>
<td>verifyElementPresent</td>
<td>//div[@id='text_node_for_blob']//font[contains(text(), "Fournisseur")]</td>
<td></td>
</tr>
<tr>
<td>verifyElementPresent</td>
<td>//div[@id='text_node_for_blob']//font[contains(text(), "Mode de livraison")]</td>
<td></td>
</tr>
</tbody></table>
</body>
</html>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ZopePageTemplate" module="Products.PageTemplates.ZopePageTemplate"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>expand</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>testProductionOrderPrintProductionOrder</string> </value>
</item>
<item>
<key> <string>output_encoding</string> </key>
<value> <string>utf-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <unicode></unicode> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<html xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test Production Order Report</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr><td rowspan="1" colspan="3">Test Produdction Order Report</td></tr>
</thead><tbody>
<tal:block metal:use-macro="here/Zuite_CommonTemplate/macros/init" />
<!-- Initialize -->
<tr>
<td>open</td>
<td>${base_url}/web_site_module/renderjs_runner/#/production_order_module</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_app_loaded" />
<tal:block tal:define="click_configuration python: {'text': 'Add'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/click_on_header_link" />
</tal:block>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/submit_dialog" />
<tal:block tal:define="notification_configuration python: {'class': 'success',
'text': 'Object created.'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_notification" />
</tal:block>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tr>
<td>waitForElementPresent</td>
<td>//div[contains(@data-gadget-url, 'gadget_erp5_panel.html')]//div[contains(@data-gadget-url, 'gadget_erp5_field_multicheckbox.html')]//label</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//div[contains(@data-gadget-url, 'gadget_erp5_panel.html')]//div[contains(@data-gadget-url, 'gadget_erp5_field_multicheckbox.html')]//label</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//a[@data-i18n="Export"]</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[@data-i18n="Export"]</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//ul[@class="document-listview"]//a[text()='Print Production Order']</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//ul[@class="document-listview"]//a[text()='Print Production Order']</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//select[@id="field_your_format"]</td>
<td></td>
</tr>
<tr>
<td>type</td>
<td>//select[@id="field_your_format"]</td>
<td>html</td>
</tr>
<tr>
<td>type</td>
<td>//select[@id="field_your_target_language"]</td>
<td>fr</td>
</tr>
<!-- overwrite createObjectURL to read server response and append result to Dom -->
<tr>
<td>getEval</td>
<td>
window.URL.createObjectURL = function (blob) {
window.jIO.util.readBlobAsText(blob)
.then(function (answer) {
text_node = document.createElement('div');
text_node.innerHTML = answer.target.result;
text_node.setAttribute("id", "text_node_for_blob");
document.getElementById("selenium_myiframe").contentWindow.document.body.appendChild(text_node);
})
.then(undefined, function(error) {console.log(error);});
return '';
};
</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//input[@value="Print Production Order"]</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//button[text()='Data received.']</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//div[@id='text_node_for_blob']</td>
<td></td>
</tr>
<tr>
<td>verifyElementPresent</td>
<td>//div[@id='text_node_for_blob']//p[contains(text(), "Fournisseur")]</td>
<td></td>
</tr>
<tr>
<td>verifyElementPresent</td>
<td>//div[@id='text_node_for_blob']//p[contains(text(), "Date du document")]</td>
<td></td>
</tr>
</tbody></table>
</body>
</html>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ZopePageTemplate" module="Products.PageTemplates.ZopePageTemplate"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>expand</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>testProductionPackingListPrintProductionPackingList</string> </value>
</item>
<item>
<key> <string>output_encoding</string> </key>
<value> <string>utf-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <unicode></unicode> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<html xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test Production Packing List Report</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr><td rowspan="1" colspan="3">Test Produdction Packing List Report</td></tr>
</thead><tbody>
<tal:block metal:use-macro="here/Zuite_CommonTemplate/macros/init" />
<!-- Initialize -->
<tr>
<td>open</td>
<td>${base_url}/web_site_module/renderjs_runner/#/production_packing_list_module</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_app_loaded" />
<tal:block tal:define="click_configuration python: {'text': 'Add'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/click_on_header_link" />
</tal:block>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/submit_dialog" />
<tal:block tal:define="notification_configuration python: {'class': 'success',
'text': 'Object created.'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_notification" />
</tal:block>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tr>
<td>waitForElementPresent</td>
<td>//div[contains(@data-gadget-url, 'gadget_erp5_panel.html')]//div[contains(@data-gadget-url, 'gadget_erp5_field_multicheckbox.html')]//label</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//div[contains(@data-gadget-url, 'gadget_erp5_panel.html')]//div[contains(@data-gadget-url, 'gadget_erp5_field_multicheckbox.html')]//label</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//a[@data-i18n="Export"]</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[@data-i18n="Export"]</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//ul[@class="document-listview"]//a[text()='Print Production Packing List']</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//ul[@class="document-listview"]//a[text()='Print Production Packing List']</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//select[@id="field_your_format"]</td>
<td></td>
</tr>
<tr>
<td>type</td>
<td>//select[@id="field_your_format"]</td>
<td>html</td>
</tr>
<tr>
<td>type</td>
<td>//select[@id="field_your_target_language"]</td>
<td>fr</td>
</tr>
<!-- overwrite createObjectURL to read server response and append result to Dom -->
<tr>
<td>getEval</td>
<td>
window.URL.createObjectURL = function (blob) {
window.jIO.util.readBlobAsText(blob)
.then(function (answer) {
text_node = document.createElement('div');
text_node.innerHTML = answer.target.result;
text_node.setAttribute("id", "text_node_for_blob");
document.getElementById("selenium_myiframe").contentWindow.document.body.appendChild(text_node);
})
.then(undefined, function(error) {console.log(error);});
return '';
};
</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//input[@value="Print Packing List"]</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//button[text()='Data received.']</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//div[@id='text_node_for_blob']</td>
<td></td>
</tr>
<tr>
<td>verifyElementPresent</td>
<td>//div[@id='text_node_for_blob']//p[contains(text(), "Fournisseur")]</td>
<td></td>
</tr>
<tr>
<td>verifyElementPresent</td>
<td>//div[@id='text_node_for_blob']//p[contains(text(), "Date du document")]</td>
<td></td>
</tr>
</tbody></table>
</body>
</html>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ZopePageTemplate" module="Products.PageTemplates.ZopePageTemplate"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>expand</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>testProjectRelatedTaskReport</string> </value>
</item>
<item>
<key> <string>output_encoding</string> </key>
<value> <string>utf-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <unicode></unicode> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<html xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test Project Report</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr><td rowspan="1" colspan="3">Test Project Report</td></tr>
</thead><tbody>
<tal:block metal:use-macro="here/Zuite_CommonTemplate/macros/init" />
<!-- Initialize -->
<tr>
<td>open</td>
<td>${base_url}/web_site_module/renderjs_runner/#/project_module</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_app_loaded" />
<tal:block tal:define="click_configuration python: {'text': 'Add'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/click_on_header_link" />
</tal:block>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/submit_dialog" />
<tal:block tal:define="notification_configuration python: {'class': 'success',
'text': 'Object created.'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_notification" />
</tal:block>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tr>
<td>waitForElementPresent</td>
<td>//div[contains(@data-gadget-url, 'gadget_erp5_panel.html')]//div[contains(@data-gadget-url, 'gadget_erp5_field_multicheckbox.html')]//label</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//div[contains(@data-gadget-url, 'gadget_erp5_panel.html')]//div[contains(@data-gadget-url, 'gadget_erp5_field_multicheckbox.html')]//label</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//a[@data-i18n="Export"]</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[@data-i18n="Export"]</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//ul[@class="document-listview"]//a[text()='Related Tasks Report']</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//ul[@class="document-listview"]//a[text()='Related Tasks Report']</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//select[@id="field_your_format"]</td>
<td></td>
</tr>
<tr>
<td>type</td>
<td>//select[@id="field_your_format"]</td>
<td>html</td>
</tr>
<tr>
<td>type</td>
<td>//select[@id="field_your_target_language"]</td>
<td>fr</td>
</tr>
<!-- overwrite createObjectURL to read server response and append result to Dom -->
<tr>
<td>getEval</td>
<td>
window.URL.createObjectURL = function (blob) {
window.jIO.util.readBlobAsText(blob)
.then(function (answer) {
text_node = document.createElement('div');
text_node.innerHTML = answer.target.result;
text_node.setAttribute("id", "text_node_for_blob");
document.getElementById("selenium_myiframe").contentWindow.document.body.appendChild(text_node);
})
.then(undefined, function(error) {console.log(error);});
return '';
};
</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//input[@value="Related Tasks Report"]</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//button[text()='Data received.']</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//div[@id='text_node_for_blob']</td>
<td></td>
</tr>
<tr>
<td>verifyElementPresent</td>
<td>//div[@id='text_node_for_blob']//font[contains(text(), "Date de début")]</td>
<td></td>
</tr>
</tbody></table>
</body>
</html>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ZopePageTemplate" module="Products.PageTemplates.ZopePageTemplate"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>expand</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>testSaleOrderOrderSummaryReport</string> </value>
</item>
<item>
<key> <string>output_encoding</string> </key>
<value> <string>utf-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <unicode></unicode> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<html xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test Sale Order Report</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr><td rowspan="1" colspan="3">Test Sale Order Report</td></tr>
</thead><tbody>
<tal:block metal:use-macro="here/Zuite_CommonTemplate/macros/init" />
<!-- Initialize -->
<tr>
<td>open</td>
<td>${base_url}/web_site_module/renderjs_runner/#/sale_order_module</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_app_loaded" />
<tal:block tal:define="click_configuration python: {'text': 'Add'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/click_on_header_link" />
</tal:block>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/submit_dialog" />
<tal:block tal:define="notification_configuration python: {'class': 'success',
'text': 'Object created.'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_notification" />
</tal:block>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tr>
<td>waitForElementPresent</td>
<td>//div[contains(@data-gadget-url, 'gadget_erp5_panel.html')]//div[contains(@data-gadget-url, 'gadget_erp5_field_multicheckbox.html')]//label</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//div[contains(@data-gadget-url, 'gadget_erp5_panel.html')]//div[contains(@data-gadget-url, 'gadget_erp5_field_multicheckbox.html')]//label</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//a[@data-i18n="Export"]</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[@data-i18n="Export"]</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//ul[@class="document-listview"]//a[text()='Order Summary']</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//ul[@class="document-listview"]//a[text()='Order Summary']</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//select[@id="field_your_format"]</td>
<td></td>
</tr>
<tr>
<td>type</td>
<td>//select[@id="field_your_format"]</td>
<td>html</td>
</tr>
<tr>
<td>type</td>
<td>//select[@id="field_your_target_language"]</td>
<td>fr</td>
</tr>
<tr>
<td>type</td>
<td>//select[@id="field_your_quantity_unit_list"]</td>
<td>day</td>
</tr>
<!-- overwrite createObjectURL to read server response and append result to Dom -->
<tr>
<td>getEval</td>
<td>
window.URL.createObjectURL = function (blob) {
window.jIO.util.readBlobAsText(blob)
.then(function (answer) {
text_node = document.createElement('div');
text_node.innerHTML = answer.target.result;
text_node.setAttribute("id", "text_node_for_blob");
document.getElementById("selenium_myiframe").contentWindow.document.body.appendChild(text_node);
})
.then(undefined, function(error) {console.log(error);});
return '';
};
</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//input[@value="Order Summary"]</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//button[text()='Data received.']</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//div[@id='text_node_for_blob']</td>
<td></td>
</tr>
<tr>
<td>verifyElementPresent</td>
<td>//div[@id='text_node_for_blob']//font[contains(text(), "Date de livraison")]</td>
<td></td>
</tr>
</tbody></table>
</body>
</html>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ZopePageTemplate" module="Products.PageTemplates.ZopePageTemplate"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>expand</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>testSaleOrderPrintOrder</string> </value>
</item>
<item>
<key> <string>output_encoding</string> </key>
<value> <string>utf-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <unicode></unicode> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<html xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test Sale Order Report</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr><td rowspan="1" colspan="3">Test Sale Order Report</td></tr>
</thead><tbody>
<tal:block metal:use-macro="here/Zuite_CommonTemplate/macros/init" />
<!-- Initialize -->
<tr>
<td>open</td>
<td>${base_url}/web_site_module/renderjs_runner/#/sale_order_module</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_app_loaded" />
<tal:block tal:define="click_configuration python: {'text': 'Add'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/click_on_header_link" />
</tal:block>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/submit_dialog" />
<tal:block tal:define="notification_configuration python: {'class': 'success',
'text': 'Object created.'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_notification" />
</tal:block>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tr>
<td>waitForElementPresent</td>
<td>//div[contains(@data-gadget-url, 'gadget_erp5_panel.html')]//div[contains(@data-gadget-url, 'gadget_erp5_field_multicheckbox.html')]//label</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//div[contains(@data-gadget-url, 'gadget_erp5_panel.html')]//div[contains(@data-gadget-url, 'gadget_erp5_field_multicheckbox.html')]//label</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//a[@data-i18n="Export"]</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[@data-i18n="Export"]</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//ul[@class="document-listview"]//a[text()='Print Order']</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//ul[@class="document-listview"]//a[text()='Print Order']</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//select[@id="field_your_format"]</td>
<td></td>
</tr>
<tr>
<td>type</td>
<td>//select[@id="field_your_format"]</td>
<td>html</td>
</tr>
<tr>
<td>type</td>
<td>//select[@id="field_your_target_language"]</td>
<td>fr</td>
</tr>
<!-- overwrite createObjectURL to read server response and append result to Dom -->
<tr>
<td>getEval</td>
<td>
window.URL.createObjectURL = function (blob) {
window.jIO.util.readBlobAsText(blob)
.then(function (answer) {
text_node = document.createElement('div');
text_node.innerHTML = answer.target.result;
text_node.setAttribute("id", "text_node_for_blob");
document.getElementById("selenium_myiframe").contentWindow.document.body.appendChild(text_node);
})
.then(undefined, function(error) {console.log(error);});
return '';
};
</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//input[@value="Print Order"]</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//button[text()='Data received.']</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//div[@id='text_node_for_blob']</td>
<td></td>
</tr>
<tr>
<td>verifyElementPresent</td>
<td>//div[@id='text_node_for_blob']//p[contains(text(), "Fournisseur")]</td>
<td></td>
</tr>
<tr>
<td>verifyElementPresent</td>
<td>//div[@id='text_node_for_blob']//p[contains(text(), "Date du document")]</td>
<td></td>
</tr>
</tbody></table>
</body>
</html>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ZopePageTemplate" module="Products.PageTemplates.ZopePageTemplate"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>expand</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>testSalePackingListPrintPackingList</string> </value>
</item>
<item>
<key> <string>output_encoding</string> </key>
<value> <string>utf-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <unicode></unicode> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<html xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test Sale Packing List Report</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr><td rowspan="1" colspan="3">Test Sale Packing List Report</td></tr>
</thead><tbody>
<tal:block metal:use-macro="here/Zuite_CommonTemplate/macros/init" />
<!-- Initialize -->
<tr>
<td>open</td>
<td>${base_url}/web_site_module/renderjs_runner/#/sale_packing_list_module</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_app_loaded" />
<tal:block tal:define="click_configuration python: {'text': 'Add'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/click_on_header_link" />
</tal:block>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/submit_dialog" />
<tal:block tal:define="notification_configuration python: {'class': 'success',
'text': 'Object created.'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_notification" />
</tal:block>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tr>
<td>waitForElementPresent</td>
<td>//div[contains(@data-gadget-url, 'gadget_erp5_panel.html')]//div[contains(@data-gadget-url, 'gadget_erp5_field_multicheckbox.html')]//label</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//div[contains(@data-gadget-url, 'gadget_erp5_panel.html')]//div[contains(@data-gadget-url, 'gadget_erp5_field_multicheckbox.html')]//label</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//a[@data-i18n="Export"]</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[@data-i18n="Export"]</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//ul[@class="document-listview"]//a[text()='Print Packing List']</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//ul[@class="document-listview"]//a[text()='Print Packing List']</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//select[@id="field_your_format"]</td>
<td></td>
</tr>
<tr>
<td>type</td>
<td>//select[@id="field_your_format"]</td>
<td>html</td>
</tr>
<tr>
<td>type</td>
<td>//select[@id="field_your_target_language"]</td>
<td>fr</td>
</tr>
<!-- overwrite createObjectURL to read server response and append result to Dom -->
<tr>
<td>getEval</td>
<td>
window.URL.createObjectURL = function (blob) {
window.jIO.util.readBlobAsText(blob)
.then(function (answer) {
text_node = document.createElement('div');
text_node.innerHTML = answer.target.result;
text_node.setAttribute("id", "text_node_for_blob");
document.getElementById("selenium_myiframe").contentWindow.document.body.appendChild(text_node);
})
.then(undefined, function(error) {console.log(error);});
return '';
};
</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//input[@value="Print Packing List"]</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//button[text()='Data received.']</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//div[@id='text_node_for_blob']</td>
<td></td>
</tr>
<tr>
<td>verifyElementPresent</td>
<td>//div[@id='text_node_for_blob']//p[contains(text(), "Fournisseur")]</td>
<td></td>
</tr>
<tr>
<td>verifyElementPresent</td>
<td>//div[@id='text_node_for_blob']//p[contains(text(), "Date du document")]</td>
<td></td>
</tr>
</tbody></table>
</body>
</html>
\ No newline at end of file
##############################################################################
#
# Copyright (c) 2011 Nexedi SARL and Contributors. All Rights Reserved.
# Kazuhiko <kazuhiko@nexedi.com>
# Rafael Monnerat <rafael@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 unittest
from Products.ERP5Type.tests.ERP5TypeFunctionalTestCase import ERP5TypeFunctionalTestCase
class TestFunctionalRJSReportLanguage(ERP5TypeFunctionalTestCase):
foreground = 0
run_only = "renderjs_ui_report_language_zuite"
def afterSetUp(self):
ERP5TypeFunctionalTestCase.afterSetUp(self)
portal = self.getPortalObject()
portal.portal_preferences.default_site_preference.edit(preferred_report_style="ODS")
if portal.portal_preferences.default_site_preference.getPreferenceState() !='global':
portal.portal_preferences.default_site_preference.enable()
if not getattr(portal.portal_categories.quantity_unit, 'day', None):
portal.portal_categories.quantity_unit.newContent(
portal_type='Category',
id = 'day',
title='Day'
)
self.tic()
def getBusinessTemplateList(self):
return (
'erp5_web_renderjs_ui',
'erp5_web_renderjs_ui_test',
'erp5_ui_test_core',
'erp5_trade',
)
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestFunctionalRJSReportLanguage))
return suite
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Test Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testFunctionalRJSReportLanguage</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>test.erp5.testFunctionalRJSReportLanguage</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Test 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/>
</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">AAAAAAAAAAM=</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/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<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">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<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> <string>validate</string> </value>
</item>
<item>
<key> <string>error_message</string> </key>
<value>
<list>
<persistent> <string encoding="base64">AAAAAAAAAAU=</string> </persistent>
</list>
</value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="5" aka="AAAAAAAAAAU=">
<pickle>
<global name="Message" module="Products.ERP5Type.Message"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default</string> </key>
<value> <string>ID is invalid, should be \'${id_prefix}.VERSION.REFERENCE\'</string> </value>
</item>
<item>
<key> <string>domain</string> </key>
<value> <string>erp5_ui</string> </value>
</item>
<item>
<key> <string>mapping</string> </key>
<value>
<dictionary>
<item>
<key> <string>id_prefix</string> </key>
<value> <string>test</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>message</string> </key>
<value> <string>ID is invalid, should be \'${id_prefix}.VERSION.REFERENCE\'</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
erp5_web_renderjs_ui_test
erp5_trade
erp5_ods_style
erp5_mrp
erp5_l10n_fr
\ No newline at end of file
portal_tests/renderjs_ui_report_language_zuite
portal_tests/renderjs_ui_report_language_zuite/**
\ No newline at end of file
test.erp5.testFunctionalRJSReportLanguage
\ No newline at end of file
erp5_full_text_mroonga_catalog
\ No newline at end of file
erp5_report_renderjs_ui_test
\ No newline at end of file
"""
This script should returns always two list of Business Template.
- The first list is to resolve dependencies and upgrade.
- The second list is what you want to keep. This is useful if we want to keep
a old business template without updating it and without removing it
This script may be overridden.
It is expected to return two lists of Business Templates:
- The first is a tuple containing names of Business Templates to install and
keep up-to-date. Their dependencies are also installed and kept up-to-date.
- The second is a list containing names of Business Templates which should be
kept if already installed, ignored if missing, and not be upgraded nor
removed.
"""
return ('erp5_base',), ["erp5_upgrader"]
return (
tuple(context.getPortalObject().getCoreBusinessTemplateList() + ['erp5_base']),
['erp5_upgrader'],
)
return ('erp5_web',), ["erp5_upgrader", "erp5_upgrader_test"]
return (
tuple(context.getPortalObject().getCoreBusinessTemplateList() + ['erp5_web']),
["erp5_upgrader", "erp5_upgrader_test"],
)
......@@ -79,7 +79,7 @@ class WebSite(WebSection):
if name in language_list:
default_language = self.getDefaultAvailableLanguage()
if request.get('AcceptLanguage') is not None:
request['AcceptLanguage'].set(name, 100)
request['AcceptLanguage'].set(name, 10)
request.set(WEBSITE_LANGUAGE_KEY, name)
if self.isTempObject() or name == default_language:
redirect_path_list = [self.getOriginalDocument().absolute_url()]
......
......@@ -28,6 +28,7 @@ def generateDocumentListHTML(result_list, document_list):
if (document_list):
result_list.append('<aside id="document_list"><ul class="h-feed">')
for section in document_list:
publication_date = section['effective_date'] or section['modification_date']
result_list.append("""
<li class="h-entry">
<div class="e-content">
......@@ -41,8 +42,8 @@ def generateDocumentListHTML(result_list, document_list):
('<p class="p-summary">%s</p>' % _(section['description'])) if section.get('description') else '',
('<p class="p-author h-card">%s</p>' % _(section['document'].Document_getContributorTitleList()[0])),
__(section['url']),
__(section['modification_date'].HTML4()),
_(section['modification_date'].rfc822())
__(publication_date.HTML4()),
_(publication_date.rfc822())
))
result_list.append('</ul></aside>')
......@@ -75,6 +76,6 @@ result_list.append('</nav>')
# Documents
if include_document:
generateDocumentListHTML(result_list, web_section.WebSection_getSiteMapTree(include_subsection=False, exclude_default_document=True, depth=1, property_mapping=('translated_title', 'description', 'modification_date')))
generateDocumentListHTML(result_list, web_section.WebSection_getSiteMapTree(include_subsection=False, exclude_default_document=True, depth=1, property_mapping=('translated_title', 'description', 'effective_date', 'modification_date')))
return ''.join(result_list)
......@@ -90,7 +90,7 @@
</tr>
<tr>
<td>assertElementPresent</td>
<td>//aside[@id='document_list']//p[contains(text(), 'Date: ') and contains(text(), 'GMT')]</td>
<td>//aside[@id='document_list']//p[text()='Date: Tue, 13 Dec 2011 06:22:33 GMT']</td>
<td></td>
</tr>
......
......@@ -83,7 +83,7 @@
</tr>
<tr>
<td>assertElementPresent</td>
<td>//aside[@id='document_list']//ul[@class="h-feed"]//li[@class="h-entry"]//a[@class="u-url" and @rel='permalink' and contains(@href, 'web_site_module/erp5_web_js_style_test_site/erp5_web_js_style_test_contentpage')]//time[@class="dt-published" and contains(@datetime, 'T')]</td>
<td>//aside[@id='document_list']//ul[@class="h-feed"]//li[@class="h-entry"]//a[@class="u-url" and @rel='permalink' and contains(@href, 'web_site_module/erp5_web_js_style_test_site/erp5_web_js_style_test_contentpage')]//time[@class="dt-published" and text()='Tue, 13 Dec 2011 11:22:33 +0500' and @datetime='2011-12-13T06:22:33Z']</td>
<td></td>
</tr>
......
......@@ -78,7 +78,7 @@
</tr>
<tr>
<td>assertElementPresent</td>
<td>//aside[@id='document_list']//p[contains(text(), 'Date: ') and contains(text(), 'GMT')]</td>
<td>//aside[@id='document_list']//p[text()='Date: Tue, 13 Dec 2011 06:22:33 GMT']</td>
<td></td>
</tr>
......@@ -341,7 +341,7 @@
</tr>
<tr>
<td>assertElementPresent</td>
<td>//aside[@id='document_list']//p[contains(text(), 'Date: ') and contains(text(), 'GMT')]</td>
<td>//aside[@id='document_list']//p[text()='Date: Tue, 13 Dec 2011 06:22:33 GMT']</td>
<td></td>
</tr>
......
......@@ -78,7 +78,7 @@
</tr>
<tr>
<td>assertElementPresent</td>
<td>//aside[@id='document_list']//p[contains(text(), 'Date: ') and contains(text(), 'GMT')]</td>
<td>//aside[@id='document_list']//p[text()='Date: Tue, 13 Dec 2011 06:22:33 GMT']</td>
<td></td>
</tr>
......@@ -315,7 +315,7 @@
</tr>
<tr>
<td>assertElementPresent</td>
<td>//aside[@id='document_list']//p[contains(text(), 'Date: ') and contains(text(), 'GMT')]</td>
<td>//aside[@id='document_list']//p[text()='Date: Tue, 13 Dec 2011 06:22:33 GMT']</td>
<td></td>
</tr>
......
......@@ -73,7 +73,7 @@
</tr>
<tr>
<td>assertElementPresent</td>
<td>//aside[@id='document_list']//p[contains(text(), 'Date: ') and contains(text(), 'GMT')]</td>
<td>//aside[@id='document_list']//p[text()='Date: Tue, 13 Dec 2011 06:22:33 GMT']</td>
<td></td>
</tr>
......@@ -143,7 +143,7 @@
</tr>
<tr>
<td>assertElementPresent</td>
<td>//aside[@id='document_list']//p[contains(text(), 'Date: ') and contains(text(), 'GMT')]</td>
<td>//aside[@id='document_list']//p[text()='Date: Tue, 13 Dec 2011 06:22:33 GMT']</td>
<td></td>
</tr>
......@@ -213,7 +213,7 @@
</tr>
<tr>
<td>assertElementPresent</td>
<td>//aside[@id='document_list']//p[contains(text(), 'Date: ') and contains(text(), 'GMT')]</td>
<td>//aside[@id='document_list']//p[text()='Date: Tue, 13 Dec 2011 06:22:33 GMT']</td>
<td></td>
</tr>
......@@ -283,7 +283,7 @@
</tr>
<tr>
<td>assertElementPresent</td>
<td>//aside[@id='document_list']//p[contains(text(), 'Date: ') and contains(text(), 'GMT')]</td>
<td>//aside[@id='document_list']//p[text()='Date: Tue, 13 Dec 2011 06:22:33 GMT']</td>
<td></td>
</tr>
......
......@@ -73,7 +73,7 @@
</tr>
<tr>
<td>assertElementPresent</td>
<td>//aside[@id='document_list']//p[contains(text(), 'Date: ') and contains(text(), 'GMT')]</td>
<td>//aside[@id='document_list']//p[text()='Date: Tue, 13 Dec 2011 06:22:33 GMT']</td>
<td></td>
</tr>
......@@ -143,7 +143,7 @@
</tr>
<tr>
<td>assertElementPresent</td>
<td>//aside[@id='document_list']//p[contains(text(), 'Date: ') and contains(text(), 'GMT')]</td>
<td>//aside[@id='document_list']//p[text()='Date: Tue, 13 Dec 2011 06:22:33 GMT']</td>
<td></td>
</tr>
......@@ -213,7 +213,7 @@
</tr>
<tr>
<td>assertElementPresent</td>
<td>//aside[@id='document_list']//p[contains(text(), 'Date: ') and contains(text(), 'GMT')]</td>
<td>//aside[@id='document_list']//p[text()='Date: Tue, 13 Dec 2011 06:22:33 GMT']</td>
<td></td>
</tr>
......@@ -283,7 +283,7 @@
</tr>
<tr>
<td>assertElementPresent</td>
<td>//aside[@id='document_list']//p[contains(text(), 'Date: ') and contains(text(), 'GMT')]</td>
<td>//aside[@id='document_list']//p[text()='Date: Tue, 13 Dec 2011 06:22:33 GMT']</td>
<td></td>
</tr>
......
......@@ -73,7 +73,7 @@
</tr>
<tr>
<td>assertElementPresent</td>
<td>//aside[@id='document_list']//p[contains(text(), 'Date: ') and contains(text(), 'GMT')]</td>
<td>//aside[@id='document_list']//p[text()='Date: Tue, 13 Dec 2011 06:22:33 GMT']</td>
<td></td>
</tr>
......@@ -138,7 +138,7 @@
</tr>
<tr>
<td>assertElementPresent</td>
<td>//aside[@id='document_list']//p[contains(text(), 'Date: ') and contains(text(), 'GMT')]</td>
<td>//aside[@id='document_list']//p[text()='Date: Tue, 13 Dec 2011 06:22:33 GMT']</td>
<td></td>
</tr>
......@@ -203,7 +203,7 @@
</tr>
<tr>
<td>assertElementPresent</td>
<td>//aside[@id='document_list']//p[contains(text(), 'Date: ') and contains(text(), 'GMT')]</td>
<td>//aside[@id='document_list']//p[text()='Date: Tue, 13 Dec 2011 06:22:33 GMT']</td>
<td></td>
</tr>
......@@ -268,7 +268,7 @@
</tr>
<tr>
<td>assertElementPresent</td>
<td>//aside[@id='document_list']//p[contains(text(), 'Date: ') and contains(text(), 'GMT')]</td>
<td>//aside[@id='document_list']//p[text()='Date: Tue, 13 Dec 2011 06:22:33 GMT']</td>
<td></td>
</tr>
......
......@@ -78,7 +78,7 @@
</tr>
<tr>
<td>assertElementPresent</td>
<td>//aside[@id='document_list']//p[contains(text(), 'Date: ') and contains(text(), 'GMT')]</td>
<td>//aside[@id='document_list']//p[text()='Date: Tue, 13 Dec 2011 06:22:33 GMT']</td>
<td></td>
</tr>
......@@ -139,7 +139,7 @@
</tr>
<tr>
<td>assertElementPresent</td>
<td>//aside[@id='document_list']//p[contains(text(), 'Date: ') and contains(text(), 'GMT')]</td>
<td>//aside[@id='document_list']//p[text()='Date: Tue, 13 Dec 2011 06:22:33 GMT']</td>
<td></td>
</tr>
......@@ -209,7 +209,7 @@
</tr>
<tr>
<td>assertElementPresent</td>
<td>//aside[@id='document_list']//p[contains(text(), 'Date: ') and contains(text(), 'GMT')]</td>
<td>//aside[@id='document_list']//p[text()='Date: Tue, 13 Dec 2011 06:22:33 GMT']</td>
<td></td>
</tr>
......@@ -270,7 +270,7 @@
</tr>
<tr>
<td>assertElementPresent</td>
<td>//aside[@id='document_list']//p[contains(text(), 'Date: ') and contains(text(), 'GMT')]</td>
<td>//aside[@id='document_list']//p[text()='Date: Tue, 13 Dec 2011 06:22:33 GMT']</td>
<td></td>
</tr>
......
......@@ -78,7 +78,7 @@
</tr>
<tr>
<td>assertElementPresent</td>
<td>//aside[@id='document_list']//p[contains(text(), 'Date: ') and contains(text(), 'GMT')]</td>
<td>//aside[@id='document_list']//p[text()='Date: Tue, 13 Dec 2011 06:22:33 GMT']</td>
<td></td>
</tr>
......@@ -178,7 +178,7 @@
</tr>
<tr>
<td>assertElementPresent</td>
<td>//aside[@id='document_list']//p[contains(text(), 'Date: ') and contains(text(), 'GMT')]</td>
<td>//aside[@id='document_list']//p[text()='Date: Tue, 13 Dec 2011 06:22:33 GMT']</td>
<td></td>
</tr>
......@@ -248,7 +248,7 @@
</tr>
<tr>
<td>assertElementPresent</td>
<td>//aside[@id='document_list']//p[contains(text(), 'Date: ') and contains(text(), 'GMT')]</td>
<td>//aside[@id='document_list']//p[text()='Date: Tue, 13 Dec 2011 06:22:33 GMT']</td>
<td></td>
</tr>
......@@ -348,7 +348,7 @@
</tr>
<tr>
<td>assertElementPresent</td>
<td>//aside[@id='document_list']//p[contains(text(), 'Date: ') and contains(text(), 'GMT')]</td>
<td>//aside[@id='document_list']//p[text()='Date: Tue, 13 Dec 2011 06:22:33 GMT']</td>
<td></td>
</tr>
......
......@@ -34,6 +34,8 @@ web_page_content_en_id = "erp5_web_js_style_test_contentpage_en"
web_page_content_fr_id = "erp5_web_js_style_test_contentpage_fr"
web_page_content_zh_id = "erp5_web_js_style_test_contentpage_zh"
publicate_date = DateTime('2011/12/13 11:22:33 GMT+5')
### English web page
module = portal.getDefaultModule(web_page_portal_type)
if getattr(module, web_page_frontend_en_id, None) is not None:
......@@ -43,6 +45,7 @@ web_page = module.newContent(
id=web_page_frontend_en_id,
reference=web_page_frontend_reference,
contributor_value=contributor,
effective_date=publicate_date,
language="en",
version="001",
text_content="""
......@@ -61,6 +64,7 @@ web_page = module.newContent(
id=web_page_content_en_id,
reference=web_page_content_reference,
contributor_value=contributor,
effective_date=publicate_date,
title="%s title" % web_page_content_reference,
description="%s description" % web_page_content_reference,
language="en",
......@@ -79,6 +83,7 @@ web_page = module.newContent(
id=web_page_frontend_fr_id,
reference=web_page_frontend_reference,
contributor_value=contributor,
effective_date=publicate_date,
language="fr",
version="001",
text_content="""
......@@ -97,6 +102,7 @@ web_page = module.newContent(
id=web_page_content_fr_id,
reference=web_page_content_reference,
contributor_value=contributor,
effective_date=publicate_date,
title="%s title" % web_page_content_reference,
description="%s description" % web_page_content_reference,
language="fr",
......@@ -115,6 +121,7 @@ web_page = module.newContent(
id=web_page_frontend_zh_id,
reference=web_page_frontend_reference,
contributor_value=contributor,
effective_date=publicate_date,
language="zh",
version="001",
text_content="""
......@@ -133,6 +140,7 @@ web_page = module.newContent(
id=web_page_content_zh_id,
reference=web_page_content_reference,
contributor_value=contributor,
effective_date=publicate_date,
title="%s title" % web_page_content_reference,
description="%s description" % web_page_content_reference,
language="zh",
......
......@@ -92,6 +92,15 @@ div[data-gadget-url$="gadget_erp5_page_project_front_page.html"] .front-project-
margin-bottom: 8px;
}
div[data-gadget-url$="gadget_erp5_page_project_front_page.html"] .front-project-list .project-box .project-title a {
color: #267B87;
font-weight: normal;
font-size: 14px;
line-height: 33px;
margin-bottom: 8px;
margin-left: 14px;
}
div[data-gadget-url$="gadget_erp5_page_project_front_page.html"] .front-project-list .project-box span.margined {
margin-right: 5px;
}
......
......@@ -242,7 +242,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>982.36779.34422.1348</string> </value>
<value> <string>998.5120.55627.52002</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -260,7 +260,7 @@
</tuple>
<state>
<tuple>
<float>1584457148.55</float>
<float>1644420658.08</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -301,11 +301,15 @@
function createProjectHtmlElement(project_id, project_title,
project_url, supervisor, supervisor_url) {
var project_link = domsugar('a', {
href: project_url
}, [project_title]),
//title_div will have 2 elements: span (with title text) and a link (with text "Project Page")
//update styles: " div[data-gadget-url$="gadget_erp5_page_project_front_page.html"] a" to be a smaller text
var project_title_span = domsugar('span', {}, [project_title]),
project_link = domsugar('a', {
href: project_url,
id: project_title + "-project_page_link"
}, ["(Project Page)"]),
title_div = domsugar('div', { class: "project-title" },
[project_link]),
[project_title_span, project_link]),
left_info_div = domsugar('div', { class: "project-left" }),
supervisor_field_label = domsugar('label', {}, [SUPERVISOR_FIELD_TITLE]),
supervisor_value_link = domsugar('a', {
......@@ -562,6 +566,7 @@
})
.declareJob("detachRenderProjectForumLink", function () {
return;
var gadget = this,
i,
forum_link_html,
......
......@@ -234,7 +234,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>982.41488.58477.39116</string> </value>
<value> <string>998.5123.35694.4864</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1584697605.84</float>
<float>1644428660.52</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -92,12 +92,17 @@
<!-- Check project present -->
<tr>
<td>assertElementPresent</td>
<td>//div[@class='front-project-list']//div[@class='project-title']//a[text()='test-project']</td>
<td>//div[@class='front-project-list']//div[@class='project-title']//span[text()='test-project']</td>
<td></td>
</tr>
<tr>
<td>assertElementPresent</td>
<td>//div[@class='front-project-list']//div[@class='project-title']//a[text()='(Project Page)']</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//div[@class='front-project-list']//div[@class='project-title']//a[text()='test-project']</td>
<td>//div[@class='front-project-list']//div[@class='project-title']//a[text()='(Project Page)']</td>
<td></td>
</tr>
<!-- check project quick view page elements -->
......
......@@ -66,13 +66,13 @@
<!-- Check draft project -->
<tr>
<td>assertElementNotPresent</td>
<td>//div[@class='front-project-list']//div[@class='project-title']//a[text()='draft-project']</td>
<td>//div[@class='front-project-list']//div[@class='project-title']//span[text()='draft-project']</td>
<td></td>
</tr>
<!-- Check empty project -->
<tr>
<td>assertElementPresent</td>
<td>//div[@class='front-project-list']//div[@class='project-title']//a[text()='empty-project']</td>
<td>//div[@class='front-project-list']//div[@class='project-title']//span[text()='empty-project']</td>
<td></td>
</tr>
<!-- Check empty Task line -->
......@@ -158,12 +158,12 @@
<!-- Check documented project -->
<tr>
<td>assertElementPresent</td>
<td>//div[@class='front-project-list']//div[@class='project-title']//a[text()='documented-project']</td>
<td>//div[@class='front-project-list']//div[@class='project-title']//span[text()='documented-project']</td>
<td></td>
</tr>
<!-- Check Task line -->
<tr>
<td>assertElementPresent</td>
<td>waitForElementPresent</td>
<td>//div[@class='front-project-list']//div[@class='project-line']//span[@class='status none margined green' and contains(@id, 'documented-project') and contains(@id, 'Task') and contains(@id, '-status')]</td>
<td></td>
</tr>
......@@ -266,16 +266,16 @@
<td>//div[@class='front-project-list']//div[@class='project-line']//span[contains(@id, 'documented-project') and contains(@id, 'Task-Report') and contains(@id, '-outdated') and text()='0']</td>
<td></td>
</tr>
<!-- Check Forum link -->
<!-- Check Forum link
<tr>
<td>assertElementPresent</td>
<td>//div[@class='front-project-list']//div[@class='project-right']//a[contains(@id, 'documented-project') and contains(@id, 'link-forum') and @href='test-forum-link' and text()='Project Forum']</td>
<td></td>
</tr>
</tr> -->
<!-- Check failed project -->
<tr>
<td>assertElementPresent</td>
<td>//div[@class='front-project-list']//div[@class='project-title']//a[text()='failed-project']</td>
<td>//div[@class='front-project-list']//div[@class='project-title']//span[text()='failed-project']</td>
<td></td>
</tr>
<tr>
......
......@@ -55,12 +55,17 @@
<!-- Check project present -->
<tr>
<td>assertElementPresent</td>
<td>//div[@class='front-project-list']//div[@class='project-title']//a[text()='test-project-home']</td>
<td>//div[@class='front-project-list']//div[@class='project-title']//span[text()='test-project-home']</td>
<td></td>
</tr>
<tr>
<td>assertElementPresent</td>
<td>//div[@class='front-project-list']//div[@class='project-title']//a[@id='test-project-home-project_page_link' and text()='(Project Page)']</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//div[@class='front-project-list']//div[@class='project-title']//a[text()='test-project-home']</td>
<td>//div[@class='front-project-list']//div[@class='project-title']//a[@id='test-project-home-project_page_link' and text()='(Project Page)']</td>
<td></td>
</tr>
<!-- check project quick view page elements -->
......
......@@ -85,7 +85,7 @@
</item>
<item>
<key> <string>title</string> </key>
<value> <string>User Id</string> </value>
<value> <string>User ID</string> </value>
</item>
</dictionary>
</value>
......
......@@ -61,7 +61,11 @@ class ActivityRuntimeEnvironment(object):
def getPriority(self):
result = self._priority
if result is None:
return self._message.line.priority
message = self._message
line = message.line
if line is None:
return message.activity_kw.get('priority', 1)
return line.priority
return result
security.declarePublic('edit')
......
......@@ -197,6 +197,7 @@ class Message(BaseMessage):
traceback = None
document_uid = None
is_registered = False
line = None
def __init__(
self,
......
......@@ -66,7 +66,9 @@ def for_each_activity(wrapped):
for activity in ActivityTool.activity_dict:
wrapped(self, activity)
self.abort()
self.assertFalse(getMessageList())
self.assertFalse([
x.__dict__ for x in getMessageList()
])
return wraps(wrapped)(wrapper)
def registerFailingTransactionManager(*args, **kw):
......@@ -314,6 +316,7 @@ class TestCMFActivity(ERP5TypeTestCase, LogInterceptor):
self.commit()
self.assertEqual(organisation.getTitle(),self.title1)
self.assertEqual(organisation.getDescription(),self.title1)
self.tic()
@for_each_activity
def testTryTwoMethodsAndFlushThem(self, activity):
......@@ -427,6 +430,9 @@ class TestCMFActivity(ERP5TypeTestCase, LogInterceptor):
result = active_process.getResultList()[0]
self.assertEqual(result.method_id , 'getTitle')
self.assertEqual(result.result , self.title1)
# Execute any further activity which may have been spawned by activity
# execution (ex: fulltext indeation of the active process).
self.tic()
def TryActiveProcessWithResultDict(self, activity):
"""
......@@ -456,6 +462,9 @@ class TestCMFActivity(ERP5TypeTestCase, LogInterceptor):
result = result_dict[3]
self.assertEqual(result_dict[3].method_id, 'getTitle')
self.assertEqual(result.result , self.title1)
# Execute any further activity which may have been spawned by activity
# execution (ex: fulltext indeation of the active process).
self.tic()
@for_each_activity
def testTryMethodAfterMethod(self, activity):
......@@ -2339,10 +2348,24 @@ class TestCMFActivity(ERP5TypeTestCase, LogInterceptor):
obj = activity_tool.newActiveProcess()
obj.reindexObject(activate_kw={'tag': 'foo', 'after_tag': 'bar'})
self.commit()
# Check that both messages were inserted.
# Also serves as a sanity check on indexation activities group_method_id.
indexation_group_metdod_id = 'portal_catalog/catalogObjectList'
self.assertEqual(
len([
x
for x in activity_tool.getMessageList(path=obj.getPath())
if x.activity_kw.get('group_method_id') == indexation_group_metdod_id
]),
2,
)
invoked = []
def invokeGroup(self, *args):
invoked.append(len(args[1]))
return ActivityTool_invokeGroup(self, *args)
def invokeGroup(self, method_id, message_list, *args):
# Ignore any other activity which may be spawned from these catalog
# indexations (ex: fulltext indexations).
if method_id == indexation_group_metdod_id:
invoked.append(len(message_list))
return ActivityTool_invokeGroup(self, method_id, message_list, *args)
ActivityTool_invokeGroup = activity_tool.__class__.invokeGroup
try:
activity_tool.__class__.invokeGroup = invokeGroup
......
......@@ -114,13 +114,13 @@ class WebSiteTraversalHook(WebSectionTraversalHook):
default_language = container.getDefaultAvailableLanguage()
if default_language and container.isStaticLanguageSelection():
if request.get('AcceptLanguage') is not None:
request['AcceptLanguage'].set(default_language, 80)
request['AcceptLanguage'].set(default_language, 8)
else:
accept_language = request.get('AcceptLanguage')
if accept_language is not None:
selected_language = accept_language.select_language(
container.getAvailableLanguageList())
if selected_language:
request['AcceptLanguage'].set(selected_language, 80)
request['AcceptLanguage'].set(selected_language, 8)
elif default_language:
request['AcceptLanguage'].set(default_language, 80)
request['AcceptLanguage'].set(default_language, 8)
......@@ -279,6 +279,20 @@ class ERP5Site(ResponseHeaderGenerator, FolderMixIn, PortalObjectBase, CacheCook
PortalObjectBase.__init__(self, id)
self.creation_date = DateTime()
security.declarePublic('getCoreBusinessTemplateList')
def getCoreBusinessTemplateList(self):
# Return the list of business templates expected to be installed when this
# class is instanciated. Allows including these business templates in the
# list of business templates to upgrade (ex: in upgrade unit tests) without
# duplicating this list.
return [
'erp5_property_sheets',
'erp5_core',
self.erp5_catalog_storage,
'erp5_jquery',
'erp5_xhtml_style',
]
security.declarePrivate('reindexObject')
def reindexObject(self, idxs=[]):
"""from Products.CMFDefault.Portal"""
......@@ -2406,8 +2420,7 @@ class ERP5Generator(PortalGenerator):
"""
template_tool = p.portal_templates
if template_tool.getInstalledBusinessTemplate('erp5_core') is None:
for bt in ('erp5_property_sheets', 'erp5_core', p.erp5_catalog_storage, 'erp5_jquery',
'erp5_xhtml_style'):
for bt in p.getCoreBusinessTemplateList():
if not bt:
continue
url = getBootstrapBusinessTemplateUrl(bt)
......
......@@ -144,12 +144,24 @@ class InteractionWorkflowDefinition (DCWorkflowDefinition, ActiveObject):
security.declarePrivate('activeScript')
def activeScript(self, script_name, ob_url, status, tdef_id):
script = self.scripts[script_name]
ob = self.unrestrictedTraverse(ob_url)
tdef = self.interactions.get(tdef_id)
sci = StateChangeInfo(
ob, self, status, tdef, None, None, None)
script(sci)
# BBB for when an upgrade to callInterationScript still has unexecuted
# activeScript activities leftover from the previous code.
self.callInterationScript(
script_name=script_name,
ob=self.unrestrictedTraverse(ob_url),
status=status,
tdef_id=tdef_id,
)
security.declarePrivate('callInterationScript')
def callInterationScript(self, script_name, ob, status, tdef_id):
self.scripts[script_name](
StateChangeInfo(
ob, self, status,
self.interactions.get(tdef_id),
None, None, None,
),
)
def _checkTransitionGuard(self, t, ob, **kw):
# This check can be implemented with a guard expression, but
......
......@@ -195,6 +195,9 @@ class DiscoverableMixin(CachedConvertableMixin):
document.share()
elif publication_state == "released":
document.release()
elif publication_state == "published":
document.publish()
maybeChangeState(self)
# Finish ingestion by calling method
......
......@@ -6,6 +6,12 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>DiscoverableMixin</string> </value>
......@@ -53,13 +59,28 @@
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
<persistent> <string encoding="base64">AAAAAAAAAAM=</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/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
......@@ -72,7 +93,7 @@
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
......@@ -81,7 +102,7 @@
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
......
# - Years always starts at 0h of the current year's first day and
# - Years always starts at 0h of the current year's first day and
# finish 0h of the next year's first day.
from Products.ERP5Type.Message import translateString
......@@ -27,21 +27,21 @@ if depth == 0:
count = 0
while count < 12:
# Create one Temp Object
o = newTempBase(portal, id='year' ,uid='new_%s' % zfill('year',4))
# Seting delimiter
o = newTempBase(portal, id='year', uid='new_%s' % zfill('year',4))
# Setting delimiter
if current_date.month() in [1, 7]:
o.setProperty('delimiter_type', 1)
else:
o.setProperty('delimiter_type', 0)
# Setting Axis Dates start and stop
o.setProperty('start',current_date)
o.setProperty('start', current_date)
if current_date.month() != 12:
stop_date = DateTime(current_date.year(),current_date.month() +1,1)
stop_date = DateTime(current_date.year(), current_date.month() +1, 1)
else:
stop_date = DateTime(year+1, 1, 1)
o.setProperty('stop', stop_date)
o.setProperty('relative_position', int(current_date))
title = translateString('${month_name} ${year}',
......@@ -50,9 +50,9 @@ if depth == 0:
o.setProperty('title', title)
# Defining Link
link = '%s&bound_start=%s&lane_path=base_month_domain' % ( default_link_url, url_quote(str(current_date)))
link = '%s&bound_start=%s&lane_path=base_month_domain' % (default_link_url, url_quote(str(current_date)))
o.setProperty('link', link)
category_list.append(o)
current_date = DateTime(str(current_date.year()) + '/' + str((current_date.month() +1)) + '/1')
count += 1
......@@ -61,12 +61,12 @@ else:
for category in category_list:
domain = parent.generateTempDomain(id = 'sub' + category.getProperty('id'))
domain.edit(title = category.getTitle(),
membership_criterion_base_category = ('parent', ),
membership_criterion_category = (category,),
domain_generator_method_id = script.id,
uid = category.getUid())
domain.edit(title=category.getTitle(),
membership_criterion_base_category=('parent', ),
membership_criterion_category=(category, ),
domain_generator_method_id=script.id,
uid=category.getUid())
domain_list.append(domain)
return domain_list
......@@ -24,7 +24,8 @@ for value in value_list:
new_dict['value'] = value
sub_field_list.append(new_dict)
sub_field_list.append(default_sub_field_property_dict)
if not sub_field_list or default_sub_field_property_dict['editable']:
sub_field_list.append(default_sub_field_property_dict)
sub_field_list[0]['title'] = title
return sub_field_list
......@@ -10,10 +10,10 @@
# - Check skin names.
# - Check script names (from skin folders and workflows).
import re
ABBREVIATION_WORD_SET = ((
"BBAN", "BIC", "BOM", "CAD", "CRM", "CSS", "CSV", "CTX", "DMS", "DNS",
"EAN", "ERP5", "FAX", "GAP", "GID", "GPG", "HTML", "HTTP", "IBAN", "ID",
"IMAP", "IP", "KM", "MIME", "MRP", "NVP", "ODT", "PDF", "PDM", "PO",
ABBREVIATION_WORD_SET = set((
"BBAN", "BIC", "BOM", "CA", "CAD", "CRM", "CSS", "CSV", "CTX", "DMS", "DNS",
"EAN", "ERP5", "FAX", "FTP", "GAP", "GID", "GPG", "HTML", "HTTP", "IBAN",
"ID", "IMAP", "IP", "KM", "MIME", "MRP", "NVP", "ODT", "PDF", "PDM", "PO",
"RAM", "RSS", "SMS", "SOAP", "SQL", "SVN", "TALES", "TCP", "TSV", "UBM",
"UID", "UOM", "URI", "URL", "VADS", "VAT", "VCS", "VPN", "XML", "ZODB",
))
......@@ -75,8 +75,14 @@ def checkField(folder, form, field):
template_field = getFieldFromProxyField(field)
if path.endswith("FieldLibrary"):
if not(template_field is field):
if not(1 in [field.id.startswith(x) for x in ('my_view_mode_',
'my_core_mode_', 'my_report_mode_', 'my_list_mode_', 'my_dialog_mode_')]):
field_id = field.id
try:
prefix, field_id = field_id.split('_', 1)
except ValueError:
prefix = ''
if prefix not in ('my', 'your') and not any([field_id.startswith(x) for x in (
'view_mode_', 'core_mode_', 'report_mode_', 'list_mode_', 'dialog_mode_',
)]):
error_message += "%s: %s : Bad ID for a Field Library Field" % (path, field.id)
if template_field is None:
if field.get_value('enabled'):
......
......@@ -301,9 +301,7 @@ class PasswordTool(BaseTool):
)
login_dict, = user_dict['login_list']
login = portal.unrestrictedTraverse(login_dict['path'])
login.checkPasswordValueAcceptable(password) # this will raise if password does not match policy
login._forceSetPassword(password)
login.reindexObject()
login.setPassword(password) # this will raise if password does not match policy
return redirect(REQUEST, site_url,
translateString("Password changed."))
......
......@@ -203,7 +203,14 @@ class PeriodicityMixin:
previous_date = next_start_date
next_start_date = max(self._getNextMinute(next_start_date, timezone),
current_date)
while 1:
# We'll try every date to check if they validate the periodicity
# constraints, but there might not be any valid date (for example,
# repeat every 2nd week in August can never be validated). Because
# gregorian calendar repeat every 28 years, if we did not get a match
# in the next 28 years we stop looping.
max_date = next_start_date + (28 * 366)
while next_start_date < max_date:
if not self._validateMonth(next_start_date):
next_start_date = self._getNextMonth(next_start_date, timezone)
elif not (self._validateDay(next_start_date) and
......
......@@ -40,7 +40,8 @@ from Products.ERP5Type.Utils import convertToUpperCase
from Products.ERP5Type.tests.ERP5TypeTestCase import (
ERP5TypeTestCase, _getConversionServerUrlList)
from Products.ERP5Type.tests.Sequence import SequenceList
from Products.ERP5Type.tests.utils import FileUpload, createZODBPythonScript
from Products.ERP5Type.tests.utils import FileUpload, removeZODBPythonScript, \
createZODBPythonScript
from Products.ERP5OOo.OOoUtils import OOoBuilder
from Products.CMFCore.utils import getToolByName
from zExceptions import BadRequest
......@@ -2025,6 +2026,53 @@ return result
self.assertTrue(document is not None)
self.assertEqual(document.getData(), data)
def test_publication_state_in_Base_viewNewFileDialog(self):
"""
Checks that with type based method returning 'published',
we can upload with Base_viewNewFileDialog and declare the document as 'published'
"""
person = self.portal.person_module.newContent(portal_type="Person")
method_id = "Person_getPreferredAttachedDocumentPublicationState"
skin_folder = self.portal.portal_skins.custom
if not getattr(skin_folder, method_id, False):
createZODBPythonScript(skin_folder, method_id, "", "return")
skin_folder[method_id].ZPythonScript_edit('', 'return ""')
self.tic()
item_list = person.Base_viewNewFileDialog.your_publication_state.get_value("items")
self.assertEqual(
item_list,
[('', ''), ('Draft', 'draft'), ('Shared', 'shared'), ('Released', 'released')])
skin_folder[method_id].ZPythonScript_edit('', 'return None')
self.tic()
item_list = person.Base_viewNewFileDialog.your_publication_state.get_value("items")
self.assertEqual(
item_list,
[('', ''), ('Draft', 'draft'), ('Shared', 'shared'), ('Released', 'released')])
skin_folder[method_id].ZPythonScript_edit('', 'return "published"')
self.tic()
item_list = person.Base_viewNewFileDialog.your_publication_state.get_value("items")
self.assertEqual(
item_list, [
('', ''), ('Draft', 'draft'), ('Shared', 'shared'),
('Released', 'released'), ('Published', 'published')
])
# clean up and check if we don't have the script and published state in the list
removeZODBPythonScript(skin_folder, method_id)
self.tic()
self.assertEqual(
person.getTypeBasedMethod('getPreferredAttachedDocumentPublicationSection').getId(),
"Base_getPreferredAttachedDocumentPublicationSection"
)
self.portal.changeSkin(None)
item_list = person.Base_viewNewFileDialog.your_publication_state.get_value("items")
self.assertEqual(
item_list,
[('', ''), ('Draft', 'draft'), ('Shared', 'shared'), ('Released', 'released')])
class Base_contributeMixin:
"""Tests for Base_contribute script.
......@@ -2149,6 +2197,24 @@ class Base_contributeMixin:
file=makeFileUpload('TEST-en-002.pdf', as_name='doc.pdf'))
self.tic()
self.assertEqual(contributed_document.getValidationState(), 'released')
contributed_document.setReference(None)
self.tic()
contributed_document = person.Base_contribute(
synchronous_metadata_discovery=False,
publication_state='published',
file=makeFileUpload('TEST-en-002.pdf', as_name='doc.pdf'))
self.tic()
self.assertEqual(contributed_document.getValidationState(), 'published')
contributed_document.setReference(None)
self.tic()
contributed_document = person.Base_contribute(
synchronous_metadata_discovery=True,
publication_state='published',
file=makeFileUpload('TEST-en-002.pdf', as_name='doc.pdf'))
self.tic()
self.assertEqual(contributed_document.getValidationState(), 'published')
def test_Base_contribute_publication_state_vs_finishIngestion_script(self):
"""Contribute dialog allow choosing a publication state, but there's
......
......@@ -127,10 +127,10 @@ class ERP5LoginUserManager(BasePlugin):
is_authentication_policy_enabled = self.getPortalObject().portal_preferences.isAuthenticationPolicyEnabled()
if check_password:
password = credentials.get('password')
if not password or not pw_validate(
login_value.getPassword(),
password,
):
login_password = login_value.getPassword()
if (not password
or login_password is None
or not pw_validate(login_password, password)):
if is_authentication_policy_enabled:
login_value.notifyLoginFailure()
return
......
......@@ -332,14 +332,59 @@ class TestUserManagement(UserManagementTestCase):
_, _, password = self._makePerson(login=login)
self._assertUserExists(login, password)
def test_PersonWithLoginWithEmptyPasswordAreNotUsers(self):
def test_PersonWithLoginWithNonePasswordAreNotUsers(self):
"""Tests a person with a login but None as a password is not a valid user."""
# check password set to None at creation
_, login, _ = self._makePerson(password=None)
self._assertUserDoesNotExists(login, None)
self._assertUserDoesNotExists(login, 'None')
self._assertUserDoesNotExists(login, '')
# check password set to None after being set
user_data, = self.portal.acl_users.searchUsers(login=login, exact_match=True)
erp5_login = self.portal.restrictedTraverse(user_data['login_list'][0]['path'])
erp5_login.setPassword('secret')
self.tic()
self._assertUserExists(login, 'secret')
erp5_login.setPassword(None)
self.tic()
self._assertUserDoesNotExists(login, 'secret')
self._assertUserDoesNotExists(login, None)
self._assertUserDoesNotExists(login, 'None')
self._assertUserDoesNotExists(login, '')
def test_PersonWithLoginWithEmptyStringPasswordAreNotUsers(self):
"""Tests a person with a login but no password is not a valid user."""
password = None
_, login, _ = self._makePerson(password=password)
self._assertUserDoesNotExists(login, password)
password = ''
_, login, self._makePerson(password=password)
self._assertUserDoesNotExists(login, password)
_, login, _ = self._makePerson(password='')
self._assertUserDoesNotExists(login, '')
self._assertUserDoesNotExists(login, 'None')
# check password set to '' after being set
user_data, = self.portal.acl_users.searchUsers(login=login, exact_match=True)
erp5_login = self.portal.restrictedTraverse(user_data['login_list'][0]['path'])
erp5_login.setPassword('secret')
self.tic()
self._assertUserExists(login, 'secret')
erp5_login.setPassword('')
self.tic()
self._assertUserDoesNotExists(login, 'secret')
self._assertUserDoesNotExists(login, None)
self._assertUserDoesNotExists(login, 'None')
self._assertUserDoesNotExists(login, '')
def test_PersonWithLoginWithoutPasswordAreNotUsers(self):
"""Tests a person with a login but no password set is not a valid user."""
# similar to _makePerson, but not passing password= to newContent
login = 'login_%s' % self._login_generator()
new_person = self.portal.person_module.newContent(portal_type='Person')
new_person.newContent(portal_type='Assignment').open()
new_person.newContent(
portal_type='ERP5 Login',
reference=login,
).validate()
self.tic()
self._assertUserDoesNotExists(login, '')
self._assertUserDoesNotExists(login, 'None')
def test_PersonWithEmptyLoginAreNotUsers(self):
"""Tests a person with empty login & password is not a valid user."""
......
......@@ -1653,6 +1653,29 @@ class Base(
"""
return self
security.declarePrivate('activeInteractionScript')
def activeInteractionScript(
self,
interaction_workflow_path,
script_name,
status,
tdef_id,
):
"""
Call interaction script on current document.
This is defined on the document involved in the interaction so that
CopySupport.unindexObject can find the activities involving this method
and flush them.
"""
self.getPortalObject().unrestrictedTraverse(
interaction_workflow_path,
).callInterationScript(
script_name=script_name,
ob=self,
status=status,
tdef_id=tdef_id,
)
security.declareProtected(Permissions.AccessContentsInformation,
'getDocumentInstance')
def getDocumentInstance(self):
......
......@@ -285,8 +285,12 @@ class InteractionWorkflow(Workflow):
# Execute "activity" scripts
for script in tdef.getActivateScriptValueList():
self.activate(activity='SQLQueue').activeScript(
script.getId(), ob.getRelativeUrl(), status, tdef.getId())
ob.activate(activity='SQLQueue').activeInteractionScript(
interaction_workflow_path=self.getPhysicalPath(),
script_name=script.getId(),
status=status,
tdef_id=tdef.getId(),
)
def _before_commit(self, sci, script_name, security_manager):
# check the object still exists before calling the script
......@@ -307,11 +311,24 @@ class InteractionWorkflow(Workflow):
security.declarePrivate('activeScript')
def activeScript(self, script_name, ob_url, status, tdef_id):
ob = self.unrestrictedTraverse(ob_url)
tdef = self.getTransitionValueByReference(tdef_id)
sci = StateChangeInfo(
ob, self, status, tdef, None, None, None)
self._getOb(script_name)(sci)
# BBB for when an upgrade to callInterationScript still has unexecuted
# activeScript activities leftover from the previous code.
self.callInterationScript(
script_name=script_name,
ob=self.unrestrictedTraverse(ob_url),
status=status,
tdef_id=tdef_id,
)
security.declarePrivate('callInterationScript')
def callInterationScript(self, script_name, ob, status, tdef_id):
self._getOb(script_name)(
StateChangeInfo(
ob, self, status,
self.getTransitionValueByReference(tdef_id),
None, None, None,
),
)
security.declarePrivate('isActionSupported')
def isActionSupported(self, ob, action, **kw):
......
......@@ -320,7 +320,13 @@ class FunctionalTestRunner:
password_field.clear()
password_field.send_keys(self.password)
login_form_url = browser.current_url
password_field.submit()
# Note: password_field.submit() (and in general, x.submit(), even if x is
# an <input type="submit"...>) does not work: it seems to submit only
# fields which have a value on their own. Which means type="submit" fields
# are absent, which breaks the form submission handling locic on Zope side.
password_field.find_element_by_xpath(
'ancestor::fieldset/descendant::input[@type="submit"]',
).click()
WebDriverWait(browser, 10).until(EC.url_changes(login_form_url))
WebDriverWait(browser, 10).until(EC.presence_of_element_located((By.TAG_NAME, 'body')))
......
......@@ -1050,8 +1050,15 @@ class ERP5TypeCommandLineTestCase(ERP5TypeTestCaseMixin):
update_business_templates = os.environ.get('update_business_templates') is not None
erp5_load_data_fs = int(os.environ.get('erp5_load_data_fs', 0))
if update_business_templates and erp5_load_data_fs:
template_list[:0] = (erp5_catalog_storage, 'erp5_property_sheets',
'erp5_core', 'erp5_xhtml_style')
app = self._app()
try:
template_list[:0] = app._getOb(
self.getPortalName(),
).getCoreBusinessTemplateList()
finally:
self.abort()
ZopeTestCase.close(app)
del app
# keep a mapping type info name -> property sheet list, to remove them in
# tear down.
......
......@@ -40,31 +40,27 @@ from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from Products.ERP5Type import Permissions
from Products.ERP5.InteractionWorkflow import InteractionWorkflowDefinition
from Products.DCWorkflow import Guard
def formatNameUnion(names):
names = list(names)
if len(names) == 2:
return ' or '.join(names)
elif len(names) > 2:
names[-1] = ' or ' + names[-1]
return '; '.join(names)
Guard.formatNameUnion = formatNameUnion
from Products.ERP5Type.Base import Base
from typing import Callable
class AssertPermissionMethod(object):
"""A method object to check that a user have a permission on a document.
"""
def __init__(self, permission_name):
# type: (str) -> None
self._permission_name = permission_name
def __get__(self, instance, cls=None):
self._instance = instance
return self
def __call__(self, username, document):
def __call__(self, user_id, document):
# type: (str, Base) -> None
sm = getSecurityManager()
try:
self._instance._loginAsUser(username)
self._instance._loginAsUser(user_id)
user = getSecurityManager().getUser()
if not user.has_permission(self._permission_name, document):
groups = []
......@@ -74,7 +70,7 @@ class AssertPermissionMethod(object):
'User %s does NOT have %s permission on %s %s (user roles: [%s], '
'roles needed: [%s], existing local roles:\n%s\n'
'your user groups: [%s])' %
(username, self._permission_name, document.getPortalTypeName(),
(user_id, self._permission_name, document.getPortalTypeName(),
document, ', '.join(user.getRolesInContext(document)),
', '.join([x['name'] for x in
document.rolesOfPermission(self._permission_name)
......@@ -90,21 +86,23 @@ class AssertNoPermissionMethod(object):
document.
"""
def __init__(self, permission_name):
# type: (str) -> None
self._permission_name = permission_name
def __get__(self, instance, cls=None):
self._instance = instance
return self
def __call__(self, username, document):
def __call__(self, user_id, document):
# type: (str, Base) -> None
sm = getSecurityManager()
try:
self._instance._loginAsUser(username)
self._instance._loginAsUser(user_id)
user = getSecurityManager().getUser()
if user.has_permission(self._permission_name, document):
self._instance.fail(
'User %s has %s permission on %s %s (roles: [%s])' %
(username, self._permission_name, document.getPortalTypeName(),
(user_id, self._permission_name, document.getPortalTypeName(),
document, ', '.join(user.getRolesInContext(document))))
finally:
setSecurityManager(sm)
......@@ -117,7 +115,6 @@ class SecurityTestCase(ERP5TypeTestCase):
"""set up and login as default user"""
super(SecurityTestCase, self)._setup()
self.login()
self.portal = self.getPortal()
self.workflow_tool = self.portal.portal_workflow
def tearDown(self):
......@@ -127,85 +124,89 @@ class SecurityTestCase(ERP5TypeTestCase):
self.portal.portal_caches.clearAllCache()
super(SecurityTestCase, self).tearDown()
def _loginAsUser(self, username):
"""Login as a given username. The user must exist.
In case Username is None, we consider test as Anonymous.
def _loginAsUser(self, user_id):
"""Login as a given user_id. The user must exist.
In case user_id is None, we consider test as Anonymous.
"""
if username is None:
if user_id is None:
newSecurityManager(None, SpecialUsers.nobody)
else:
uf = self.portal.acl_users
user = uf.getUserById(username)
self.assertNotEquals(user, None, 'No user %s' % username)
user = uf.getUserById(user_id)
self.assertNotEquals(user, None, 'No user %s' % user_id)
newSecurityManager(None, user.__of__(uf))
# Permission methods
failIfUserCanViewDocument = assertUserCanNotViewDocument = AssertNoPermissionMethod(
Permissions.View)
Permissions.View) # type: Callable[[SecurityTestCase, str, Base], None]
failIfUserCanAccessDocument = assertUserCanNotAccessDocument = AssertNoPermissionMethod(
Permissions.AccessContentsInformation)
Permissions.AccessContentsInformation) # type: Callable[[SecurityTestCase, str, Base], None]
failIfUserCanModifyDocument = assertUserCanNotModifyDocument = AssertNoPermissionMethod(
Permissions.ModifyPortalContent)
Permissions.ModifyPortalContent) # type: Callable[[SecurityTestCase, str, Base], None]
failIfUserCanAddDocument = assertUserCanNotAddDocument = AssertNoPermissionMethod(
Permissions.AddPortalContent)
Permissions.AddPortalContent) # type: Callable[[SecurityTestCase, str, Base], None]
failIfUserCanChangeLocalRoles = assertUserCanNotChangeLocalRoles = AssertNoPermissionMethod(
Permissions.ChangeLocalRoles)
Permissions.ChangeLocalRoles) # type: Callable[[SecurityTestCase, str, Base], None]
failIfUserCanDeleteDocument = assertUserCanNotDeleteDocument = AssertNoPermissionMethod(
Permissions.DeleteObjects)
Permissions.DeleteObjects) # type: Callable[[SecurityTestCase, str, Base], None]
def failIfUserHavePermissionOnDocument(self, permission_name, username, document):
def failIfUserHavePermissionOnDocument(self, permission_name, user_id, document):
# type: (str, str, Base) -> None
"""Fail If the user have a permission on document.
XXX why isn't it a method object ?
"""
method = AssertNoPermissionMethod(permission_name)
method._instance = self
return method(username, document)
return method(user_id, document)
failUnlessUserCanViewDocument = assertUserCanViewDocument =\
AssertPermissionMethod(Permissions.View)
AssertPermissionMethod(Permissions.View) # type: Callable[[SecurityTestCase, str, Base], None]
failUnlessUserCanAccessDocument = assertUserCanAccessDocument =\
AssertPermissionMethod(Permissions.AccessContentsInformation)
AssertPermissionMethod(Permissions.AccessContentsInformation) # type: Callable[[SecurityTestCase, str, Base], None]
failUnlessUserCanModifyDocument = assertUserCanModifyDocument = \
AssertPermissionMethod(Permissions.ModifyPortalContent)
AssertPermissionMethod(Permissions.ModifyPortalContent) # type: Callable[[SecurityTestCase, str, Base], None]
failUnlessUserCanAddDocument = assertUserCanAddDocument =\
AssertPermissionMethod(Permissions.AddPortalContent)
AssertPermissionMethod(Permissions.AddPortalContent) # type: Callable[[SecurityTestCase, str, Base], None]
failUnlessUserCanChangeLocalRoles = assertUserCanChangeLocalRoles =\
AssertPermissionMethod(Permissions.ChangeLocalRoles)
AssertPermissionMethod(Permissions.ChangeLocalRoles) # type: Callable[[SecurityTestCase, str, Base], None]
failUnlessUserCanDeleteDocument = assertUserCanDeleteDocument =\
AssertPermissionMethod(Permissions.DeleteObjects)
AssertPermissionMethod(Permissions.DeleteObjects) # type: Callable[[SecurityTestCase, str, Base], None]
def failUnlessUserHavePermissionOnDocument(self, permission_name, username, document):
def failUnlessUserHavePermissionOnDocument(self, permission_name, user_id, document):
# type: (str, str, Base) -> None
"""Fail Unless the user have a permission on document."""
method = AssertPermissionMethod(permission_name)
method._instance = self
return method(username, document)
return method(user_id, document)
assertUserHavePermissionOnDocument = failUnlessUserHavePermissionOnDocument
# Workflow Transition Methods
def failIfUserCanPassWorkflowTransition(self, username, transition, document):
def failIfUserCanPassWorkflowTransition(self, user_id, transition, document):
# type: (str, str, Base) -> None
"""Fails if the user can pass the workflow transition on the document."""
sm = getSecurityManager()
try:
self._loginAsUser(username)
self._loginAsUser(user_id)
user = getSecurityManager().getUser()
valid_transition_list =[ai['id'] for ai in
self.workflow_tool.listActions(object=document) if
ai['category'] == 'workflow']
if transition in valid_transition_list:
self.fail('User %s can pass %s transition on %s %s. Roles: [%s]' % (
username, transition, document.getPortalTypeName(), document,
user_id, transition, document.getPortalTypeName(), document,
", ".join(user.getRolesInContext(document))))
finally:
setSecurityManager(sm)
assertUserCanNotPassWorkflowTransition = failIfUserCanPassWorkflowTransition
assertUserCanNotPassWorkflowTransition = failIfUserCanPassWorkflowTransition # type: Callable[[SecurityTestCase, str, str, Base], None]
def failUnlessUserCanPassWorkflowTransition(self, username,
def failUnlessUserCanPassWorkflowTransition(self, user_id,
transition, document):
# type: (str, str, Base) -> None
"""Fails unless the user can pass the workflow transition on the document."""
sm = getSecurityManager()
try:
self._loginAsUser(username)
self._loginAsUser(user_id)
user = getSecurityManager().getUser()
valid_transition_list =[ai['id'] for ai in
self.workflow_tool.listActions(object=document) if
......@@ -217,17 +218,23 @@ class SecurityTestCase(ERP5TypeTestCase):
for wf in self.workflow_tool.getWorkflowValueListFor(document) or []:
if wf.getId() == 'edit_workflow':
continue
if wf.__class__.__name__ in ['InteractionWorkflowDefinition', 'Interaction Workflow'] :
if wf.__class__.__name__ in (
'InteractionWorkflowDefinition',
'Interaction Workflow',
):
continue
for wf_transition_id in wf._getWorkflowStateOf(
document).getTransitions():
wf_transition = wf.getTransitionValueByReference(wf_transition_id)
if wf_transition.trigger_type == TRIGGER_USER_ACTION:
for wf_transition in wf._getWorkflowStateOf(document).getDestinationValueList():
if wf_transition.getTriggerType() == TRIGGER_USER_ACTION:
workflow_transitions_description.append(
"%s%s[%s]: %s" % (
wf_transition_id == transition and "* " or " ",
wf_transition_id, wf.getId(),
wf_transition.getGuardSummary()))
"%s%s[%s]\n\t\tExpression: %s\n\t\tPermissions: %s\n\t\tGroups: %s" % (
wf_transition.getReference() == transition and "* " or " ",
wf_transition.getReference(),
wf.getId(),
wf_transition.getGuardExpression() or '',
', '.join(wf_transition.getGuardPermissionList()),
', '.join(wf_transition.getGuardGroupList()),
)
)
workflow_states_description.append("%s on %s" % (
wf._getWorkflowStateOf(document, id_only=1), wf.getId()))
......@@ -237,21 +244,22 @@ class SecurityTestCase(ERP5TypeTestCase):
", ".join(workflow_states_description))
self.fail('User %s can NOT pass %s transition on %s.\n '
'Roles: [%s]\n Available transitions:\n\t%s' % ( username,
'Roles: [%s]\n Available transitions:\n\t%s' % ( user_id,
transition, document_description,
", ".join(user.getRolesInContext(document)),
"\n\t".join(workflow_transitions_description)))
finally:
setSecurityManager(sm)
assertUserCanPassWorkflowTransition = failUnlessUserCanPassWorkflowTransition
assertUserCanPassWorkflowTransition = failUnlessUserCanPassWorkflowTransition # type: Callable[[SecurityTestCase, str, str, Base], None]
def assertUserHasWorklist(self, username, worklist_id, document_count):
def assertUserHasWorklist(self, user_id, worklist_id, document_count):
# type: (str, str, int) -> None
self.portal.portal_workflow.refreshWorklistCache()
self.portal.portal_caches.clearAllCache()
sm = getSecurityManager()
try:
self._loginAsUser(username)
self._loginAsUser(user_id)
global_action_list = [x for x in
self.portal.portal_workflow.listActions(object=self.portal)
if x['category'] == 'global']
......@@ -259,56 +267,58 @@ class SecurityTestCase(ERP5TypeTestCase):
if x['worklist_id'] == worklist_id]
if not(worklist_action_list):
self.fail("User %s does not have worklist %s.\nWorklists: %s" % (
username, worklist_id, pformat(global_action_list)))
user_id, worklist_id, pformat(global_action_list)))
worklist_action, = worklist_action_list
self.assertEquals(document_count, worklist_action['count'],
"User %s has %s documents in her %s worklist, not %s" % (
username, worklist_action['count'], worklist_id, document_count))
user_id, worklist_action['count'], worklist_id, document_count))
finally:
setSecurityManager(sm)
def assertUserHasNoWorklist(self, username, worklist_id):
def assertUserHasNoWorklist(self, user_id, worklist_id):
# type: (str, str) -> None
self.portal.portal_workflow.refreshWorklistCache()
self.portal.portal_caches.clearAllCache()
sm = getSecurityManager()
try:
self._loginAsUser(username)
self._loginAsUser(user_id)
worklist_action_list = [x for x in
self.portal.portal_workflow.listActions(object=self.portal)
if x['category'] == 'global' and x['worklist_id'] == worklist_id]
if worklist_action_list:
self.fail("User %s has worklist %s: %s" % (username, worklist_id, pformat(worklist_action_list)))
self.fail("User %s has worklist %s: %s" % (user_id, worklist_id, pformat(worklist_action_list)))
finally:
setSecurityManager(sm)
# Simple check for an user Role
def failIfUserHaveRoleOnDocument(self, username, role, document):
def failIfUserHaveRoleOnDocument(self, user_id, role, document):
# type: (str, str, Base) -> None
"""Fails if the user have the role on the document."""
sm = getSecurityManager()
try:
self._loginAsUser(username)
self._loginAsUser(user_id)
user = getSecurityManager().getUser()
if role in user.getRolesInContext(document):
self.fail('User %s have %s role on %s at %s' % (
username, role, document.getPortalType(), document.getRelativeUrl()))
user_id, role, document.getPortalType(), document.getRelativeUrl()))
finally:
setSecurityManager(sm)
assertUserDoesNotHaveRoleOnDocument = failIfUserHaveRoleOnDocument
assertUserDoesNotHaveRoleOnDocument = failIfUserHaveRoleOnDocument # type: Callable[[SecurityTestCase, str, str, Base], None]
def failUnlessUserHaveRoleOnDocument(self, username, role, document):
def failUnlessUserHaveRoleOnDocument(self, user_id, role, document):
# type: (str, str, Base) -> None
"""Fails if the user does not have the role on the document."""
sm = getSecurityManager()
try:
self._loginAsUser(username)
self._loginAsUser(user_id)
user = getSecurityManager().getUser()
if role not in user.getRolesInContext(document):
self.fail('User %s does not have %s role on %s at %s '
'(user roles: %s)' % ( username, role,
'(user roles: %s)' % ( user_id, role,
document.getPortalType(), document.getRelativeUrl(),
user.getRolesInContext(document)))
finally:
setSecurityManager(sm)
assertUserHaveRoleOnDocument = failUnlessUserHaveRoleOnDocument
assertUserHaveRoleOnDocument = failUnlessUserHaveRoleOnDocument # type: Callable[[SecurityTestCase, str, str, Base], None]
......@@ -71,6 +71,7 @@ class TestNamingConvention(ERP5TypeTestCase):
'test_xhtml_style', 'cloudooo_data', 'cloudooo_web', 'erp5_configurator',
'erp5_configurator_maxma_demo', 'erp5_configurator_run_my_doc', 'erp5_configurator_standard',
'erp5_configurator_standard_solver',
'erp5_web_service',
# skip l10n templates to save time.
# 'erp5_l10n_fr', 'erp5_l10n_ja',
# 'erp5_l10n_pl_PL', 'erp5_l10n_pt-BR',
......
......@@ -161,7 +161,7 @@ class ERP5(_ERP5):
if search:
group_dict = search.groupdict()
status_dict['failure_count'] = int(group_dict['failures']) \
+ int(status_dict.get('failure_count', 0))
or int(status_dict.get('failure_count', 0))
status_dict['test_count'] = int(group_dict['total'])
status_dict['skip_count'] = int(group_dict['expected_failure'])
return status_dict
......
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