From 79e5b5fc6c3f17e603ea1ca6ed728ec5d6445a78 Mon Sep 17 00:00:00 2001 From: Ivan Tyagov <ivan@nexedi.com> Date: Wed, 19 Oct 2011 10:48:57 +0300 Subject: [PATCH] Add pre_converted_only argument on format REQUEST arguments. If pre_converted_only is True system will return desired conversion only if it's already cached. Extend testDms to cover it. --- .../Document_getFailsafeConversion.xml | 85 ++++++++++++++++ .../default_conversion_failure_image.xml | 96 +++++++++++++++++++ bt5/erp5_dms/bt/revision | 2 +- product/ERP5/mixin/document.py | 23 ++++- product/ERP5/mixin/downloadable.py | 2 +- product/ERP5OOo/tests/testDms.py | 50 +++++++++- 6 files changed, 250 insertions(+), 8 deletions(-) create mode 100644 bt5/erp5_dms/SkinTemplateItem/portal_skins/erp5_dms/Document_getFailsafeConversion.xml create mode 100644 bt5/erp5_dms/SkinTemplateItem/portal_skins/erp5_dms/default_conversion_failure_image.xml diff --git a/bt5/erp5_dms/SkinTemplateItem/portal_skins/erp5_dms/Document_getFailsafeConversion.xml b/bt5/erp5_dms/SkinTemplateItem/portal_skins/erp5_dms/Document_getFailsafeConversion.xml new file mode 100644 index 0000000000..bfdf1f184f --- /dev/null +++ b/bt5/erp5_dms/SkinTemplateItem/portal_skins/erp5_dms/Document_getFailsafeConversion.xml @@ -0,0 +1,85 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <global name="PythonScript" module="Products.PythonScripts.PythonScript"/> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>Script_magic</string> </key> + <value> <int>3</int> </value> + </item> + <item> + <key> <string>_bind_names</string> </key> + <value> + <object> + <klass> + <global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/> + </klass> + <tuple/> + <state> + <dictionary> + <item> + <key> <string>_asgns</string> </key> + <value> + <dictionary> + <item> + <key> <string>name_container</string> </key> + <value> <string>container</string> </value> + </item> + <item> + <key> <string>name_context</string> </key> + <value> <string>context</string> </value> + </item> + <item> + <key> <string>name_m_self</string> </key> + <value> <string>script</string> </value> + </item> + <item> + <key> <string>name_subpath</string> </key> + <value> <string>traverse_subpath</string> </value> + </item> + </dictionary> + </value> + </item> + </dictionary> + </state> + </object> + </value> + </item> + <item> + <key> <string>_body</string> </key> + <value> <string>"""\n + Generic method to handle conversion failures ans still return something to use \n + to explain what when wrong, etc.\n +"""\n +VALID_IMAGE_FORMAT_LIST = (\'jpg\', \'jpeg\', \'png\', \'gif\', \'pnm\', \'ppm\', \'tiff\')\n +\n +# some good defaults\n +mimetype = "text/plain"\n +data = "Conversion failure"\n +\n +if format in VALID_IMAGE_FORMAT_LIST:\n + # default image is an OFSImage so even if conversion engine is down \n + # we are still able to deliver it\n + default_image = getattr(context, "default_conversion_failure_image", None)\n + if default_image is not None:\n + mimetype = default_image.getContentType()\n + data = default_image.index_html(context.REQUEST, context.REQUEST.RESPONSE)\n +\n +return mimetype, data\n +</string> </value> + </item> + <item> + <key> <string>_params</string> </key> + <value> <string>format=None, **kw</string> </value> + </item> + <item> + <key> <string>id</string> </key> + <value> <string>Document_getFailsafeConversion</string> </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/bt5/erp5_dms/SkinTemplateItem/portal_skins/erp5_dms/default_conversion_failure_image.xml b/bt5/erp5_dms/SkinTemplateItem/portal_skins/erp5_dms/default_conversion_failure_image.xml new file mode 100644 index 0000000000..749bd126bf --- /dev/null +++ b/bt5/erp5_dms/SkinTemplateItem/portal_skins/erp5_dms/default_conversion_failure_image.xml @@ -0,0 +1,96 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <global name="Image" module="OFS.Image"/> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>_Cacheable__manager_id</string> </key> + <value> + <none/> + </value> + </item> + <item> + <key> <string>_EtagSupport__etag</string> </key> + <value> <string>ts18922384.14</string> </value> + </item> + <item> + <key> <string>__name__</string> </key> + <value> <string>default_conversion_failure_image</string> </value> + </item> + <item> + <key> <string>content_type</string> </key> + <value> <string>image/png</string> </value> + </item> + <item> + <key> <string>data</string> </key> + <value> <string encoding="base64">iVBORw0KGgoAAAANSUhEUgAAAPAAAAC0CAYAAACqnKHoAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A +/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sKEgcTF+P+EykAAAAdaVRYdENv +bW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAACNxJREFUeNrt2llIlG0fx/Gf2t4Yxii5 +RDRlmRKRbQcxeFIk2UEFFdVZBy1GoZlDBxVhUZS02J5kexEdRAQFBVkaeVY5ErRHREJhktpCi+b/ +OXkVdUabxffRnr4fmAPvub3mmmvu79yzRZiZCcAfKZIlAAgYAAEDIGCAgAEQMAACBkDAAAEDIGAA +BAwQMAACBkDAAAgYIGAABAyAgAEQMEDAAAgYAAEDBAyAgAEQMAACBggYAAEDIGAABAwQMAACBkDA +AAEDIGAABAyAgAECBkDAAAgYIGAABAyAgAEQMEDAAAgYAAEDIGCAgAEQMAACBggYAAEDIGAABAwQ +MAACBkDAAAgYIGAABAyAgAECBkDAAAgYAAEDBAyAgAEQMAACBgj4TxQREaGIiAiOgF6y9uE+HsH8 +/3/psY/sLQ+mx+MhNCDYfszMejpgSerbt68ePXqklJQUv9f38DTxLx0HoT7Owfz/f+mY6jUvoRsb +G5WTk8ORDPyJAc+ePVs3b97U1atXf7uvmenYsWPKyMhQXFyc4uLilJGRoePHjwf8rNrV+7DGxkbl +5ubK5XJpypQpOnv2rCTp8+fPysvL0+jRozVy5Ejt3LnT79j37t3T/PnzlZiYqNjYWM2cOVMPHz70 +u29NTY0WL16s+Ph4paamasOGDWpqaur0rcONGzeUlZUll8ul6OhoTZ48WQUFBfrx40dA9zuQudXW +1mrAgAHq06eP3r9/7zPG169fFRMTo2HDhqmxsTHo+xzo26JgxmzR1NSkDRs2KDU1VQkJCVq6dKlq +a2sDWptw17ZHWA+TZJLsyZMn1qdPH3O5XPbt2zef61s0NzdbZmZm6/aOl6ysLGtubg74dv1tW7ly +Zbsxo6KizOv12oIFC3xu7/r1652O3fbSv39/e/z4cbv9GhoabMSIET77rl692u/8Vq1a1en9Tk9P +tx8/fgR8v383t8WLF5sk27t3r88YJSUlJsny8/ODHvd3ax/KXNvu23btWi4ul8s+f/7c5W11x9r2 +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</string> </value> + </item> + <item> + <key> <string>height</string> </key> + <value> <int>180</int> </value> + </item> + <item> + <key> <string>precondition</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>size</string> </key> + <value> <int>2437</int> </value> + </item> + <item> + <key> <string>title</string> </key> + <value> <string>Default Image returned if conversion failed</string> </value> + </item> + <item> + <key> <string>width</string> </key> + <value> <int>240</int> </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/bt5/erp5_dms/bt/revision b/bt5/erp5_dms/bt/revision index c789257680..b8d1607c2b 100644 --- a/bt5/erp5_dms/bt/revision +++ b/bt5/erp5_dms/bt/revision @@ -1 +1 @@ -1254 \ No newline at end of file +1255 \ No newline at end of file diff --git a/product/ERP5/mixin/document.py b/product/ERP5/mixin/document.py index b49740ed99..036ac58261 100644 --- a/product/ERP5/mixin/document.py +++ b/product/ERP5/mixin/document.py @@ -67,10 +67,31 @@ class DocumentMixin: # to bypass such check one should use _convert directly del transaction_variable[LOCK_PERMISSION_KEY] self._checkConversionFormatPermission(format, lock_checking=True, **kw) - result = self._convert(format, **kw) + + pre_converted_only = kw.pop('pre_converted_only', False) + if pre_converted_only: + # we will use ONLY cache to return converted content + # if respective document is not in cache we will return a good default content + try: + kw['format'] = format + result = self.getConversion(**kw) + except KeyError: + # respective document is not cached yet and we should return a failure safe content instead + result = self.getFailsafeConversion(**kw) + else: + # generate conversion on the fly or get it from cache (if already stored) + result = self._convert(format, **kw) if LOCK_PERMISSION_KEY in transaction_variable: del transaction_variable[LOCK_PERMISSION_KEY] return result + + def getFailsafeConversion(self, **kw): + """ + Return a failure resistent conversion of a document + """ + method = self._getTypeBasedMethod('getFailsafeConversion', + fallback_script_id='Document_getFailsafeConversion') + return method(**kw) def _convert(self, format, **kw): """Private method which make the transformation. diff --git a/product/ERP5/mixin/downloadable.py b/product/ERP5/mixin/downloadable.py index 30fe589d09..bf3947697b 100644 --- a/product/ERP5/mixin/downloadable.py +++ b/product/ERP5/mixin/downloadable.py @@ -41,7 +41,7 @@ class DownloadableMixin: ### Content processing methods security.declareProtected(Permissions.View, 'index_html') - @fill_args_from_request('display', 'quality', 'resolution', 'frame') + @fill_args_from_request('display', 'quality', 'resolution', 'frame', 'pre_converted_only') def index_html(self, REQUEST, RESPONSE, format=_MARKER, **kw): """ We follow here the standard Zope API for files and images diff --git a/product/ERP5OOo/tests/testDms.py b/product/ERP5OOo/tests/testDms.py index ac07847aa6..dac2c79ab9 100644 --- a/product/ERP5OOo/tests/testDms.py +++ b/product/ERP5OOo/tests/testDms.py @@ -93,9 +93,13 @@ def makeFileUpload(name, as_name=None): class TestDocumentMixin(ERP5TypeTestCase): bussiness_template_list = ['erp5_core_proxy_field_legacy', - 'erp5_full_text_myisam_catalog','erp5_base', - 'erp5_ingestion', 'erp5_ingestion_mysql_innodb_catalog', - 'erp5_web', 'erp5_dms'] + 'erp5_jquery', + 'erp5_full_text_myisam_catalog', + 'erp5_base', + 'erp5_ingestion_mysql_innodb_catalog', + 'erp5_ingestion', + 'erp5_web', + 'erp5_dms'] def setUpOnce(self): # set a dummy localizer (because normally it is cookie based) @@ -131,8 +135,8 @@ class TestDocumentMixin(ERP5TypeTestCase): preference_list = self.portal.portal_preferences.contentValues( portal_type=portal_type) if not preference_list: - preference = self.portal.portal_preferences.newContent( - portal_type=portal_type) + preference = self.portal.portal_preferences.newContent(title="Default System Preference", + portal_type=portal_type) else: preference = preference_list[0] if self.portal.portal_workflow.isTransitionPossible(preference, 'enable'): @@ -2131,6 +2135,42 @@ return 1 # if PDF size is larger than A4 format system should deny conversion self.assertRaises(Unauthorized, pdf.convert, format='jpeg') + + def test_preConversionOnly(self): + """ + Test usage of pre_converted_only argument - i.e. return a conversion only form cache otherwise + return a default (i.e. indicating a conversion failures) + """ + doc = self.portal.document_module.newContent(portal_type='Presentation') + upload_file = makeFileUpload('TEST-en-003.odp') + doc.edit(file=upload_file) + doc.publish() + self.stepTic() + + default_conversion_failure_image_size, default_conversion_failure_image_file_size = \ + self.getURLSizeList('%s/default_conversion_failure_image' %self.portal.absolute_url()) + + doc_url = '%s/%s' %(self.portal.absolute_url(), doc.getPath()) + converted_image_size_70, converted_file_size_70 = self.getURLSizeList(doc_url, \ + **{'format':'png', 'quality':70.0}) + self.assertTrue(doc.hasConversion(**{'format': 'png', 'quality': 70.0})) + + # try with new quality and pre_converted_only now a default image + # with content "No image available" should be returned + failure_image_size, failure_file_size = self.getURLSizeList(doc_url, \ + **{'format':'png', 'quality':80.0, 'pre_converted_only':1}) + self.assertSameSet(failure_image_size, default_conversion_failure_image_size) + + + converted_image_size_80, converted_file_size_80 = self.getURLSizeList(doc_url, \ + **{'format':'png', 'quality':80.0}) + self.assertSameSet(converted_image_size_80, converted_image_size_70) + self.assertTrue(doc.hasConversion(**{'format': 'png', 'quality': 80.0})) + + # as conversion is cached we should get it + converted_image_size_80n, converted_file_size_80n = self.getURLSizeList(doc_url, + **{'format':'png', 'quality':80.0, 'pre_converted_only':1}) + self.assertSameSet(converted_image_size_80n, converted_image_size_70) def test_getSearchText(self): """ -- 2.30.9