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