...
 
Commits (29)
Showing 40 changed files with 437 additions and 162 deletions
......@@ -139,18 +139,29 @@ def byteify(string):
return string
def ensureUTF8(obj):
"""Make sure string is UTF-8, by replacing characters that
cannot be decoded.
"""
if isinstance(obj, str):
return obj.decode('utf-8', 'replace').encode('utf-8')
elif isinstance(obj, unicode):
return obj.encode('utf-8', 'replace')
return obj
def ensureSerializable(obj):
"""Ensure obj and all sub-objects are JSON serializable."""
if isinstance(obj, dict):
for key in obj:
obj[key] = ensureSerializable(obj[key])
# throw away date's type information and later reconstruct as Zope's DateTime
if isinstance(obj, DateTime):
elif isinstance(obj, DateTime):
return obj.ISO() + ' ' + obj.timezone() # ISO with timezone
if isinstance(obj, (datetime.datetime, datetime.date, datetime.time)):
elif isinstance(obj, (datetime.datetime, datetime.date, datetime.time)):
return obj.isoformat()
# let us believe that iterables don't contain other unserializable objects
return obj
return ensureUTF8(obj)
datetime_iso_re = re.compile(r'^\d{4}-\d{2}-\d{2} |T\d{2}:\d{2}:\d{2}.*$')
......@@ -428,7 +439,7 @@ def renderField(traversed_document, field, form, value=MARKER, meta_type=None, k
if "Field" in meta_type:
# fields have default value and can be required (unlike boxes)
result["required"] = field.get_value("required") if field.has_value("required") else None
result["default"] = getFieldDefault(form, field, key, value=value)
result["default"] = ensureUTF8(getFieldDefault(form, field, key, value=value))
# start the actual "switch" on field's meta_type here
if meta_type in ("ListField", "RadioField", "ParallelListField", "MultiListField"):
......@@ -560,6 +571,7 @@ def renderField(traversed_document, field, form, value=MARKER, meta_type=None, k
if not isinstance(result["default"], list):
result["default"] = [result["default"], ]
result["default"] = [ensureUTF8(x) for x in result["default"]]
result.update({
"relation_field_id": traversed_document.Field_getSubFieldKeyDict(field, "relation", key=result["key"]),
......@@ -892,7 +904,7 @@ def renderForm(traversed_document, form, response_dict, key_prefix=None, selecti
"script_id": script.id
},
"name": getRealRelativeUrl(traversed_document),
"title": traversed_document.getTitle()
"title": ensureUTF8(traversed_document.getTitle())
}
form_relative_url = getFormRelativeUrl(form)
......@@ -1265,7 +1277,7 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
action_dict = {} # actions available on current `traversed_document`
last_form_id = None # will point to the previous form so we can obtain previous selection
result_dict['title'] = traversed_document.getTitle()
result_dict['title'] = ensureUTF8(traversed_document.getTitle())
# extra_param_json should be base64 encoded JSON at this point
# only for mode == 'form' it is already a dictionary
......
......@@ -1270,6 +1270,25 @@ class TestERP5Document_getHateoas_mode_traverse(ERP5HALJSONStyleSkinsMixin):
self.assertTrue('_actions' not in result_dict['_embedded']['_view'])
@simulate('Base_getRequestUrl', '*args, **kwargs',
'return "http://example.org/bar"')
@simulate('Base_getRequestHeader', '*args, **kwargs',
'return "application/hal+json"')
@changeSkin('Hal')
def test_getHateoasDocument_property_corrupted_encoding(self):
document = self._makeDocument()
# this sequence of bytes does not encode to UTF-8
document.setTitle('\xe9\xcf\xf3\xaf')
fake_request = do_fake_request("GET")
result = self.portal.web_site_module.hateoas.ERP5Document_getHateoas(REQUEST=fake_request, mode="traverse", relative_url=document.getRelativeUrl(), view="view")
self.assertEquals(fake_request.RESPONSE.status, 200)
self.assertEquals(fake_request.RESPONSE.getHeader('Content-Type'),
"application/hal+json"
)
result_dict = json.loads(result)
self.assertEqual(result_dict['_embedded']['_view']['my_title']['default'], u'\ufffd\ufffd\ufffd')
self.assertEqual(result_dict['title'], u'\ufffd\ufffd\ufffd')
self.assertEqual(result_dict['_embedded']['_view']['_links']['traversed_document']['title'], u'\ufffd\ufffd\ufffd')
class TestERP5Document_getHateoas_mode_search(ERP5HALJSONStyleSkinsMixin):
......
......@@ -13,15 +13,19 @@ error_message = "No error."
context.ERP5Site_setUpActivityTool()
user_quantity = request.get('user_quantity')
if user_quantity is None: return json.dumps({"status_code" : 1, "error_message": "Parameter 'user_quantity' is required.", "password" : None })
if user_quantity is None:
return json.dumps({"status_code" : 1,
"error_message": "Parameter 'user_quantity' is required.",
"password" : None })
password = ''.join(random.choice(string.digits + string.letters) for i in xrange(10))
# check erp5_scalability_test business template is present
configurator = portal.business_configuration_module.default_standard_configuration
if configurator == None or not configurator.contentValues(portal_type='Configuration Save'):
error_message = "Could not find the scalability business configuration object. Be sure to have erp5_scalability_test business template installed."
return json.dumps({"status_code" : 1, "error_message": error_message })
return json.dumps({"status_code" : 1,
"error_message": error_message })
# install configurator if not intalled
if configurator.getSimulationState() == "draft":
......@@ -33,7 +37,8 @@ if configurator.getSimulationState() == "draft":
except Exception as e:
status_code = 1
error_message = "Error during installation: " + str(e)
return json.dumps({"status_code" : 1, "error_message": error_message })
return json.dumps({"status_code" : 1,
"error_message": error_message })
# create users if installation is done
try:
......@@ -46,5 +51,10 @@ try:
except Exception as e:
status_code = 1
error_message = "Error calling ERP5Site_createTestData script: " + str(e)
return json.dumps({"status_code" : 1, "error_message": error_message })
return json.dumps({"status_code" : status_code, "error_message": error_message, "password" : password, "quantity" : user_quantity })
return json.dumps({"status_code" : 1,
"error_message": error_message })
return json.dumps({"status_code" : status_code,
"error_message": error_message,
"password" : password,
"quantity" : user_quantity })
......@@ -50,7 +50,7 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string></string> </value>
<value> <string>user_quantity=None</string> </value>
</item>
<item>
<key> <string>guard</string> </key>
......
......@@ -7,21 +7,32 @@ test_result_lines = context.objectValues(portal_type="Test Result Line", sort_on
# Create a dict containing stats for each test
tests = []
count = 0
results = {}
for tl in test_result_lines:
# Get and parse stdout to a dict
# Get and parse stdout to a dict. this format ['Person: 372 doc/hour; SaleOrder: 132 doc/hour;']
stdout = tl.getProperty('stdout')
if stdout:
count = count + 1
stdout_lines = filter(None, stdout.split('\n'))
current_stats = dict( [(l.split("=")[0].replace(" ", "_"), \
l.split("=")[1].isdigit() and int(l.split("=")[1]) or str(l.split("=")[1])) \
for l in stdout_lines ])
tests.append(current_stats)
for stdout_line in stdout_lines:
tests_list = stdout_line.split(';')
tests_list = [x for x in tests_list if x.strip()!='']
for test in tests_list:
test = test.strip()
test_name = test.split(':')[0]
test_documents_created = test.split(':')[1].replace('doc/hour', '').strip()
# initial init
if test_name not in results.keys():
results[test_name] = []
results[test_name].append({'created_docs': test_documents_created,
'duration':3600})
test_suite = context.getPortalObject().test_suite_module.searchFolder(title=context.getTitle())[0]
xs = map(int, test_suite.getGraphCoordinate())
# testnode usually runs multiple tests, for example for Person and Sale Order creation but
# viewer shows only one graph thus return only one test
tests = results[test_suite_name]
return json.dumps({"test": tests, "xs": xs})
......@@ -50,7 +50,7 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>**kw</string> </value>
<value> <string>test_suite_name, **kw</string> </value>
</item>
<item>
<key> <string>id</string> </key>
......
"""
Return list of Test Suites being tested by introspecting Test Result Lines
"""
test_suite_list = []
test_result_line_list = context.objectValues(portal_type = 'Test Result Line')
if len(test_result_line_list) > 0:
test_result_line = test_result_line_list[0]
stdout = test_result_line.getProperty('stdout')
context.log(stdout)
for i in stdout.split(';'):
test_suite_list.append(i.split(':')[0].strip())
# remote empty elements
test_suite_list = [x for x in test_suite_list if x.strip()!='']
return test_suite_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>TestResult_getTestSuiteList</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -134,7 +134,9 @@
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
......@@ -293,4 +295,17 @@
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>here/render_scalability_graph_links</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>render_scalability_graph_links</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>
<tal:block tal:repeat="test_suite here/TestResult_getTestSuiteList">
<a tal:attributes="href python: 'test_result_graph.html?test_suite_name=' + test_suite">Show the graph for <span tal:content="test_suite"/></a><br/>
</tal:block>
\ No newline at end of file
......@@ -56,7 +56,7 @@
</item>
<item>
<key> <string>title</string> </key>
<value> <unicode>test_result_graphhttps://192.168.242.72:1234/erp5/portal_skins/erp5_test_results/externalEdit_/test_result_graph.html.zem</unicode> </value>
<value> <unicode></unicode> </value>
</item>
</dictionary>
</pickle>
......
......@@ -21,7 +21,7 @@ body {
.bullet .title { font-size: 14px; font-weight: bold; }
</style>
<script src="http://d3js.org/d3.v3.js"></script>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js" ></script>
......@@ -33,7 +33,8 @@ body {
//get data from erp5
var current_url = $(document)[0].baseURI;
var test_result_url = current_url.substr(0, current_url.lastIndexOf('/'));
var url = test_result_url+"/TestResult_getJsonScalabilityStats";
var test_suite_name = location.search.split('test_suite_name=')[1] ? location.search.split('test_suite_name=')[1] : 'Person';
var url = test_result_url+"/TestResult_getJsonScalabilityStats?test_suite_name=" + test_suite_name;
json = $.ajax({
async: false,
url: url,
......@@ -58,9 +59,11 @@ x-scale may not be respected."+bracket_left+"h1"+bracket_right_end;
}
}
if (xs[0]>1){
document.write("*Speedup and efficiency determined using case n="+xs[0]+" as perfect.");
}
document.write("<h2>Test suite name: " + test_suite_name + "</h2>");
x_max = xs[stats.test.length-1];
// just for fun..
......
......@@ -15285,8 +15285,7 @@ return new Parser;
var index = parseInt(
cursor.primaryKey.slice(key_path.length + 1),
10
),
i;
);
if ((start !== 0) && (index < start_index)) {
// No need to fetch blobs at the start
......@@ -15297,12 +15296,6 @@ return new Parser;
return;
}
i = index - start_index;
// Extend array size
while (i > promise_list.length) {
promise_list.push(null);
i -= 1;
}
// Sort the blob by their index
promise_list.splice(
index - start_index,
......@@ -15336,7 +15329,7 @@ return new Parser;
{type: "application/octet-stream"});
index = Math.floor(start / UNITE) * UNITE;
if (end === undefined) {
end = blob.length;
end = blob.size;
} else {
end = end - index;
}
......@@ -15359,13 +15352,8 @@ return new Parser;
var index = parseInt(
cursor.primaryKey.slice(key_path.length + 1),
10
),
i = index;
// Extend array size
while (i > array_buffer_list.length) {
array_buffer_list.push(null);
i -= 1;
}
);
// Sort the blob by their index
array_buffer_list.splice(
index,
......@@ -15407,7 +15395,7 @@ return new Parser;
blob = new Blob(array_buffer_list,
{type: attachment.info.content_type});
if (blob.length !== attachment.info.total_length) {
if (blob.size !== attachment.info.length) {
throw new jIO.util.jIOError(
"IndexedDB: attachment '" +
buildKeyPath([id, name]) +
......
......@@ -234,7 +234,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>977.17610.605.30924</string> </value>
<value> <string>978.21180.38418.37324</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1563892822.4</float>
<float>1568036980.42</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -270,9 +270,6 @@ ces or already launched.")
logger.info("Waiting for new configuration")
time.sleep(60*5)
self.slapos_communicator.requestInstanceStop(instance_title, request_kw)
self.slapos_communicator.waitInstanceStopped(instance_title)
self.slapos_communicator.requestInstanceStart(instance_title, request_kw)
return {'status_code' : 0}
......@@ -466,10 +463,16 @@ Require valid-user
logger.info("Getting instance information:")
instance_information_time = time.time()
instance_information = self.slapos_communicator.getInstanceUrlDict()
while not instance_information['frontend-url-list'] and \
time.time() - instance_information_time < MAX_FRONTEND_TIME:
time.sleep(5*60)
while (time.time() - instance_information_time < MAX_FRONTEND_TIME):
logger.info("getInstanceInformation=%s" %instance_information)
# loop until frontend is instanciated
if instance_information['frontend-url-list'] and \
instance_information['user'] and \
instance_information['password']:
break
time.sleep(60)
instance_information = self.slapos_communicator.getInstanceUrlDict()
logger.info(instance_information)
if not instance_information['frontend-url-list']:
raise ValueError("Error getting instance information: frontend url not available")
......
......@@ -187,7 +187,6 @@ class SlapOSMasterCommunicator(object):
state = INSTANCE_STATE_UNKNOWN
monitor_information_dict = {}
info_created_at = "-1"
is_slave = instance['portal_type'] == "Slave Instance"
if is_slave:
if len(instance['getConnectionXmlAsDict']) > 0:
......@@ -196,24 +195,15 @@ class SlapOSMasterCommunicator(object):
# not slave
instance_state = news
if instance_state.get('created_at', '-1') != "-1":
# the following does NOT take TZ into account
created_at = datetime.datetime.strptime(instance_state['created_at'],
'%a, %d %b %Y %H:%M:%S %Z')
gmt_now = datetime.datetime(*time.gmtime()[:6])
instance_text = instance_state['text']
info_created_at = '%s (%d)' % (
instance_state['created_at'], (gmt_now - created_at).seconds)
if instance_state['text'].startswith('#access'):
if instance_text.startswith('#access Instance correctly started'):
state = INSTANCE_STATE_STARTED
if instance_state['text'].startswith('#access Instance correctly stopped'):
elif instance_text.startswith('#access Instance correctly stopped'):
state = INSTANCE_STATE_STOPPED
if instance_state['text'].startswith('#destroy'):
elif instance_text.startswith('#destroy'):
state = INSTANCE_STATE_DESTROYED
if instance_state['text'].startswith('#error'):
elif instance_text.startswith('#error'):
state = INSTANCE_STATE_STARTED_WITH_ERROR
if state == INSTANCE_STATE_STARTED_WITH_ERROR:
......@@ -248,20 +238,18 @@ class SlapOSMasterCommunicator(object):
stopped = 0
self.message_history.append(message_list)
for instance in message_list:
if not instance['slave'] and \
instance['state'] in (INSTANCE_STATE_UNKNOWN, INSTANCE_STATE_STARTED_WITH_ERROR):
return instance['state']
elif not instance['slave'] and instance['state'] == INSTANCE_STATE_STARTED:
started = 1
elif not instance['slave'] and instance['state'] == INSTANCE_STATE_STOPPED:
stopped = 1
if not instance['slave']:
if instance['state'] in (INSTANCE_STATE_UNKNOWN, INSTANCE_STATE_STARTED_WITH_ERROR):
return instance['state']
elif instance['state'] == INSTANCE_STATE_STARTED:
started = 1
elif instance['state'] == INSTANCE_STATE_STOPPED:
stopped = 1
if instance['slave'] and instance['state'] == INSTANCE_STATE_UNKNOWN:
return instance['state']
if started and stopped:
return INSTANCE_STATE_STOPPED
return INSTANCE_STATE_UNKNOWN
if started:
return INSTANCE_STATE_STARTED
......@@ -382,6 +370,8 @@ class SlapOSTester(SlapOSMasterCommunicator):
# In the future, this should allow customization so each project to be tested parses its own information,
# probably in the test suite definition class
def getInstanceUrlDict(self):
user = None
password = None
frontend_url_list = []
for instance in self.getInstanceUrlList():
if "frontend-" in instance["title"]:
......
......@@ -2049,7 +2049,7 @@ class RegisteredSkinSelectionTemplateItem(BaseTemplateItem):
update_dict = kw.get('object_to_update')
force = kw.get('force')
portal = context.getPortalObject()
skin_tool = getToolByName(portal, 'portal_skins')
skin_tool = portal.portal_skins
for skin_folder_id in self._objects.keys():
......@@ -2091,7 +2091,7 @@ class RegisteredSkinSelectionTemplateItem(BaseTemplateItem):
def uninstall(self, context, **kw):
portal = context.getPortalObject()
skin_tool = getToolByName(portal, 'portal_skins')
skin_tool = portal.portal_skins
object_path = kw.get('object_path')
for skin_folder_id in (object_path,) if object_path else self._objects:
skin_selection_list = self._objects[skin_folder_id]
......@@ -6412,7 +6412,8 @@ Business Template is a set of definitions, such as skins, portal types and categ
uid=uid,
portal_type=portal_type,
reference=reference,
source_reference=source_reference)
source_reference=source_reference,
migrate=migrate)
migratable_component_list.append(obj)
......@@ -6597,7 +6598,9 @@ Business Template is a set of definitions, such as skins, portal types and categ
if list_selection_name is not None:
message = (
"All components were successfully imported from filesystem to ZODB. "
"You can now delete them from your instance home and Products.")
"Please note that imported {Document,Interfaces,Mixin,Tool Components} "
"have not been validated automatically as imports must probably be "
"adjusted before deleting them from the filesystem.")
if still_used_list_dict:
message = (
......
......@@ -322,7 +322,7 @@ class ERP5Site(FolderMixIn, CMFSite, CacheCookieMixin):
from Products.Localizer.MessageCatalog import (
message_catalog_alias_sources
)
sm = self.getSiteManager()
sm = self._components
for message_catalog in self.Localizer.objectValues():
sm.registerUtility(message_catalog,
provided=ITranslationDomain,
......@@ -332,21 +332,20 @@ class ERP5Site(FolderMixIn, CMFSite, CacheCookieMixin):
provided=ITranslationDomain,
name=alias)
def _doInitialSiteManagerMigration(self):
self._createInitialSiteManager()
# Now that we have a sitemanager, se can do things that require
# one. Including setting up ZTK style utilities and adapters. We
# can even call setSite(self), as long as we roll back that later,
# since we are actually in the middle of a setSite() call.
from zope.site.hooks import getSite, setSite
old_site = getSite()
try:
setSite(self)
# setSite(self) is not really necessary for the migration below, but
# could be needed by other migrations to be added here.
self._doTranslationDomainRegistration()
finally:
setSite(old_site)
def _registerMissingTools(self):
from Products.CMFCore import interfaces, utils
sm = self._components
# We don't want to register everything.
for tool_id in ("portal_skins", "portal_types", "portal_membership",
"portal_url", "portal_workflow", "portal_actions"):
# XXX: need to find the proper API to access "self.tool_id"
tool = getattr(self, tool_id, None)
if tool is not None:
# Note: already registered tools will be either:
# - updated
# - registered again after being unregistered
sm.registerUtility(aq_base(tool),
utils._tool_interface_registry[tool_id])
# backward compatibility auto-migration
def getSiteManager(self):
......@@ -364,13 +363,25 @@ class ERP5Site(FolderMixIn, CMFSite, CacheCookieMixin):
# as cheap as it is on the case that self._components is already
# set.
_components = self._components
if _components is not None:
return _components
# This method below can take as (reasonably) long as it pleases
# since it will not be run ever again
self._doInitialSiteManagerMigration()
assert self._components is not None, 'Migration Failed!'
return self._components
if _components is None:
# only create _components
self._createInitialSiteManager()
_components = self._components
# Now that we have a sitemanager, se can do things that require
# one. Including setting up ZTK style utilities and adapters. We
# can even call setSite(self), as long as we roll back that later,
# since we are actually in the middle of a setSite() call.
from zope.site.hooks import getSite, setSite
old_site = getSite()
try:
setSite(self)
self._doTranslationDomainRegistration()
self._registerMissingTools()
finally:
setSite(old_site)
else:
self._registerMissingTools()
return _components
security.declareProtected(Permissions.View, 'view')
def view(self):
......
......@@ -15285,8 +15285,7 @@ return new Parser;
var index = parseInt(
cursor.primaryKey.slice(key_path.length + 1),
10
),
i;
);
if ((start !== 0) && (index < start_index)) {
// No need to fetch blobs at the start
......@@ -15297,12 +15296,6 @@ return new Parser;
return;
}
i = index - start_index;
// Extend array size
while (i > promise_list.length) {
promise_list.push(null);
i -= 1;
}
// Sort the blob by their index
promise_list.splice(
index - start_index,
......@@ -15336,7 +15329,7 @@ return new Parser;
{type: "application/octet-stream"});
index = Math.floor(start / UNITE) * UNITE;
if (end === undefined) {
end = blob.length;
end = blob.size;
} else {
end = end - index;
}
......@@ -15359,13 +15352,8 @@ return new Parser;
var index = parseInt(
cursor.primaryKey.slice(key_path.length + 1),
10
),
i = index;
// Extend array size
while (i > array_buffer_list.length) {
array_buffer_list.push(null);
i -= 1;
}
);
// Sort the blob by their index
array_buffer_list.splice(
index,
......@@ -15407,7 +15395,7 @@ return new Parser;
blob = new Blob(array_buffer_list,
{type: attachment.info.content_type});
if (blob.length !== attachment.info.total_length) {
if (blob.size !== attachment.info.length) {
throw new jIO.util.jIOError(
"IndexedDB: attachment '" +
buildKeyPath([id, name]) +
......
......@@ -94,7 +94,7 @@
</item>
<item>
<key> <string>temporary_document_disallowed</string> </key>
<value> <int>0</int> </value>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>title</string> </key>
......
......@@ -91,7 +91,7 @@
</item>
<item>
<key> <string>temporary_document_disallowed</string> </key>
<value> <int>0</int> </value>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>title</string> </key>
......
"Modified version for ERP5 to append the default action (/view) in the URL."
from Products.CMFCore.utils import getToolByName
ptool = getToolByName(script, 'portal_properties')
utool = getToolByName(script, 'portal_url')
portal = context.getPortalObject()
utool = portal.portal_url
portal_url = utool()
result = []
param = int(context.REQUEST.get('ignore_layout', 0)) and '?ignore_layout:int=1' or ''
param = '?ignore_layout:int=1' if int(portal.REQUEST.get('ignore_layout', 0)) else ''
if include_root:
result.append( { 'id' : 'root'
, 'title' : ptool.title()
, 'url' : '%s/view%s' % (portal_url, param)
}
)
relative = utool.getRelativeContentPath(context)
portal = utool.getPortalObject()
result = [{
'id' : 'root',
'title' : portal.portal_properties.title(),
'url' : '%s/view%s' % (portal_url, param),
}]
else:
result = []
obj = portal
now = []
for name in relative:
for name in utool.getRelativeContentPath(context):
obj = obj.restrictedTraverse(name)
now.append(name)
title = (
getattr(obj, "getCompactTranslatedTitle", lambda: None)() or
obj.getTitle() or obj.getId()
)
if not name == 'talkback':
if name != 'talkback':
result.append( { 'id' : name
, 'title' : title
, 'url' : '%s/%s/view%s' % (portal_url, '/'.join(now), param)
......
......@@ -8225,7 +8225,14 @@ class _LocalTemplateItemMixin:
self.assertEqual(component.getTextContent(), sequence['document_data'])
self.assertEqual(component.getPortalType(), self.component_portal_type)
self.assertEqual(component.getSourceReference(), sequence['document_source_reference'])
self.assertEqual(component.getValidationState(), 'validated')
if self.component_portal_type in ('Extension Component', 'Test Component'):
self.assertEqual(component.getValidationState(), 'validated')
else:
# Not validated automatically
self.assertEqual(component.getValidationState(), 'draft')
component.validate()
self.tic()
self.assertEqual(component.getValidationState(), 'validated')
sequence.edit(document_id=component_id)
def test_BusinessTemplateWithZodbDocumentMigrated(self):
......
......@@ -33,6 +33,7 @@ import httplib
import urlparse
import base64
import urllib
import pkg_resources
from AccessControl.SecurityManagement import newSecurityManager
from Testing import ZopeTestCase
......@@ -667,7 +668,11 @@ class TestERP5Core(ERP5TypeTestCase, ZopeTestCase.Functional):
document_1.standard_error_message(error_type="MyErrorType", error_message="my error message.")
response = self.publish(document_1.getPath(), self.auth)
self.assertEqual(response.getStatus(), 401)
if pkg_resources.get_distribution("Products.CMFCore").version < "2.3":
self.assertEqual(response.getStatus(), 401)
else:
self.assertEqual(response.getStatus(), 302)
self.assertIn("/login_form", response.headers['location'])
self.assertNotIn("Also, the following error occurred", str(response))
def testCategoryExport(self):
......
......@@ -642,8 +642,6 @@ class TestProxyField(ERP5TypeTestCase):
ERP5Form('Base_viewProxyFieldLibrary', 'Proxys'))
self.container._setObject('Base_view',
ERP5Form('Base_view', 'View'))
from Products.CMFCore.tests.base.utils import _setUpDefaultTraversable
_setUpDefaultTraversable()
def addField(self, form, id, title, field_type):
......
......@@ -31,6 +31,7 @@
"""
import itertools
import pkg_resources
import transaction
import unittest
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
......@@ -1067,7 +1068,11 @@ class TestLocalRoleManagement(RoleManagementTestCase):
self.assertEqual(response.getStatus(), 200)
response = self.publish('/%s/first_last/getFirstName' % person_module_path,
basic='guest:guest')
self.assertEqual(response.getStatus(), 401)
if pkg_resources.get_distribution("Products.CMFCore").version < "2.3":
self.assertEqual(response.getStatus(), 401)
else:
self.assertEqual(response.getStatus(), 302)
self.assertIn("/login_form", response.headers['location'])
# Organisation does not have explicitly declared getTitle method in
# the class definition.
......@@ -1078,7 +1083,11 @@ class TestLocalRoleManagement(RoleManagementTestCase):
self.tic()
response = self.publish('/%s/my_company/getTitle' % self.getOrganisationModule().absolute_url(relative=1),
basic='guest:guest')
self.assertEqual(response.getStatus(), 401)
if pkg_resources.get_distribution("Products.CMFCore").version < "2.3":
self.assertEqual(response.getStatus(), 401)
else:
self.assertEqual(response.getStatus(), 302)
self.assertIn("/login_form", response.headers['location'])
class TestKeyAuthentication(RoleManagementTestCase):
......
......@@ -203,7 +203,7 @@ class EssendexGateway(XMLObject):
raise SMSGatewayError, urllib.unquote(result.get('Message', "Impossible to get the message status"))
security.declarePublic('receive')
def receive(self,REQUEST):
def receive(self, REQUEST, **kw):
"""Receive push notification"""
#XML is stored is BODY of request
......@@ -328,7 +328,7 @@ class EssendexGateway(XMLObject):
if result['Result'] == "OK":
#Push all message
type_mapping = {'Text': 'text/plain'}
now == DateTime()
now = DateTime()
for key, value in result.items():
if type(key) == int:
reception_date = self._parseDate(value['ReceivedAt'])
......
......@@ -48,6 +48,8 @@ class ExtensionComponent(ComponentMixin, TextContentHistoryMixin):
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
do_validate_on_import_from_filesystem = True
@staticmethod
def _getFilesystemPath():
import os.path
......
......@@ -49,6 +49,8 @@ class TestComponent(ComponentMixin, TextContentHistoryMixin):
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
do_validate_on_import_from_filesystem = True
@staticmethod
def _getFilesystemPath():
import os.path
......
......@@ -507,11 +507,13 @@ def checkPythonSourceCode(source_code_str, portal_type=None):
if portal_type == 'Interface Component':
# Interface inherits from InterfaceClass:
# E: 4, 0: Inheriting 'Interface', which is not a class. (inherit-non-class)
# Inheriting 'Interface', which is not a class. (inherit-non-class)
args.append('--disable=E0239')
# Interfaces methods have no arguments:
# E: 5, 2: Method has no argument (no-method-argument)
# Interfaces methods may have no arguments:
# Method has no argument (no-method-argument)
args.append('--disable=E0211')
# Method should have "self" as first argument (no-self-argument)
args.append('--disable=E0213')
try:
from pylint.extensions.bad_builtin import __name__ as ext
......
......@@ -34,6 +34,7 @@ from Products.ERP5Type.patches import DCWorkflow
from Products.ERP5Type.patches import Worklists
from Products.ERP5Type.patches import BTreeFolder2
from Products.ERP5Type.patches import WorkflowTool
from Products.ERP5Type.patches import DynamicType
from Products.ERP5Type.patches import XMLExportImport
from Products.ERP5Type.patches import ppml
from Products.ERP5Type.patches import Expression
......
......@@ -342,6 +342,13 @@ class ComponentMixin(PropertyRecordableMixin, Base):
"""
return self.getTextContent()
# Whether ZODB Components is going to be validated or not should depend on
# its types because it is fine to validate '{Test,Extension} Component' as
# it not going to break anything but not for {Document,Interface,Mixin,Tool}
# Components...
do_validate_on_import_from_filesystem = False
security.declareProtected(Permissions.ModifyPortalContent,
'importFromFilesystem')
@classmethod
......@@ -388,7 +395,9 @@ class ComponentMixin(PropertyRecordableMixin, Base):
from Products.DCWorkflow.DCWorkflow import ValidationFailed
raise ValidationFailed(consistency_message_list)
new_component.validate()
if cls.do_validate_on_import_from_filesystem:
new_component.validate()
return new_component
InitializeClass(ComponentMixin)
......@@ -141,11 +141,9 @@ def CMFCoreSkinnableSkinnableObjectManager_changeSkin(self, skinname, REQUEST=No
Patched not to call getSkin.
'''
if skinname is None:
sfn = self.getSkinsFolderName()
if sfn is not None:
sf = getattr(self, sfn, None)
if sf is not None:
skinname = sf.getDefaultSkin()
sf = getattr(self, "portal_skins", None)
if sf is not None:
skinname = sf.getDefaultSkin()
tid = get_ident()
SKINDATA[tid] = (
None,
......
......@@ -176,12 +176,19 @@ def modifyRequest(self, req, resp):
CookieCrumbler.modifyRequest = modifyRequest
def credentialsChanged(self, user, name, pw):
ac = standard_b64encode('%s:%s' % (name, pw))
method = self.getCookieMethod( 'setAuthCookie'
, self.defaultSetAuthCookie )
resp = self.REQUEST['RESPONSE']
method( resp, self.auth_cookie, quote( ac ) )
def credentialsChanged(self, user, name, pw, request=None):
"""
Updates cookie credentials if user details are changed.
"""
if request is None:
request = getRequest() # BBB for Membershiptool
reponse = request['RESPONSE']
# <patch>
ac = standard_b64encode('%s:%s' % (name, pw)).rstrip()
# </patch>
method = self.getCookieMethod('setAuthCookie',
self.defaultSetAuthCookie)
method(reponse, self.auth_cookie, quote(ac))
CookieCrumbler.credentialsChanged = credentialsChanged
......
##############################################################################
#
# Copyright (c) 2001 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
from Products.CMFCore.DynamicType import DynamicType
def getTypeInfo(self):
""" Get the TypeInformation object specified by the portal type.
"""
# <patch>
tool = getattr(self.getPortalObject(), "portal_types", None)
# </patch>
if tool is None:
return None
return tool.getTypeInfo(self) # Can return None.
DynamicType.getTypeInfo = getTypeInfo
\ No newline at end of file
......@@ -979,4 +979,17 @@ def canDoActionFor(self, ob, action, wf_id=None, guard_kw={}):
WorkflowTool.canDoActionFor = canDoActionFor
security.declarePrivate('_listTypeInfo')
def _listTypeInfo(self):
""" List the portal types which are available.
"""
# <patch>
ttool = getattr(self.getPortalObject(), "portal_types", None)
# </patch>
if ttool is not None:
return ttool.listTypeInfo()
return ()
WorkflowTool._listTypeInfo = _listTypeInfo
InitializeClass(WorkflowTool)
......@@ -20,8 +20,7 @@ from OFS.DTMLMethod import DTMLMethod
from OFS.Folder import Folder
from AccessControl.User import UserFolder
from Products.CMFCore.CookieCrumbler \
import CookieCrumbler, manage_addCC, Redirect
from Products.CMFCore.CookieCrumbler import CookieCrumbler, manage_addCC
from Products.CMFCore.tests.test_CookieCrumbler import makerequest
from Products.CMFCore.tests.test_CookieCrumbler import CookieCrumblerTests
......
......@@ -118,8 +118,8 @@ class WorkingCopy(Implicit):
parts = path.split(os.sep)
try:
i = len(parts) - parts[::-1].index(os.pardir)
parts[:i] = os.realpath(parts[:i].join(os.sep)),
path = parts.join(os.sep)
parts[:i] = os.path.realpath(os.sep.join(parts[:i])),
path = os.sep.join(parts)
except ValueError:
pass
# Allow symlinks inside instance home.
......
......@@ -18,7 +18,12 @@ class ERP5_scalability():
return 'example/scalabilityUsers'
def getUserQuantity(self, test_number):
return [8, 14, 20, 28, 36][test_number]
"""
Return number of users for a given test run.
"""
users_per_zope_process = 8
max_tests = 100
return [x * users_per_zope_process for x in range(1, max_tests)][test_number]
def getTestDuration(self, test_number):
return 60*10
......