Commit 3347e3f9 authored by Rafael Monnerat's avatar Rafael Monnerat

Update from upstream/master

parents 2718c9f4 b1dcfe05
Pipeline #35580 failed with stage
in 0 seconds
......@@ -16,18 +16,6 @@ request_form = context.ERP5Site_filterParameterList(request_form)
request_form.update(keep_items)
# XXX Allow CORS
response = context.REQUEST.RESPONSE
context.Base_prepareCorsResponse(RESPONSE=response)
# http://en.wikipedia.org/wiki/Post/Redirect/Get
response.setStatus(201, lock=True)
response.setHeader("X-Location", "urn:jio:get:%s" % context.getRelativeUrl())
# be explicit with the reponse content type because in case of reports - they
# can be in text/plain, application/pdf ... so the RJS form needs to know what
# is going exactly on. ERP5Document_getHateoas returns application/hal+json
# therefor we don't need to be afraid of clashes
response.setHeader("Content-type", "application/json; charset=utf-8")
portal_status_level = keep_items.pop("portal_status_level", "success")
if portal_status_level in ("warning", "error", "fatal"):
portal_status_level = "error"
......@@ -70,8 +58,20 @@ if (form_id not in [None, 'Base_viewFakePythonScriptActionForm', 'Base_viewFakeJ
)
}
# XXX Allow CORS
response = context.REQUEST.RESPONSE
context.Base_prepareCorsResponse(RESPONSE=response)
response.setHeader("X-Location", "urn:jio:get:%s" % context.getRelativeUrl())
# be explicit with the reponse content type because in case of reports - they
# can be in text/plain, application/pdf ... so the RJS form needs to know what
# is going exactly on. ERP5Document_getHateoas returns application/hal+json
# therefor we don't need to be afraid of clashes
response.setHeader("Content-type", "application/json; charset=utf-8")
result = json.dumps(result_dict, indent=2)
# http://en.wikipedia.org/wiki/Post/Redirect/Get
response.setStatus(201, lock=True)
if abort_transaction:
response.setBody(result, lock=True)
raise Redirect('')
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Base Type" module="erp5.portal_type"/>
<global name="Delivery Type" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......@@ -68,6 +68,10 @@
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>type_class</string> </key>
<value> <string>AccountingTransaction</string> </value>
</item>
</dictionary>
</pickle>
</record>
......
......@@ -21,8 +21,8 @@ elif code is not None:
code,
"{0}/ERP5Site_callbackFacebookLogin".format(context.absolute_url()))
if response_dict is not None:
access_token = response_dict['access_token'].encode('utf-8')
hash_str = context.Base_getHMAC(access_token, access_token)
access_token = response_dict['access_token']
hash_str = context.Base_getHMAC(access_token.encode('utf-8'), access_token.encode('utf-8'))
context.setAuthCookie(response, '__ac_facebook_hash', hash_str)
# store timestamp in second since the epoch in UTC is enough
......
##############################################################################
# Copyright (c) 2024 Nexedi SA and Contributors. All Rights Reserved.
#
# 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.
#
##############################################################################
from AccessControl import ClassSecurityInfo
import random
import string
import time
import oauthlib.oauth2
import requests
from zExceptions import Unauthorized
from Products.ERP5Type.XMLObject import XMLObject
from Products.ERP5Type import Permissions
from Products.ERP5Type.Utils import unicode2str
from Products.ERP5Type.Timeout import getTimeLeft
AUTH_URL = 'https://accounts.google.com/o/oauth2/auth'
TOKEN_URL = 'https://accounts.google.com/o/oauth2/token'
USER_INFO_URL = 'https://www.googleapis.com/oauth2/v1/userinfo'
SCOPE_LIST = ['https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/userinfo.email']
# Default timeout (in seconds) for the HTTP request made to google servers to
# exchange the authorization code for a token.
DEFAULT_HTTP_TIMEOUT = 10
class GoogleConnector(XMLObject):
meta_type = 'ERP5 Google Connector'
portal_type = 'Google Connector'
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
@security.public
def redirectToGoogleLoginPage(self, redirect_uri, RESPONSE):
"""Redirect to authorization page.
"""
authorization_url = self._getOAuthlibClient().prepare_request_uri(
uri=AUTH_URL,
redirect_uri=redirect_uri,
scope=SCOPE_LIST,
access_type="offline",
include_granted_scopes='true',
prompt="consent",
state=self._getAuthorizationState(),
)
return RESPONSE.redirect(authorization_url)
@security.public # XXX public but not publishable
def getTokenFromCode(self, state, code, redirect_uri):
self._verifyAuthorizationState(state)
body = self._getOAuthlibClient().prepare_request_body(
code=code,
client_secret=self.getSecretKey(),
redirect_uri=redirect_uri,
)
resp = requests.post(
TOKEN_URL,
data=body,
headers={'Content-Type': 'application/x-www-form-urlencoded'},
timeout=self._getTimeout(),
)
__traceback_info__ = (resp.content, resp.status_code)
resp.raise_for_status()
return self._getGoogleTokenFromJSONResponse(resp.json())
@security.private
def refreshToken(self, token):
"""Refresh auth token.
Used by Products.ERP5Security.ERP5ExternalOauth2ExtractionPlugin
"""
body = self._getOAuthlibClient().prepare_refresh_body(
client_id=self.getClientId(),
client_secret=self.getSecretKey(),
access_type="offline",
refresh_token=token['refresh_token'],
)
resp = requests.post(
TOKEN_URL,
data=body,
headers={'Content-Type': 'application/x-www-form-urlencoded'},
timeout=self._getTimeout(),
)
if not resp.ok:
return {}
return self._getGoogleTokenFromJSONResponse(resp.json())
@security.private
def getUserEntry(self, access_token):
resp = requests.get(
USER_INFO_URL,
headers={'Authorization': 'Bearer {}'.format(access_token)},
timeout=self._getTimeout(),
)
resp.raise_for_status()
google_entry = resp.json()
user_entry = {}
# remap user info
for erp5_key, google_key in (
('first_name', 'given_name'),
('last_name', 'family_name'),
('email', 'email'),
('reference', 'email'),
):
user_entry[erp5_key] = unicode2str(google_entry.get(google_key, ''))
return user_entry
def _getOAuthlibClient(self):
return oauthlib.oauth2.WebApplicationClient(
self.getClientId(),
access_type="offline",
)
def _getGoogleTokenFromJSONResponse(self, token):
return {
'access_token': unicode2str(token['access_token']),
'refresh_token': unicode2str(token['refresh_token']),
'expires_in': token['expires_in'],
'response_timestamp': time.time(),
'connector_relative_url': self.getRelativeUrl(),
}
def _getAuthorizationState(self):
alphabet = string.ascii_letters + string.digits
state = ''.join(random.SystemRandom().choice(alphabet) for _ in range(32))
self.getPortalObject().portal_sessions['google_login_auth_state'][state] = True
return state
def _verifyAuthorizationState(self, state):
if not self.getPortalObject().portal_sessions['google_login_auth_state'].pop(state, False):
raise Unauthorized
def _getTimeout(self):
"""Compute the time left according to publisher deadline.
"""
time_left = getTimeLeft()
if time_left is None:
time_left = DEFAULT_HTTP_TIMEOUT
return min(self.getTimeout() or DEFAULT_HTTP_TIMEOUT, time_left)
......@@ -2,13 +2,13 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Extension Component" module="erp5.portal_type"/>
<global name="Document Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>GoogleLoginUtility</string> </value>
<value> <string>GoogleConnector</string> </value>
</item>
<item>
<key> <string>description</string> </key>
......@@ -18,11 +18,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>extension.erp5.GoogleLoginUtility</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Extension Component</string> </value>
<value> <string>document.erp5.GoogleConnector</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
......
import json
import oauth2client.client
import oauth2client.transport
from Products.ERP5Security.ERP5ExternalOauth2ExtractionPlugin import getGoogleUserEntry
from zExceptions import Unauthorized
SCOPE_LIST = ['https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/userinfo.email']
# Default timeout (in seconds) for the HTTP request made to google servers to
# exchange the authorization code for a token.
DEFAULT_HTTP_TIMEOUT = 10
def _getGoogleClientIdAndSecretKey(portal, reference="default"):
"""Returns google client id and secret key.
Internal function.
"""
result_list = unrestrictedSearchGoogleConnector(portal, reference=reference)
assert result_list, "Google Connector not found"
if len(result_list) == 2:
raise ValueError("Impossible to select one Google Connector")
google_connector = result_list[0].getObject()
return google_connector.getClientId(), google_connector.getSecretKey()
def redirectToGoogleLoginPage(self):
client_id, secret_key = _getGoogleClientIdAndSecretKey(self.getPortalObject())
flow = oauth2client.client.OAuth2WebServerFlow(
client_id=client_id,
client_secret=secret_key,
scope=SCOPE_LIST,
redirect_uri="{0}/ERP5Site_receiveGoogleCallback".format(self.absolute_url()),
access_type="offline",
prompt="consent",
include_granted_scopes="true")
self.REQUEST.RESPONSE.redirect(flow.step1_get_authorize_url())
def getAccessTokenFromCode(self, code, redirect_uri, timeout=DEFAULT_HTTP_TIMEOUT):
client_id, secret_key = _getGoogleClientIdAndSecretKey(self.getPortalObject())
flow = oauth2client.client.OAuth2WebServerFlow(
client_id=client_id,
client_secret=secret_key,
scope=SCOPE_LIST,
redirect_uri=redirect_uri,
access_type="offline",
include_granted_scopes="true")
credential = flow.step2_exchange(
code,
http=oauth2client.transport.get_http_object(timeout=timeout))
credential_data = json.loads(credential.to_json())
return credential_data
def unrestrictedSearchGoogleConnector(self, reference="default"):
return self.getPortalObject().portal_catalog.unrestrictedSearchResults(
portal_type="Google Connector",
reference=reference,
validation_state="validated",
limit=2)
def unrestrictedSearchGoogleLogin(self, login, REQUEST=None):
if REQUEST is not None:
raise Unauthorized
return self.getPortalObject().portal_catalog.unrestrictedSearchResults(
portal_type="Google Login",
reference=login,
validation_state="validated", limit=1)
def getUserEntry(access_token):
return getGoogleUserEntry(access_token)
\ No newline at end of file
<property_sheet_list>
<portal_type id="Google Connector">
<item>OAuthClient</item>
<item>SocketClient</item>
</portal_type>
<portal_type id="Template Tool">
<item>TemplateToolERP5GoogleExtractionPluginConstraint</item>
......
......@@ -40,7 +40,7 @@
</item>
<item>
<key> <string>type_class</string> </key>
<value> <string>XMLObject</string> </value>
<value> <string>GoogleConnector</string> </value>
</item>
<item>
<key> <string>type_interface</string> </key>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ExternalMethod" module="Products.ExternalMethod.ExternalMethod"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_function</string> </key>
<value> <string>getAccessTokenFromCode</string> </value>
</item>
<item>
<key> <string>_module</string> </key>
<value> <string>GoogleLoginUtility</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ERP5Site_getAccessTokenFromCode</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
portal = context.getPortalObject()
result_list = portal.portal_catalog(
portal_type="Google Connector",
reference=reference,
validation_state="validated",
limit=2)
if len(result_list) != 1:
raise ValueError("Impossible to select one Google Connector")
return result_list[0].getObject()
......@@ -50,19 +50,19 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>login, REQUEST=None</string> </value>
<value> <string>reference="default"</string> </value>
</item>
<item>
<key> <string>_proxy_roles</string> </key>
<value>
<tuple>
<string>Manager</string>
<string>Auditor</string>
</tuple>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ERP5Site_getPersonFromGoogleLogin</string> </value>
<value> <string>ERP5Site_getDefaultGoogleConnector</string> </value>
</item>
</dictionary>
</pickle>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ExternalMethod" module="Products.ExternalMethod.ExternalMethod"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_function</string> </key>
<value> <string>unrestrictedSearchGoogleConnector</string> </value>
</item>
<item>
<key> <string>_module</string> </key>
<value> <string>GoogleLoginUtility</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ERP5Site_getGoogleConnector</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="ExternalMethod" module="Products.ExternalMethod.ExternalMethod"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_function</string> </key>
<value> <string>unrestrictedSearchGoogleConnector</string> </value>
</item>
<item>
<key> <string>_module</string> </key>
<value> <string>GoogleLoginUtility</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ERP5Site_getGoogleLogin</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="ExternalMethod" module="Products.ExternalMethod.ExternalMethod"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_function</string> </key>
<value> <string>getUserEntry</string> </value>
</item>
<item>
<key> <string>_module</string> </key>
<value> <string>GoogleLoginUtility</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ERP5Site_getGoogleUserEntry</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
from zExceptions import Unauthorized
if REQUEST is not None:
raise Unauthorized
login = context.ERP5Site_getGoogleLogin(login)
if login is None:
return login
if len(login) > 1:
raise ValueError("Duplicated User")
return login[0].getParentValue().getRelativeUrl()
import time
from Products.ERP5Type.Utils import str2bytes
portal = context.getPortalObject()
request = container.REQUEST
response = request.RESPONSE
......@@ -16,26 +17,21 @@ if error is not None:
return handleError(error)
elif code is not None:
response_dict = context.ERP5Site_getAccessTokenFromCode(
code,
"{0}/ERP5Site_receiveGoogleCallback".format(context.absolute_url()))
if response_dict is not None:
access_token = response_dict['access_token'].encode('utf-8')
hash_str = context.Base_getHMAC(access_token, access_token)
context.setAuthCookie(response, '__ac_google_hash', hash_str)
# store timestamp in second since the epoch in UTC is enough
response_dict["response_timestamp"] = time.time()
context.Base_setBearerToken(hash_str,
google_connector = portal.ERP5Site_getDefaultGoogleConnector()
response_dict = google_connector.getTokenFromCode(
state=state,
code=code,
redirect_uri="{}/ERP5Site_receiveGoogleCallback".format(portal.absolute_url()),
)
access_token = str2bytes(response_dict['access_token'])
hash_str = portal.Base_getHMAC(access_token, access_token)
portal.setAuthCookie(response, '__ac_google_hash', hash_str)
portal.Base_setBearerToken(
hash_str,
response_dict,
"google_server_auth_token_cache_factory")
user_dict = context.ERP5Site_getGoogleUserEntry(access_token)
user_reference = user_dict["email"]
context.Base_setBearerToken(access_token,
{"reference": user_reference},
"google_server_auth_token_cache_factory")
method = getattr(context, "ERP5Site_createGoogleUserToOAuth", None)
if method is not None:
method(user_reference, user_dict)
# XXX for ERP5JS web sites without a rewrite rule, we make sure there's a trailing /
return response.redirect(request.get("came_from") or context.absolute_url() + '/')
......
......@@ -50,7 +50,7 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>code=None, error=None</string> </value>
<value> <string>code=None, state=None, error=None</string> </value>
</item>
<item>
<key> <string>id</string> </key>
......
portal = context.getPortalObject()
google_connector = portal.ERP5Site_getDefaultGoogleConnector()
return google_connector.redirectToGoogleLoginPage(
"{0}/ERP5Site_receiveGoogleCallback".format(portal.absolute_url()),
RESPONSE=RESPONSE,
)
......@@ -2,25 +2,67 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ExternalMethod" module="Products.ExternalMethod.ExternalMethod"/>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_function</string> </key>
<value> <string>redirectToGoogleLoginPage</string> </value>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</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>_module</string> </key>
<value> <string>GoogleLoginUtility</string> </value>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ERP5Site_redirectToGoogleLoginPage</string> </value>
<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>RESPONSE</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
<key> <string>_proxy_roles</string> </key>
<value>
<tuple>
<string>Auditor</string>
</tuple>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ERP5Site_redirectToGoogleLoginPage</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -25,290 +25,373 @@
#
##############################################################################
import uuid
import contextlib
import mock
import lxml
import urlparse
import httplib
import json
import responses
import time
import six.moves.urllib as urllib
import six.moves.http_client
import six.moves.http_cookies
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from Products.ERP5Type.tests.utils import createZODBPythonScript
CLIENT_ID = "a1b2c3"
SECRET_KEY = "3c2ba1"
ACCESS_TOKEN = "T1234"
CODE = "1234"
def getUserId(access_token):
return "dummy@example.com"
def getAccessTokenFromCode(code, redirect_uri):
assert code == CODE, "Invalid code"
# This is an example of a Google response
return {'_module': 'oauth2client.client',
'scopes': ['https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile'],
'revoke_uri': 'https://accounts.google.com/o/oauth2/revoke',
'access_token': ACCESS_TOKEN,
'token_uri': 'https://www.googleapis.com/oauth2/v4/token',
'token_info_uri': 'https://www.googleapis.com/oauth2/v3/tokeninfo',
'invalid': False,
'token_response': {
'access_token': ACCESS_TOKEN,
'token_type': 'Bearer',
'expires_in': 3600,
'refresh_token': "111",
'id_token': '222'
},
'client_id': CLIENT_ID,
'id_token': {
'picture': '',
'sub': '',
'aud': '',
'family_name': 'D',
'iss': 'https://accounts.google.com',
'email_verified': True,
'at_hash': 'p3vPYQkVuqByBA',
'given_name': 'John',
'exp': 123,
'azp': '123.apps.googleusercontent.com',
'iat': 455,
'locale': 'pt',
'email': getUserId(None),
'name': 'John D'
},
'client_secret': 'secret',
'token_expiry': '2017-03-31T16:06:28Z',
'_class': 'OAuth2Credentials',
'refresh_token': '111',
'user_agent': None
}
def getUserEntry(access_token):
return {
"first_name": "John",
"last_name": "Doe",
"email": getUserId(None),
"reference": getUserId(None)
}
class GoogleLoginTestCase(ERP5TypeTestCase):
default_google_login_email_address = 'dummy@example.com'
dummy_connector_id = 'test_google_connector'
client_id = "a1b2c3"
secret_key = "3c2ba1"
def afterSetUp(self):
"""
This is ran before anything, used to set the environment
"""
self.login()
self.portal.TemplateTool_checkGoogleExtractionPluginExistenceConsistency(fixit=True)
self.dummy_connector_id = "test_google_connector"
# use random tokens because we can not clear the memcached cache
self.access_token = 'access-token' + self.id() + self.newPassword()
self.refresh_token = 'refresh-token' + self.id() + self.newPassword()
self.default_user_person = self.portal.person_module.newContent(
portal_type='Person',
first_name=self.id(),
)
self.default_user_person.newContent(
portal_type='Google Login',
reference=self.default_google_login_email_address,
).validate()
self.default_user_person.newContent(portal_type='Assignment').open()
if getattr(self.portal.portal_oauth, self.dummy_connector_id, None) is None:
connector = self.portal.portal_oauth.newContent(
id=self.dummy_connector_id,
portal_type="Google Connector",
reference="default",
client_id=self.client_id,
secret_key=self.secret_key)
connector.validate()
self.tic()
def beforeTearDown(self):
self.abort()
self.portal.portal_caches.getRamCacheRoot().get(
self.portal.acl_users.erp5_google_extraction.cache_factory_name
).clearCache()
self.portal.person_module.manage_delObjects([self.default_user_person.getId()])
portal_catalog = self.portal.portal_catalog
for obj in portal_catalog(portal_type=["Google Login", "Person"],
reference=getUserId(None),
validation_state="validated"):
obj.getObject().invalidate()
uuid_str = uuid.uuid4().hex
obj.setReference(uuid_str)
obj.setUserId(uuid_str)
for connector in portal_catalog(portal_type="Google Connector",
validation_state="validated",
id="NOT %s" % self.dummy_connector_id,
reference="default"):
connector.invalidate()
if getattr(self.portal.portal_oauth, self.dummy_connector_id, None) is None:
connector = self.portal.portal_oauth.newContent(id=self.dummy_connector_id,
portal_type="Google Connector",
reference="default",
client_id=CLIENT_ID,
secret_key=SECRET_KEY)
connector.validate()
self.tic()
self.logout()
@contextlib.contextmanager
def _default_login_responses(self):
with responses.RequestsMock() as rsps:
rsps.add(
method='POST',
url='https://accounts.google.com/o/oauth2/token',
json={
'access_token': self.access_token,
'refresh_token': self.refresh_token,
'expires_in': 3600,
},
)
rsps.add(
method='GET',
url='https://www.googleapis.com/oauth2/v1/userinfo',
json={
"first_name": "John",
"last_name": "Doe",
"email": self.default_google_login_email_address,
}
)
yield
class TestGoogleLogin(GoogleLoginTestCase):
def test_redirect(self):
"""
Check URL generate to redirect to Google
Check URL generated to redirect to Google
"""
self.logout()
self.portal.ERP5Site_redirectToGoogleLoginPage()
location = self.portal.REQUEST.RESPONSE.getHeader("Location")
response = self.portal.REQUEST.RESPONSE
self.portal.ERP5Site_redirectToGoogleLoginPage(RESPONSE=response)
location = response.getHeader("Location")
self.assertIn("https://accounts.google.com/o/oauth2/", location)
self.assertIn("response_type=code", location)
self.assertIn("client_id=%s" % CLIENT_ID, location)
self.assertIn("client_id=%s" % self.client_id, location)
self.assertNotIn("secret_key=", location)
self.assertIn("ERP5Site_receiveGoogleCallback", location)
def test_existing_user(self):
self.login()
person = self.portal.person_module.newContent(
portal_type='Person',
)
person.newContent(
portal_type='Google Login',
reference=getUserId(None)
).validate()
person.newContent(portal_type='Assignment').open()
self.tic()
self.logout()
request = self.portal.REQUEST
response = request.RESPONSE
with mock.patch(
'erp5.component.extension.GoogleLoginUtility.getAccessTokenFromCode',
side_effect=getAccessTokenFromCode,
) as getAccessTokenFromCode_mock, \
mock.patch(
'erp5.component.extension.GoogleLoginUtility.getUserEntry',
side_effect=getUserEntry
) as getUserEntry_mock:
getAccessTokenFromCode_mock.__code__ = getAccessTokenFromCode.__code__
getUserEntry_mock.__code__ = getUserEntry.__code__
self.portal.ERP5Site_receiveGoogleCallback(code=CODE)
getAccessTokenFromCode_mock.assert_called_once()
getUserEntry_mock.assert_called_once()
request["__ac_google_hash"] = response.cookies["__ac_google_hash"]["value"]
redirect_url = urllib.parse.urlparse(
self.portal.ERP5Site_redirectToGoogleLoginPage(RESPONSE=response))
state = dict(urllib.parse.parse_qsl(redirect_url.query))['state']
self.assertTrue(state)
with mock.patch(
code = 'code-ABC'
def token_callback(request):
self.assertEqual(
request.headers['Content-Type'],
'application/x-www-form-urlencoded')
self.assertEqual(
dict(urllib.parse.parse_qsl(request.body)),
{
'client_id': self.client_id,
'code': code,
'grant_type': 'authorization_code',
'client_secret': self.secret_key,
'redirect_uri': self.portal.absolute_url() + '/ERP5Site_receiveGoogleCallback',
},
)
return 200, {}, json.dumps({
'access_token': self.access_token,
'refresh_token': self.refresh_token,
'expires_in': 3600,
})
with responses.RequestsMock() as rsps:
rsps.add_callback(
responses.POST,
'https://accounts.google.com/o/oauth2/token',
token_callback,
)
self.portal.ERP5Site_receiveGoogleCallback(code=code, state=state)
def userinfo_callback(request):
self.assertEqual(
request.headers['Authorization'],
'Bearer ' + self.access_token)
return 200, {}, json.dumps({
"first_name": "John",
"last_name": "Doe",
"email": self.default_google_login_email_address,
})
request['__ac_google_hash'] = response.cookies['__ac_google_hash']['value']
with responses.RequestsMock() as rsps, \
mock.patch(
'Products.ERP5Security.ERP5ExternalOauth2ExtractionPlugin._setUserNameForAccessLog'
) as _setUserNameForAccessLog:
credentials = self.portal.acl_users.erp5_google_extraction.extractCredentials(request)
self.assertEqual(
'Google Login',
credentials['login_portal_type'])
rsps.add_callback(
responses.GET,
'https://www.googleapis.com/oauth2/v1/userinfo',
userinfo_callback,
)
credentials = self.portal.acl_users.erp5_google_extraction.extractCredentials(
request)
self.assertEqual(credentials['login_portal_type'], 'Google Login')
self.assertEqual(
getUserId(None),
credentials['external_login'])
credentials['external_login'],
self.default_google_login_email_address)
# this is what will appear in Z2.log
_setUserNameForAccessLog.assert_called_once_with(
'erp5_google_extraction=%s' % getUserId(None),
'erp5_google_extraction=dummy@example.com',
request)
user_id, login = self.portal.acl_users.erp5_login_users.authenticateCredentials(credentials)
self.assertEqual(person.getUserId(), user_id)
self.assertEqual(getUserId(None), login)
user_id, user_name = self.portal.acl_users.erp5_login_users.authenticateCredentials(credentials)
self.assertEqual(user_id, self.default_user_person.getUserId())
self.assertEqual(user_name, self.default_google_login_email_address)
self.login(user_id)
self.assertEqual(self.portal.Base_getUserCaption(), login)
self.assertEqual(self.portal.Base_getUserCaption(), user_name)
def test_auth_cookie(self):
request = self.portal.REQUEST
response = request.RESPONSE
# (the secure flag is only set if we accessed through https)
request.setServerURL('https', 'example.com')
with mock.patch(
'erp5.component.extension.GoogleLoginUtility.getAccessTokenFromCode',
side_effect=getAccessTokenFromCode,
) as getAccessTokenFromCode_mock, \
mock.patch(
'erp5.component.extension.GoogleLoginUtility.getUserEntry',
side_effect=getUserEntry
) as getUserEntry_mock:
getAccessTokenFromCode_mock.__code__ = getAccessTokenFromCode.__code__
getUserEntry_mock.__code__ = getUserEntry.__code__
self.portal.ERP5Site_receiveGoogleCallback(code=CODE)
getAccessTokenFromCode_mock.assert_called_once()
getUserEntry_mock.assert_called_once()
redirect_url = urllib.parse.urlparse(
self.portal.ERP5Site_redirectToGoogleLoginPage(RESPONSE=response))
state = dict(urllib.parse.parse_qsl(redirect_url.query))['state']
with self._default_login_responses():
self.portal.ERP5Site_receiveGoogleCallback(code='code', state=state)
ac_cookie, = [v for (k, v) in response.listHeaders() if k.lower() == 'set-cookie' and '__ac_google_hash=' in v]
self.assertIn('; secure', ac_cookie.lower())
self.assertIn('; httponly', ac_cookie.lower())
self.assertIn('; samesite=lax', ac_cookie.lower())
def test_create_user_in_ERP5Site_createGoogleUserToOAuth(self):
"""
Check if ERP5 set cookie properly after receive code from external service
"""
self.login()
id_list = []
for result in self.portal.portal_catalog(portal_type="Credential Request",
reference=getUserId(None)):
id_list.append(result.getObject().getId())
self.portal.credential_request_module.manage_delObjects(ids=id_list)
skin = self.portal.portal_skins.custom
createZODBPythonScript(skin, "CredentialRequest_createUser", "", """
person = context.getDestinationDecisionValue(portal_type="Person")
login_list = [x for x in person.objectValues(portal_type='Google Login') \
if x.getValidationState() == 'validated']
if len(login_list):
login = login_list[0]
else:
login = person.newContent(portal_type='Google Login')
reference = context.getReference()
if not login.hasReference():
if not reference:
raise ValueError("Impossible to create an account without login")
login.setReference(reference)
if not person.Person_getUserId():
person.setUserId(reference)
if login.getValidationState() == 'draft':
login.validate()
return reference, None
""")
createZODBPythonScript(skin, "ERP5Site_createGoogleUserToOAuth", "user_reference, user_dict", """
module = context.getPortalObject().getDefaultModule(portal_type='Credential Request')
credential_request = module.newContent(
portal_type="Credential Request",
first_name=user_dict["first_name"],
last_name=user_dict["last_name"],
reference=user_reference,
default_email_text=user_dict["email"],
)
credential_request.submit()
context.portal_alarms.accept_submitted_credentials.activeSense()
return credential_request
""")
self.logout()
# make sure user info URL is called for _default_login_responses
cookie = six.moves.http_cookies.SimpleCookie()
cookie.load(ac_cookie)
resp = self.publish(
self.portal.getPath(),
env={
'HTTP_COOKIE': '__ac_google_hash="%s"' % cookie.get('__ac_google_hash').value
}
)
self.assertEqual(resp.getStatus(), six.moves.http_client.OK)
def test_non_existing_user(self):
request = self.portal.REQUEST
response = request.RESPONSE
redirect_url = urllib.parse.urlparse(
self.portal.ERP5Site_redirectToGoogleLoginPage(RESPONSE=response))
state = dict(urllib.parse.parse_qsl(redirect_url.query))['state']
with responses.RequestsMock() as rsps:
rsps.add(
method='POST',
url='https://accounts.google.com/o/oauth2/token',
json={
'access_token': self.access_token,
'refresh_token': self.refresh_token,
'expires_in': 3600,
},
)
self.portal.ERP5Site_receiveGoogleCallback(code='code', state=state)
request['__ac_google_hash'] = response.cookies['__ac_google_hash']['value']
with responses.RequestsMock() as rsps:
rsps.add(
method='GET',
url='https://www.googleapis.com/oauth2/v1/userinfo',
json={
"first_name": "Bob",
"last_name": "Doe",
"email": "unknown@example.com",
}
)
credentials = self.portal.acl_users.erp5_google_extraction.extractCredentials(
request)
self.assertEqual(credentials['login_portal_type'], 'Google Login')
self.assertEqual(
credentials['external_login'],
"unknown@example.com")
self.assertIsNone(
self.portal.acl_users.erp5_login_users.authenticateCredentials(credentials))
def test_invalid_cookie(self):
request = self.portal.REQUEST
request['__ac_google_hash'] = '???'
credentials = self.portal.acl_users.erp5_google_extraction.extractCredentials(
request)
self.assertEqual(credentials, {})
def test_refresh_token(self):
request = self.portal.REQUEST
response = request.RESPONSE
redirect_url = urllib.parse.urlparse(
self.portal.ERP5Site_redirectToGoogleLoginPage(RESPONSE=response))
state = dict(urllib.parse.parse_qsl(redirect_url.query))['state']
with self._default_login_responses():
resp = self.publish(
'%s/ERP5Site_receiveGoogleCallback?%s' % (
self.portal.getPath(),
urllib.parse.urlencode(
{
'code': 'code',
'state': state,
}
)
)
)
self.assertEqual(resp.getStatus(), six.moves.http_client.FOUND)
env = {
'HTTP_COOKIE': '__ac_google_hash="%s"' % resp.getCookie('__ac_google_hash')['value']
}
resp = self.publish(self.portal.getPath(), env=env)
self.assertEqual(resp.getStatus(), six.moves.http_client.OK)
def token_callback(request):
self.assertEqual(
request.headers['Content-Type'],
'application/x-www-form-urlencoded')
self.assertEqual(
dict(urllib.parse.parse_qsl(request.body)),
{
'access_type': 'offline',
'client_id': self.client_id,
'client_secret': self.secret_key,
'grant_type': 'refresh_token',
'refresh_token': self.refresh_token,
}
)
return 200, {}, json.dumps({
'access_token': 'new' + self.access_token,
'refresh_token': 'new' + self.refresh_token,
'expires_in': 3600,
})
with mock.patch(
'erp5.component.extension.GoogleLoginUtility.getAccessTokenFromCode',
side_effect=getAccessTokenFromCode,
) as getAccessTokenFromCode_mock, \
mock.patch(
'erp5.component.extension.GoogleLoginUtility.getUserEntry',
side_effect=getUserEntry
) as getUserEntry_mock:
getAccessTokenFromCode_mock.__code__ = getAccessTokenFromCode.__code__
getUserEntry_mock.__code__ = getUserEntry.__code__
response = self.portal.ERP5Site_receiveGoogleCallback(code=CODE)
getAccessTokenFromCode_mock.assert_called_once()
getUserEntry_mock.assert_called_once()
google_hash = self.portal.REQUEST.RESPONSE.cookies.get("__ac_google_hash")["value"]
self.assertEqual("b01533abb684a658dc71c81da4e67546", google_hash)
absolute_url = self.portal.absolute_url()
self.assertNotEqual(absolute_url[-1], '/')
self.assertEqual(absolute_url + '/', response)
cache_dict = self.portal.Base_getBearerToken(google_hash, "google_server_auth_token_cache_factory")
self.assertEqual(CLIENT_ID, cache_dict["client_id"])
self.assertEqual(ACCESS_TOKEN, cache_dict["access_token"])
self.assertEqual({'reference': getUserId(None)},
self.portal.Base_getBearerToken(ACCESS_TOKEN, "google_server_auth_token_cache_factory")
'Products.ERP5Security.ERP5ExternalOauth2ExtractionPlugin.time.time',
return_value=time.time() + 5000), \
responses.RequestsMock() as rsps:
rsps.add_callback(
responses.POST,
'https://accounts.google.com/o/oauth2/token',
token_callback,
)
self.portal.REQUEST["__ac_google_hash"] = google_hash
erp5_google_extractor = self.portal.acl_users.erp5_google_extraction
self.assertEqual({'external_login': getUserId(None),
'login_portal_type': 'Google Login',
'remote_host': '',
'remote_address': ''}, erp5_google_extractor.extractCredentials(self.portal.REQUEST))
self.tic()
self.login()
credential_request = self.portal.portal_catalog(portal_type="Credential Request",
reference=getUserId(None))[0].getObject()
credential_request.accept()
person = credential_request.getDestinationDecisionValue()
google_login = person.objectValues(portal_types="Google Login")[0]
self.assertEqual(getUserId(None), google_login.getReference())
# refreshing the token calls userinfo again
rsps.add(
method='GET',
url='https://www.googleapis.com/oauth2/v1/userinfo',
json={
"first_name": "John",
"last_name": "Doe",
"email": self.default_google_login_email_address,
}
)
resp = self.publish(self.portal.getPath(), env=env)
self.assertEqual(resp.getStatus(), six.moves.http_client.OK)
resp = self.publish(self.portal.getPath(), env=env)
self.assertEqual(resp.getStatus(), six.moves.http_client.OK)
def test_refresh_token_expired(self):
request = self.portal.REQUEST
response = request.RESPONSE
redirect_url = urllib.parse.urlparse(
self.portal.ERP5Site_redirectToGoogleLoginPage(RESPONSE=response))
state = dict(urllib.parse.parse_qsl(redirect_url.query))['state']
with self._default_login_responses():
resp = self.publish(
'%s/ERP5Site_receiveGoogleCallback?%s' % (
self.portal.getPath(),
urllib.parse.urlencode(
{
'code': 'code',
'state': state,
}
)
)
)
self.assertEqual(resp.getStatus(), six.moves.http_client.FOUND)
env = {
'HTTP_COOKIE': '__ac_google_hash="%s"' % resp.getCookie('__ac_google_hash')['value']
}
resp = self.publish(self.portal.getPath(), env=env)
self.assertEqual(resp.getStatus(), six.moves.http_client.OK)
with mock.patch(
'Products.ERP5Security.ERP5ExternalOauth2ExtractionPlugin.time.time',
return_value=time.time() + 5000), \
responses.RequestsMock() as rsps:
rsps.add(
method='POST',
url='https://accounts.google.com/o/oauth2/token',
status=six.moves.http_client.UNAUTHORIZED,
)
resp = self.publish(self.portal.getPath(), env=env)
self.assertEqual(resp.getStatus(), six.moves.http_client.FOUND)
self.assertIn('/login_form', resp.getHeader('Location'))
def test_logout(self):
resp = self.publish(self.portal.getId() + '/logout')
......@@ -328,9 +411,9 @@ class TestERP5JSGoogleLogin(GoogleLoginTestCase):
if img.attrib['alt'] == 'Sign in with Google'
]
self.assertIn('/ERP5Site_redirectToGoogleLoginPage', google_login_link)
resp = self.publish(urlparse.urlparse(google_login_link).path)
resp = self.publish(urllib.parse.urlparse(google_login_link).path)
# this request redirects to google
self.assertEqual(resp.getStatus(), httplib.FOUND)
self.assertEqual(resp.getStatus(), six.moves.http_client.FOUND)
self.assertIn('google.com', resp.getHeader('Location'))
def test_logout(self):
......
document.erp5.GoogleConnector
\ No newline at end of file
extension.erp5.GoogleLoginUtility
\ No newline at end of file
Google Connector | OAuthClient
Google Connector | SocketClient
Template Tool | TemplateToolERP5GoogleExtractionPluginConstraint
\ No newline at end of file
......@@ -92,7 +92,6 @@
element_data,
sub_element,
a_element,
a_properties,
sub_element_list,
td_element_list,
tr_element_list = [];
......@@ -157,13 +156,9 @@
if (cell.editable || !cell.href) {
sub_element_list.push(sub_element);
} else {
a_properties = {
a_element = domsugar('a', {
"href": cell.href
};
if (cell.onclick) {
a_properties.onclick = cell.onclick;
}
a_element = domsugar('a', a_properties, [sub_element]);
}, [sub_element]);
sub_element_list.push(a_element);
}
......@@ -1000,9 +995,6 @@
if (value.url_value.command) {
url_value = line_link_list[counter + index];
index += 1;
} else if (value.url_value.onclick) {
url_value = false;
value.onclick = value.url_value.onclick;
} else {
url_value = false;
}
......
......@@ -228,7 +228,7 @@
</item>
<item>
<key> <string>actor</string> </key>
<value> <unicode>zope</unicode> </value>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
......@@ -242,7 +242,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>1016.40877.17868.16571</string> </value>
<value> <string>994.13949.44825.36573</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -262,7 +262,7 @@
</tuple>
<state>
<tuple>
<float>1715876236.83</float>
<float>1629394496.1</float>
<string>UTC</string>
</tuple>
</state>
......
<!DOCTYPE html>
<html>
<!--
data-i18n=Active Preference
data-i18n=Preferences
data-i18n=Language
data-i18n=Profile
......
......@@ -240,7 +240,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>994.61047.58814.54169</string> </value>
<value> <string>994.62318.46378.10649</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -260,7 +260,7 @@
</tuple>
<state>
<tuple>
<float>1632221574.49</float>
<float>1716899361.99</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -29,6 +29,7 @@
return RSVP.hash({
translation: gadget.getTranslationDict([
'Active Preference',
'Preferences',
'Language',
'Profile',
......@@ -50,8 +51,6 @@
// Calculate user name
if (result === undefined) {
user = null;
} else if (result.data.rows.length === 0) {
user = null;
} else {
user = result.data.rows[0].value.title;
}
......@@ -60,8 +59,10 @@
url_dict: gadget.getUrlForDict({
// Back url
back: {command: 'history_previous'},
preference: {command: 'push_history',
my_preference: {command: 'push_history',
options: {page: "preference"}},
portal_preference: {command: 'push_history',
options: {jio_key: "portal_preferences"}},
person: {command: 'push_history',
options: {jio_key: me_relative_url}},
// Change language
......@@ -75,7 +76,11 @@
domsugar(gadget.element.querySelector('.document_list'), [
domsugar('ul', {'class': 'document-listview'}, [
domsugar('li', [
domsugar('a', {href: result_dict.url_dict.preference,
domsugar('a', {href: result_dict.url_dict.my_preference,
text: result_dict.translation['Active Preference']})
]),
domsugar('li', [
domsugar('a', {href: result_dict.url_dict.portal_preference,
text: result_dict.translation.Preferences})
]),
(me_relative_url === undefined) ? '' :
......
......@@ -236,7 +236,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>998.18207.59213.64375</string> </value>
<value> <string>1014.4857.18905.5273</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -256,7 +256,7 @@
</tuple>
<state>
<tuple>
<float>1667992189.61</float>
<float>1716899348.27</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -222,7 +222,7 @@
}
var source_title = entry.value[gadget.state.source_column] || '',
attachment_list = entry.value
.DiscussionPost_getAttachmentList || [],
.Event_getAttachmentList || [],
attachment_element_list = [],
j,
word_list = source_title.split(' '),
......@@ -241,7 +241,10 @@
text: attachment_list[j].title,
href: attachment_list[j].url,
download: attachment_list[j].title
})
}),
' (',
attachment_list[j].content_type || 'application/octet-stream',
')'
])
);
}
......@@ -317,7 +320,7 @@
limit: limit_options,
select_list: ['asStrippedHTML', gadget.state.date_column,
gadget.state.source_column,
'DiscussionPost_getAttachmentList'],
'Event_getAttachmentList'],
sort_on: [[gadget.state.date_column, 'ASC'], ['uid', 'ASC']]
})
.push(function (result) {
......
......@@ -238,7 +238,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>1010.61000.1041.39372</string> </value>
<value> <string>1017.21412.29767.13277</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -258,7 +258,7 @@
</tuple>
<state>
<tuple>
<float>1693928044.6</float>
<float>1718640624.19</float>
<string>UTC</string>
</tuple>
</state>
......
return context.File_viewAsWeb(REQUEST=REQUEST, response=response, mapping_dict=mapping_dict)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</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>REQUEST=None, response=None, mapping_dict=None</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>OOoDocument_viewAsWeb</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
return context.File_viewAsWeb(REQUEST=REQUEST, response=response, mapping_dict=mapping_dict)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</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>REQUEST=None, response=None, mapping_dict=None</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>PDF_viewAsWeb</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -19,7 +19,7 @@
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/click_on_panel_link" />
</tal:block>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tal:block tal:define="click_configuration python: {'text': 'Preferences'}">
<tal:block tal:define="click_configuration python: {'text': 'Active Preference'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/click_on_page_link" />
</tal:block>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
......
......@@ -21,7 +21,7 @@
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tr>
<td>assertElementPresent</td>
<td>//ul[@class="ui-list-grid"]/li[2]/h2[text()="Tools"]</td>
<td>//ul[@class="ui-list-grid"]//li/h2[text()="Tools"]</td>
<td></td>
</tr>
<tr>
......@@ -33,7 +33,7 @@
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/click_on_panel_link" />
</tal:block>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tal:block tal:define="click_configuration python: {'text': 'Preferences'}">
<tal:block tal:define="click_configuration python: {'text': 'Active Preference'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/click_on_page_link" />
</tal:block>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
......@@ -53,7 +53,7 @@
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tr>
<td>assertElementNotPresent</td>
<td>//ul[@class="ui-list-grid"]/li[2]/h2[text()="Tools"]</td>
<td>//ul[@class="ui-list-grid"]//li/h2[text()="Tools"]</td>
<td></td>
</tr>
</tbody></table>
......
......@@ -27,7 +27,13 @@
<tr>
<td>assertElementPresent</td>
<td>//div[@data-gadget-scope='m']//a[text()='Preferences' and contains(@href, '#!push_history') and contains(@href, 'n.page=preference')]</td>
<td>//div[@data-gadget-scope='m']//a[text()='Active Preference' and contains(@href, '#!push_history') and contains(@href, 'n.page=preference')]</td>
<td></td>
</tr>
<tr>
<td>assertElementPresent</td>
<td>//div[@data-gadget-scope='m']//a[text()='Preferences' and contains(@href, '#!push_history') and contains(@href, 'n.jio_key=portal_preferences')]</td>
<td></td>
</tr>
......
......@@ -39,6 +39,7 @@ if related:
"that_title": context.getTitleOrId() }),)
return module.Base_redirect(
'view', keep_items={'%s__uid' % base_category: relation.getUid(),
'portal_type': portal_type,
'ignore_hide_rows': 1,
'reset': 1,
'portal_status_message': message})
......
from zExceptions import Unauthorized
if REQUEST is not None:
raise Unauthorized
portal = context.getPortalObject()
alarm_tool = portal.portal_alarms
# Higher than simulable movement priority
# priority=3, to be executed after all reindex, but also execute simulation _expand
PRIORITY = 3
if alarm_tool.isSubscribed() and len(alarm_id_list):
# No alarm tool is not subscribed, respect this choice and do not activate any alarm
tag = None
if must_reindex_context:
tag = "%s-%s" % (script.id, context.getRelativeUrl())
context.reindexObject(activate_kw={'tag': tag})
for alarm_id in alarm_id_list:
alarm = alarm_tool.restrictedTraverse(alarm_id)
deduplication_tag = 'Base_reindexAndSenseAlarm_%s' % alarm_id
if alarm.isEnabled():
# do nothing if the alarm is not enabled
if tag is not None:
activate_kw = {}
activate_kw['activity'] = 'SQLQueue'
activate_kw['after_tag'] = tag
activate_kw['tag'] = deduplication_tag
activate_kw['priority'] = max(1, PRIORITY-1)
# Wait for the context indexation to be finished
alarm_tool.activate(**activate_kw).Base_reindexAndSenseAlarm([alarm_id],
must_reindex_context=False)
elif portal.portal_activities.countMessageWithTag(deduplication_tag) <= 1:
if alarm.isActive():
# If the alarm is active, wait for it
# and try to reduce the number of activities
# to reduce the number of alarm execution
activate_kw = {}
activate_kw['activity'] = 'SQLQueue'
activate_kw['priority'] = PRIORITY
activate_kw['tag'] = deduplication_tag
activate_kw['after_path'] = alarm.getPath()
# Wait for the previous alarm run to be finished
# call on alarm tool to gather and drop with sqldict
alarm_tool.activate(**activate_kw).Base_reindexAndSenseAlarm([alarm_id],
must_reindex_context=False)
else:
# activeSense create an activity in SQLDict
alarm.activeSense()
# Prevent 2 nodes to call activateSense concurrently
alarm.serialize()
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</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>alarm_id_list, must_reindex_context=True, REQUEST=None</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Base_reindexAndSenseAlarm</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
from Products.ERP5Type.Utils import getMessageIdWithContext
from Products.ERP5Type.Utils import getMessageIdWithContext, str2unicode, unicode2str
supported_languages = context.Localizer.get_supported_languages()
translated_keys = {} # This dict prevents entering the same key twice
......@@ -34,7 +34,7 @@ for portal_type in portal_type_list:
for lang in supported_languages:
key = (lang, portal_type.getId(), state_var, state_reference)
if key not in translated_keys:
translated_message = context.Localizer.erp5_ui.gettext(state_reference, lang=lang).encode('utf-8')
translated_message = unicode2str(context.Localizer.erp5_ui.gettext(state_reference, lang=lang))
translated_keys[key] = None # mark as translated
object_list.append(dict(language=lang, message_context=state_var, portal_type=portal_type.getId(), original_message=state_reference,
translated_message=translated_message))
......@@ -43,10 +43,10 @@ for portal_type in portal_type_list:
if state.getTitle() is not None and state.getTitle() != '':
state_var_title = '%s_title' % state_var
msg_id = getMessageIdWithContext(state.getTitle(), 'state', wf_id)
translated_message = context.Localizer.erp5_ui.gettext(msg_id, default='', lang=lang).encode('utf-8')
translated_message = unicode2str(context.Localizer.erp5_ui.gettext(msg_id, default='', lang=lang))
if translated_message == '':
msg_id = state.getTitle()
translated_message = context.Localizer.erp5_ui.gettext(state.getTitle().decode('utf-8'), lang=lang).encode('utf-8')
translated_message = unicode2str(context.Localizer.erp5_ui.gettext(str2unicode(state.getTitle()), lang=lang))
key = (lang, portal_type.getId(), state_var_title, state_reference, msg_id)
if key not in translated_keys:
translated_keys[key] = None # mark as translated
......@@ -67,7 +67,7 @@ for ptype in context.portal_types.objectValues():
if key not in translated_keys:
translated_keys[key] = None # mark as translated
object_list.append(dict(language=lang, message_context='portal_type', portal_type=portal_type, original_message=portal_type,
translated_message=context.Localizer.erp5_ui.gettext(portal_type, lang=lang).encode('utf-8')))
translated_message=unicode2str(context.Localizer.erp5_ui.gettext(portal_type, lang=lang))))
if object_list:
catalog_translation_list(object_list)
......
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (C) 2024 Nexedi SA and Contributors.
#
# This program is free software: you can Use, Study, Modify and Redistribute
# it under the terms of the GNU General Public License version 3, or (at your
# option) any later version, as published by the Free Software Foundation.
#
# You can also Link and Combine this program with other software covered by
# the terms of any of the Free Software licenses or any of the Open Source
# Initiative approved licenses and Convey the resulting work. Corresponding
# source of such a combination shall include the source code for all other
# software used.
#
# This program is distributed WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See COPYING file for full licensing terms.
# See https://www.nexedi.com/licensing for rationale and options.
#
##############################################################################
import transaction
from zExceptions import Unauthorized
from DateTime import DateTime
from Products.ERP5Type.tests.utils import createZODBPythonScript
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from time import sleep
class TemporaryAlarmScript(object):
"""
Context manager for temporary alarm python scripts
"""
def __init__(self, portal, script_name, fake_return="", attribute=None):
self.script_name = script_name
self.portal = portal
self.fake_return = fake_return
self.attribute = attribute
def __enter__(self):
if self.script_name in self.portal.portal_skins.custom.objectIds():
raise ValueError('Precondition failed: %s exists in custom' % self.script_name)
if self.attribute is None:
content = """portal_workflow = context.portal_workflow
portal_workflow.doActionFor(context, action='edit_action', comment='Visited by %s')
return %s""" % (self.script_name, self.fake_return)
else:
content = """portal_workflow = context.portal_workflow
context.edit(%s='Visited by %s')
return %s""" % (self.attribute, self.script_name, self.fake_return)
createZODBPythonScript(self.portal.portal_skins.custom,
self.script_name,
'*args, **kwargs',
'# Script body\n' + content)
transaction.commit()
def __exit__(self, exc_type, exc_value, traceback):
if self.script_name in self.portal.portal_skins.custom.objectIds():
self.portal.portal_skins.custom.manage_delObjects(self.script_name)
transaction.commit()
class TestBase_reindexAndSenseAlarm(ERP5TypeTestCase):
def getBusinessTemplateList(self):
"""Business Templates required for this test.
"""
return ('erp5_full_text_mroonga_catalog', 'erp5_simulation_test', )
def afterSetUp(self):
# Ensure the alarms has a workflow history
for alarm_id in ['invoice_builder_alarm',
'packing_list_builder_alarm']:
alarm = self.portal.portal_alarms[alarm_id]
old_comment = alarm.getProperty('comment')
alarm.edit(comment='%s foo' % old_comment)
alarm.edit(comment=old_comment)
return super(TestBase_reindexAndSenseAlarm, self).afterSetUp()
def beforeTearDown(self):
transaction.abort()
def assertLessThan(self, value1, value2):
self.assertTrue(value1 < value2, 'Expected: %s < %s' % (value1, value2))
def getIndexationDate(self, document):
return DateTime(self.portal.portal_catalog(
uid=document.getUid(),
select_list=['indexation_timestamp']
)[0].indexation_timestamp)
def test_reindexAndSenseAlarm_REQUEST_disallowed(self):
document = self.portal.internal_order_module
self.assertRaises(
Unauthorized,
document.Base_reindexAndSenseAlarm,
[],
REQUEST={})
def test_reindexAndSenseAlarm_callAlarmAfterContextReindex(self):
# Check that the alarm is triggered
# only after the context is reindexed
document = self.portal.internal_order_module
alarm = self.portal.portal_alarms.invoice_builder_alarm
document.Base_reindexAndSenseAlarm(['invoice_builder_alarm'])
previous_indexation_timestamp = self.getIndexationDate(document)
workflow_history_count = len(alarm.workflow_history['edit_workflow'])
# Sadly, catalog indexation timestamp has a second precision
# It is needed to wait this 1 second to be able to verify new indexation
sleep(1)
with TemporaryAlarmScript(alarm, 'Alarm_buildInvoice'):
self.tic()
next_indexation_timestamp = self.getIndexationDate(document)
edit_timestamp = alarm.getModificationDate()
# check that the document has been reindexed
self.assertLessThan(previous_indexation_timestamp, next_indexation_timestamp)
# check that alarm was called after the object was reindexed
self.assertLessThan(next_indexation_timestamp, edit_timestamp)
self.assertEqual(
len(alarm.workflow_history['edit_workflow']),
workflow_history_count + 1
)
self.assertEqual(
'Visited by Alarm_buildInvoice',
alarm.workflow_history['edit_workflow'][-1]['comment']
)
def test_reindexAndSenseAlarm_callAlarmWithoutContextReindex(self):
# Check that the alarm is triggered
# without reindexing the context
document = self.portal.internal_order_module
alarm = self.portal.portal_alarms.invoice_builder_alarm
document.Base_reindexAndSenseAlarm(['invoice_builder_alarm'],
must_reindex_context=False)
previous_indexation_timestamp = self.getIndexationDate(document)
workflow_history_count = len(alarm.workflow_history['edit_workflow'])
# Sadly, catalog indexation timestamp has a second precision
# It is needed to wait this 1 second to be able to verify new indexation
sleep(1)
with TemporaryAlarmScript(alarm, 'Alarm_buildInvoice'):
self.tic()
next_indexation_timestamp = self.getIndexationDate(document)
edit_timestamp = alarm.getModificationDate()
# check that the document was not reindexed
self.assertEqual(previous_indexation_timestamp, next_indexation_timestamp)
# check that alarm was called after the object was reindexed
self.assertLessThan(next_indexation_timestamp, edit_timestamp)
self.assertEqual(
len(alarm.workflow_history['edit_workflow']),
workflow_history_count + 1
)
self.assertEqual(
'Visited by Alarm_buildInvoice',
alarm.workflow_history['edit_workflow'][-1]['comment']
)
def test_reindexAndSenseAlarm_doesNotReindexIfNoAlarm(self):
# Check that no alarm is triggered
# and the context is not reindexed
document = self.portal.internal_order_module
alarm = self.portal.portal_alarms.invoice_builder_alarm
document.Base_reindexAndSenseAlarm([])
previous_indexation_timestamp = self.getIndexationDate(document)
workflow_history_count = len(alarm.workflow_history['edit_workflow'])
# Sadly, catalog indexation timestamp has a second precision
# It is needed to wait this 1 second to be able to verify new indexation
sleep(1)
with TemporaryAlarmScript(alarm, 'Alarm_buildInvoice'):
self.tic()
next_indexation_timestamp = self.getIndexationDate(document)
# check that the document was not reindex
self.assertEqual(previous_indexation_timestamp, next_indexation_timestamp)
# check that the alarm was not triggered
self.assertEqual(
len(alarm.workflow_history['edit_workflow']),
workflow_history_count
)
def test_reindexAndSenseAlarm_twiceInTheSameTransaction(self):
# Check that the alarm is triggered only ONCE
# if the script is called twice in a transaction
document = self.portal.internal_order_module
alarm = self.portal.portal_alarms.invoice_builder_alarm
document.Base_reindexAndSenseAlarm(['invoice_builder_alarm'])
document.Base_reindexAndSenseAlarm(['invoice_builder_alarm'])
previous_indexation_timestamp = self.getIndexationDate(document)
workflow_history_count = len(alarm.workflow_history['edit_workflow'])
# Sadly, catalog indexation timestamp has a second precision
# It is needed to wait this 1 second to be able to verify new indexation
sleep(1)
with TemporaryAlarmScript(alarm, 'Alarm_buildInvoice'):
self.tic()
next_indexation_timestamp = self.getIndexationDate(document)
edit_timestamp = alarm.getModificationDate()
# check that the document has been reindexed
self.assertLessThan(previous_indexation_timestamp, next_indexation_timestamp)
# check that alarm was called ONCE after the object was reindexed
self.assertLessThan(next_indexation_timestamp, edit_timestamp)
self.assertEqual(
len(alarm.workflow_history['edit_workflow']),
workflow_history_count + 1
)
self.assertEqual(
'Visited by Alarm_buildInvoice',
alarm.workflow_history['edit_workflow'][-1]['comment']
)
def test_reindexAndSenseAlarm_twiceInTheSameTransactionWithoutReindex(self):
# Check that the alarm is triggered only ONCE
# if the script is called twice in a transaction
document = self.portal.internal_order_module
alarm = self.portal.portal_alarms.invoice_builder_alarm
document.Base_reindexAndSenseAlarm(['invoice_builder_alarm'],
must_reindex_context=False)
document.Base_reindexAndSenseAlarm(['invoice_builder_alarm'],
must_reindex_context=False)
workflow_history_count = len(alarm.workflow_history['edit_workflow'])
with TemporaryAlarmScript(alarm, 'Alarm_buildInvoice'):
self.tic()
# check that alarm was called ONCE
self.assertEqual(
len(alarm.workflow_history['edit_workflow']),
workflow_history_count + 1
)
self.assertEqual(
'Visited by Alarm_buildInvoice',
alarm.workflow_history['edit_workflow'][-1]['comment']
)
def test_reindexAndSenseAlarm_twiceInTheTwoTransactions(self):
# Check that the alarm is triggered only ONCE
# if the script is called twice in a transaction
document = self.portal.internal_order_module
alarm = self.portal.portal_alarms.invoice_builder_alarm
document.Base_reindexAndSenseAlarm(['invoice_builder_alarm'])
transaction.commit()
document.Base_reindexAndSenseAlarm(['invoice_builder_alarm'])
previous_indexation_timestamp = self.getIndexationDate(document)
workflow_history_count = len(alarm.workflow_history['edit_workflow'])
# Sadly, catalog indexation timestamp has a second precision
# It is needed to wait this 1 second to be able to verify new indexation
sleep(1)
with TemporaryAlarmScript(alarm, 'Alarm_buildInvoice'):
self.tic()
next_indexation_timestamp = self.getIndexationDate(document)
edit_timestamp = alarm.getModificationDate()
# check that the document has been reindexed
self.assertLessThan(previous_indexation_timestamp, next_indexation_timestamp)
# check that alarm was called ONCE after the object was reindexed
self.assertLessThan(next_indexation_timestamp, edit_timestamp)
self.assertEqual(
len(alarm.workflow_history['edit_workflow']),
workflow_history_count + 1
)
self.assertEqual(
'Visited by Alarm_buildInvoice',
alarm.workflow_history['edit_workflow'][-1]['comment']
)
def test_reindexAndSenseAlarm_alarmActive(self):
# Check that the script wait for the alarm to be not activate
# before triggering it again
document = self.portal.internal_order_module
alarm = self.portal.portal_alarms.invoice_builder_alarm
tag = 'foobar'
alarm.activate(tag=tag).getId()
# Call edit, to ensure the last edit contains the comment value
alarm.activate(after_tag=tag, tag=tag+'1').edit(description=alarm.getDescription() + ' ')
alarm.activate(after_tag=tag+'1').edit(description=alarm.getDescription()[:-1])
transaction.commit()
document.Base_reindexAndSenseAlarm(['invoice_builder_alarm'],
must_reindex_context=False)
workflow_history_count = len(alarm.workflow_history['edit_workflow'])
with TemporaryAlarmScript(alarm, 'Alarm_buildInvoice'):
self.tic()
self.assertEqual(
len(alarm.workflow_history['edit_workflow']),
workflow_history_count + 3
)
self.assertEqual(
'Visited by Alarm_buildInvoice',
alarm.workflow_history['edit_workflow'][-1]['comment']
)
def test_reindexAndSenseAlarm_twoContextSameTransaction(self):
# Check that the script wait for the alarm to be not activate
# before triggering it again
document1 = self.portal.internal_order_module
document2 = self.portal.internal_packing_list_module
alarm = self.portal.portal_alarms.invoice_builder_alarm
document1.Base_reindexAndSenseAlarm(['invoice_builder_alarm'])
document2.Base_reindexAndSenseAlarm(['invoice_builder_alarm'])
previous_indexation_timestamp1 = self.getIndexationDate(document1)
previous_indexation_timestamp2 = self.getIndexationDate(document2)
workflow_history_count = len(alarm.workflow_history['edit_workflow'])
# Sadly, catalog indexation timestamp has a second precision
# It is needed to wait this 1 second to be able to verify new indexation
sleep(1)
with TemporaryAlarmScript(alarm, 'Alarm_buildInvoice'):
self.tic()
next_indexation_timestamp1 = self.getIndexationDate(document1)
next_indexation_timestamp2 = self.getIndexationDate(document2)
edit_timestamp = alarm.getModificationDate()
# check that the document has been reindexed
self.assertLessThan(previous_indexation_timestamp1, next_indexation_timestamp1)
self.assertLessThan(previous_indexation_timestamp2, next_indexation_timestamp2)
# check that alarm was called after the object was reindexed
self.assertLessThan(next_indexation_timestamp1, edit_timestamp)
self.assertLessThan(next_indexation_timestamp2, edit_timestamp)
self.assertEqual(
len(alarm.workflow_history['edit_workflow']),
workflow_history_count + 1
)
self.assertEqual(
'Visited by Alarm_buildInvoice',
alarm.workflow_history['edit_workflow'][-1]['comment']
)
def test_reindexAndSenseAlarm_twoContextDifferentTransaction(self):
# Check that the script wait for the alarm to be not activate
# before triggering it again
document1 = self.portal.internal_order_module
document2 = self.portal.internal_packing_list_module
alarm = self.portal.portal_alarms.invoice_builder_alarm
document1.Base_reindexAndSenseAlarm(['invoice_builder_alarm'])
document2.Base_reindexAndSenseAlarm(['invoice_builder_alarm'])
previous_indexation_timestamp1 = self.getIndexationDate(document1)
transaction.commit()
previous_indexation_timestamp2 = self.getIndexationDate(document2)
workflow_history_count = len(alarm.workflow_history['edit_workflow'])
# Sadly, catalog indexation timestamp has a second precision
# It is needed to wait this 1 second to be able to verify new indexation
sleep(1)
with TemporaryAlarmScript(alarm, 'Alarm_buildInvoice'):
self.tic()
next_indexation_timestamp1 = self.getIndexationDate(document1)
next_indexation_timestamp2 = self.getIndexationDate(document2)
edit_timestamp = alarm.getModificationDate()
# check that the document has been reindexed
self.assertLessThan(previous_indexation_timestamp1, next_indexation_timestamp1)
self.assertLessThan(previous_indexation_timestamp2, next_indexation_timestamp2)
# check that alarm was called after the object was reindexed
self.assertLessThan(next_indexation_timestamp1, edit_timestamp)
self.assertLessThan(next_indexation_timestamp2, edit_timestamp)
self.assertEqual(
len(alarm.workflow_history['edit_workflow']),
workflow_history_count + 1
)
self.assertEqual(
'Visited by Alarm_buildInvoice',
alarm.workflow_history['edit_workflow'][-1]['comment']
)
def test_reindexAndSenseAlarm_twoAlarm(self):
# Check that the script wait for the alarm to be not activate
# before triggering it again
document = self.portal.internal_order_module
alarm1 = self.portal.portal_alarms.invoice_builder_alarm
alarm2 = self.portal.portal_alarms.packing_list_builder_alarm
document.Base_reindexAndSenseAlarm(['invoice_builder_alarm',
'packing_list_builder_alarm'])
previous_indexation_timestamp = self.getIndexationDate(document)
workflow_history_count1 = len(alarm1.workflow_history['edit_workflow'])
workflow_history_count2 = len(alarm2.workflow_history['edit_workflow'])
# Sadly, catalog indexation timestamp has a second precision
# It is needed to wait this 1 second to be able to verify new indexation
sleep(1)
with TemporaryAlarmScript(alarm1, 'Alarm_buildInvoice'):
with TemporaryAlarmScript(alarm2, 'Alarm_buildPackingList'):
self.tic()
next_indexation_timestamp = self.getIndexationDate(document)
edit_timestamp1 = alarm1.getModificationDate()
edit_timestamp2 = alarm2.getModificationDate()
self.assertLessThan(previous_indexation_timestamp, next_indexation_timestamp)
# check that alarm was called after the object was reindexed
self.assertLessThan(next_indexation_timestamp, edit_timestamp1)
self.assertLessThan(next_indexation_timestamp, edit_timestamp2)
self.assertEqual(
len(alarm1.workflow_history['edit_workflow']),
workflow_history_count1 + 1
)
self.assertEqual(
'Visited by Alarm_buildInvoice',
alarm1.workflow_history['edit_workflow'][-1]['comment']
)
self.assertEqual(
len(alarm2.workflow_history['edit_workflow']),
workflow_history_count2 + 1
)
self.assertEqual(
'Visited by Alarm_buildPackingList',
alarm2.workflow_history['edit_workflow'][-1]['comment']
)
<?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>default_reference</string> </key>
<value> <string>testERP5CoreSkins</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>test.erp5.testERP5CoreSkins</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">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
test.erp5.testERP5CoreSkins
\ No newline at end of file
erp5_full_text_mroonga_catalog
erp5_simulation_test
\ No newline at end of file
......@@ -8,9 +8,7 @@
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value>
<none/>
</value>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
......
......@@ -8,9 +8,7 @@
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value>
<none/>
</value>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>jquery.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>bender-runner.config.json</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>build-config.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>ckeditor.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>config.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>contents.css</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>af.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>ar.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>az.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>bg.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>bn.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>bs.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>ca.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>cs.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>cy.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>da.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>de-ch.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>de.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>el.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>en-au.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>en-ca.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>en-gb.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>en.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>eo.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>es-mx.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>es.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>et.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>eu.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>fa.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>fi.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>fo.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>fr-ca.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>fr.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>gl.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>gu.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>he.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>hi.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>hr.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>hu.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>id.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>is.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>it.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>ja.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>ka.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>km.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>ko.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>ku.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>lt.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>lv.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>mk.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>mn.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>ms.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>nb.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>nl.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>no.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>oc.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>pl.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>pt-br.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>pt.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>ro.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>ru.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>si.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>sk.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>sl.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>sq.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>sr-latn.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>sr.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>sv.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>th.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>tr.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>tt.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>ug.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>uk.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>vi.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>zh-cn.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>zh.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>a11yhelp.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>af.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>ar.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>az.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>bg.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>ca.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>cs.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>cy.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>da.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>de-ch.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>de.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>el.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>en-au.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>en-gb.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>en.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>eo.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>es-mx.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>es.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>et.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>eu.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>fa.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>fi.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>fo.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>fr-ca.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>fr.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>gl.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>gu.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>he.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>hi.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>hr.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>hu.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>id.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>it.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>ja.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>km.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>ko.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>ku.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>lt.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>lv.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>mk.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>mn.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>nb.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>nl.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>no.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>oc.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>pl.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>pt-br.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>pt.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>ro.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>ru.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>si.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>sk.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>sl.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>sq.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>sr-latn.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>sr.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>sv.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>th.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>tr.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>tt.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>ug.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>uk.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>vi.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>zh-cn.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>zh.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>about.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>logo_ckeditor.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>logo_ckeditor.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>paste.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>colordialog.css</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>colordialog.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>cursor-disabled.svg</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>cursor.svg</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>copyformatting.css</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>dialogDefinition.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>dialog.css</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>div.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>find.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>button.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>checkbox.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>form.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>hiddenfield.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>radio.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>select.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>textarea.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>textfield.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>hiddenfield.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>icons.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>icons_hidpi.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>iframe.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>placeholder.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>image.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>noimage.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>anchor.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>link.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>anchor.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>anchor.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>liststyle.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>icon-rtl.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>icon.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>icon-rtl.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>icon.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>pagebreak.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>default.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>common.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>image.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>pagebreak.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>preview.html</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>screen.css</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>dialog.css</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>options.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>toolbar.css</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>scayt.css</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>block_address.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>block_blockquote.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>block_div.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>block_h1.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>block_h2.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>block_h3.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>block_h4.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>block_h5.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>block_h6.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>block_p.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>block_pre.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>smiley.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>angel_smile.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>angel_smile.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>angry_smile.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>angry_smile.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>broken_heart.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>broken_heart.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>confused_smile.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>confused_smile.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>cry_smile.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>cry_smile.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>devil_smile.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>devil_smile.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>embaressed_smile.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>embarrassed_smile.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>embarrassed_smile.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>envelope.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>envelope.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>heart.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>heart.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>kiss.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>kiss.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>lightbulb.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>lightbulb.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>omg_smile.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>omg_smile.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>regular_smile.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>regular_smile.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>sad_smile.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>sad_smile.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>shades_smile.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>shades_smile.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>teeth_smile.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>teeth_smile.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>thumbs_down.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>thumbs_down.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>thumbs_up.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>thumbs_up.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>tongue_smile.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>tongue_smile.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>tounge_smile.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>whatchutalkingabout_smile.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>whatchutalkingabout_smile.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>wink_smile.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>wink_smile.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>af.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>ar.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>az.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>bg.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>ca.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>cs.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>cy.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>da.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>de-ch.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>de.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>el.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>en-au.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>en-ca.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>en-gb.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>en.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>eo.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>es-mx.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>es.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>et.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>eu.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>fa.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>fi.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>fr-ca.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>fr.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>gl.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>he.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>hr.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>hu.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>id.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>it.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>ja.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>km.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>ko.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>ku.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>lt.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>lv.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>nb.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>nl.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>no.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>oc.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>pl.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>pt-br.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>pt.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>ro.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>ru.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>si.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>sk.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>sl.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>sq.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>sr-latn.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>sr.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>sv.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>th.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>tr.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>tt.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>ug.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>uk.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>vi.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>zh-cn.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>zh.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>specialchar.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>table.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>tableselection.css</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>tableCell.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>templates.css</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>templates.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>templatedefinition.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>default.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>template1.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>template2.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>template3.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>dialog.css</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>dialog_ie.css</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>dialog_ie8.css</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>dialog_iequirks.css</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>editor.css</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>editor_gecko.css</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>editor_ie.css</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>editor_ie8.css</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>editor_iequirks.css</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>icons.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>icons_hidpi.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>arrow.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>close.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>close.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>lock-open.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>lock.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>refresh.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>lock-open.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>lock.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>refresh.png</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>spinner.gif</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>styles.js</string> </value>
......
......@@ -6,6 +6,10 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>promise.js</string> </value>
......
......@@ -41,6 +41,7 @@ from AccessControl import getSecurityManager
from Testing import ZopeTestCase
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from Products.ERP5Type.tests.utils import addUserToDeveloperRole, findContentChain
from Products.ERP5Form.ProxyField import BrokenProxyField
from Products.CMFCore.utils import getToolByName
# You can invoke same tests in your favourite collection of business templates
# by using TestXHTMLMixin like the following :
......@@ -106,8 +107,17 @@ class TestXHTMLMixin(ERP5TypeTestCase):
for field_path, field in skins_tool[skin_folder_id].ZopeFind(
skins_tool[skin_folder_id],
obj_metatypes=['ProxyField'], search_sub=1):
try:
template_field = field.getTemplateField(cache=False)
except BrokenProxyField:
template_field = None
if template_field is None:
# check if the field parent form can be used by this skin_name
field_form_id = field_path.split('/')[0]
if not skins_tool.isFirstInSkin('%s/%s' % (skin_folder_id, field_form_id), skin=skin_name):
# The problematic form will not be used by this skin selection
# as another object with the same name exists
continue
# Base_viewRelatedObjectList (used for proxy listbox ids on
# relation fields) is an exception, the proxy field has no target
# by default.
......
......@@ -432,7 +432,9 @@ class ProxyField(ZMIField):
else:
proxy_form = getattr(object, form_id, None)
if (proxy_form is not None):
if (proxy_form is not None) and (proxy_form.meta_type == 'ERP5 Form'):
# If the proxy_form is not an ERP5 Form, but a Page Template,
# accessing _getOb method fails
field_id = self.get_value('field_id')
proxy_field = proxy_form._getOb(field_id, None)
if proxy_field is None:
......
......@@ -35,54 +35,22 @@ from Products.PluggableAuthService.utils import classImplements
from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin
from Products.ERP5Security import _setUserNameForAccessLog
from AccessControl.SecurityManagement import getSecurityManager, \
setSecurityManager, newSecurityManager
from Products.ERP5Type.Cache import DEFAULT_CACHE_SCOPE
import time
from six.moves import urllib
import json
from zLOG import LOG, ERROR, INFO
from zLOG import LOG, INFO
try:
import facebook
except ImportError:
facebook = None
try:
import apiclient.discovery
import httplib2
import oauth2client.client
except ImportError:
httplib2 = None
#Form for new plugin in ZMI
# Form for new plugin in ZMI
manage_addERP5FacebookExtractionPluginForm = PageTemplateFile(
'www/ERP5Security_addERP5FacebookExtractionPlugin', globals(),
__name__='manage_addERP5FacebookExtractionPluginForm')
def getGoogleUserEntry(token):
if httplib2 is None:
LOG('ERP5GoogleExtractionPlugin', INFO,
'No Google modules available, please install google-api-python-client '
'package. Authentication disabled..')
return None
http = oauth2client.client.AccessTokenCredentials(token,
'ERP5 Client'
).authorize(httplib2.Http(timeout=5))
service = apiclient.discovery.build("oauth2", "v1", http=http)
google_entry = service.userinfo().get().execute()
user_entry = {}
if google_entry is not None:
# sanitise value
for k in (('first_name', 'given_name'),
('last_name', 'family_name'),
('email', 'email'),
('reference', 'email'),):
value = google_entry.get(k[1], '').encode('utf-8')
user_entry[k[0]] = value
return user_entry
def addERP5FacebookExtractionPlugin(dispatcher, id, title=None, REQUEST=None):
""" Add a ERP5FacebookExtractionPlugin to a Pluggable Auth Service. """
......@@ -97,7 +65,7 @@ def addERP5FacebookExtractionPlugin(dispatcher, id, title=None, REQUEST=None):
'ERP5FacebookExtractionPlugin+added.'
% dispatcher.absolute_url())
#Form for new plugin in ZMI
# Form for new plugin in ZMI
manage_addERP5GoogleExtractionPluginForm = PageTemplateFile(
'www/ERP5Security_addERP5GoogleExtractionPlugin', globals(),
__name__='manage_addERP5GoogleExtractionPluginForm')
......@@ -156,12 +124,12 @@ class ERP5ExternalOauth2ExtractionPlugin:
for cache_plugin in cache_factory.getCachePluginList():
cache_entry = cache_plugin.get(key, DEFAULT_CACHE_SCOPE)
if cache_entry is not None:
# Avoid errors if the plugin don't have the funcionality of refresh token
refreshTokenIfExpired = getattr(self, "refreshTokenIfExpired", None)
cache_value = cache_entry.getValue()
if refreshTokenIfExpired is not None:
return refreshTokenIfExpired(key, cache_value)
else:
# getToken is called for the access_token_dict and for
# the user entry. We try to refresh only for the
# access_token_dict
if 'refresh_token' in cache_value:
return self.refreshTokenIfExpired(key, cache_value)
return cache_value
raise KeyError('Key %r not found' % key)
......@@ -170,19 +138,29 @@ class ERP5ExternalOauth2ExtractionPlugin:
####################################
security.declarePrivate('extractCredentials')
def extractCredentials(self, request):
""" Extract Oauth2 credentials from the request header. """
user_dict = {}
""" Extract Oauth2 credentials from cookie.
This plugins uses two level of cache storage:
- cookie_value => access_token_dict
- access_token => user_entry
access_token_dict depends on the concrete plugin classes,
but this is generally access_token and refresh_token.
user_entry must contain "reference", which is the reference
of the corresponding login document in ERP5.
"""
access_token_dict = {}
cookie_hash = request.get(self.cookie_name)
if cookie_hash is not None:
try:
user_dict = self.getToken(cookie_hash)
access_token_dict = self.getToken(cookie_hash)
except KeyError:
LOG(self.getId(), INFO, 'Hash %s not found' % cookie_hash)
return {}
token = None
if "access_token" in user_dict:
token = user_dict["access_token"]
if "access_token" in access_token_dict:
token = access_token_dict["access_token"]
if token is None:
# no token, then no credentials
......@@ -192,7 +170,7 @@ class ERP5ExternalOauth2ExtractionPlugin:
try:
user_entry = self.getToken(token)
except KeyError:
user_entry = self.getUserEntry(token)
user_entry = self.getUserEntry(access_token_dict)
if user_entry is not None:
# Reduce data size because, we don't need more than reference
user_entry = {"reference": user_entry["reference"]}
......@@ -240,11 +218,14 @@ def getFacebookUserEntry(token):
if facebook_entry is not None:
# sanitise value
for k in ('name', 'id'):
v = facebook_entry[k]
if six.PY2:
v = v.encode('utf-8')
try:
if k == 'id':
user_entry['reference'] = facebook_entry[k].encode('utf-8')
user_entry['reference'] = v
else:
user_entry[k] = facebook_entry[k].encode('utf-8')
user_entry[k] = v
except KeyError:
raise ValueError(facebook_entry)
return user_entry
......@@ -259,40 +240,36 @@ class ERP5FacebookExtractionPlugin(ERP5ExternalOauth2ExtractionPlugin, BasePlugi
cookie_name = "__ac_facebook_hash"
cache_factory_name = "facebook_server_auth_token_cache_factory"
def refreshTokenIfExpired(self, key, cache_value):
return cache_value
def refreshTokenIfExpired(self, key, access_token_dict):
return access_token_dict
def getUserEntry(self, token):
return getFacebookUserDict(token)
class ERP5GoogleExtractionPlugin(ERP5ExternalOauth2ExtractionPlugin, BasePlugin):
"""
Plugin to authenicate as machines.
Plugin to authenticate using google OAuth2.
"""
meta_type = "ERP5 Google Extraction Plugin"
login_portal_type = "Google Login"
cookie_name = "__ac_google_hash"
cache_factory_name = "google_server_auth_token_cache_factory"
def refreshTokenIfExpired(self, key, cache_value):
expires_in = cache_value.get("token_response", {}).get("expires_in")
refresh_token = cache_value.get("refresh_token")
if expires_in and refresh_token:
if (time.time() - cache_value["response_timestamp"]) >= float(expires_in):
credential = oauth2client.client.OAuth2Credentials(
cache_value["access_token"], cache_value["client_id"],
cache_value["client_secret"], refresh_token,
cache_value["token_expiry"], cache_value["token_uri"],
cache_value["user_agent"])
credential.refresh(httplib2.Http(timeout=5))
cache_value = json.loads(credential.to_json())
cache_value["response_timestamp"] = time.time()
self.setToken(key, cache_value)
return cache_value
def refreshTokenIfExpired(self, key, access_token_dict):
if (time.time() - access_token_dict["response_timestamp"]) \
>= access_token_dict['expires_in']:
access_token_dict = self.getPortalObject().unrestrictedTraverse(
access_token_dict['connector_relative_url']
).refreshToken(access_token_dict)
self.setToken(key, access_token_dict)
return access_token_dict
def getUserEntry(self, access_token_dict):
return self.getPortalObject().unrestrictedTraverse(
access_token_dict['connector_relative_url'],
).getUserEntry(access_token_dict['access_token'])
def getUserEntry(self, token):
return getGoogleUserEntry(token)
#List implementation of class
classImplements( ERP5FacebookExtractionPlugin,
......
......@@ -106,15 +106,11 @@ def message_decode(message):
To be used in the user interface, to avoid problems with the
encodings, HTML entities, etc..
"""
message = decodebytes(message)
message = decodebytes(message.encode())
encoding = HTTPRequest.default_encoding
return six.text_type(message, encoding)
def filter_sort(x, y):
return cmp(to_unicode(x), to_unicode(y))
def get_url(url, batch_start, batch_size, regex, lang, empty, **kw):
params = []
for key, value in six.iteritems(kw):
......@@ -396,7 +392,7 @@ class MessageCatalog(LanguageManager, ObjectManager, SimpleItem):
for m, t in six.iteritems(self._messages):
if query.search(m) and (not empty or not t.get(lang, '').strip()):
messages.append(m)
messages.sort(filter_sort)
messages.sort(key=lambda m: to_unicode(m))
# How many messages
n = len(messages)
namespace['n_messages'] = n
......@@ -664,6 +660,8 @@ class MessageCatalog(LanguageManager, ObjectManager, SimpleItem):
messages = self._messages
# Load the data
if isinstance(data, bytes):
data = data.decode("utf-8")
po = polib.pofile(data)
encoding = to_str(po.encoding)
for entry in po:
......@@ -688,8 +686,10 @@ class MessageCatalog(LanguageManager, ObjectManager, SimpleItem):
def manage_import(self, lang, file, REQUEST=None, RESPONSE=None):
""" """
# XXX For backwards compatibility only, use "po_import" instead.
if isinstance(file, str):
if isinstance(file, str): # six.PY2
content = file
elif isinstance(file, bytes): # six.PY3
content = file.decode()
else:
content = file.read()
......
......@@ -23,9 +23,7 @@ from App.ImageFile import ImageFile
from DocumentTemplate.DT_String import String
# Import from Localizer
import six
if six.PY2:
from . import patches as _
from . import patches as _
from . import Localizer, MessageCatalog
from .LocalFiles import LocalDTMLFile
......
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