testDms.py 134 KB
Newer Older
1
# -*- coding: utf-8 -*-
Bartek Górny's avatar
Bartek Górny committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
##############################################################################
#
# Copyright (c) 2004 Nexedi SARL and Contributors. All Rights Reserved.
#          Sebastien Robin <seb@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################

30 31 32
"""
  A test suite for Document Management System functionality.
  This will test:
Bartek Górny's avatar
Bartek Górny committed
33
  - creating Text Document objects
34 35 36 37 38 39 40 41 42 43 44 45 46
  - setting properties of a document, assigning local roles
  - setting relations between documents (explicit and implicity)
  - searching in basic and advanced modes
  - document publication workflow settings
  - sourcing external content
  - (...)
  This will NOT test:
  - contributing files of various types
  - convertion between many formats
  - metadata extraction and editing
  - email ingestion
  These are subject to another suite "testIngestion".
"""
Bartek Górny's avatar
Bartek Górny committed
47

48
import unittest
49
import time
50
import StringIO
51
from subprocess import Popen, PIPE
52
from cgi import FieldStorage
53
from unittest import expectedFailure
Bartek Górny's avatar
Bartek Górny committed
54

55
import ZPublisher.HTTPRequest
Bartek Górny's avatar
Bartek Górny committed
56
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
Jérome Perrin's avatar
Jérome Perrin committed
57
from Products.ERP5Type.tests.ERP5TypeTestCase import  _getConversionServerDict
58
from Products.ERP5Type.tests.utils import FileUpload
59
from Products.ERP5Type.tests.utils import DummyLocalizer
60
from Products.ERP5OOo.OOoUtils import OOoBuilder
61
from Products.CMFCore.utils import getToolByName
Jérome Perrin's avatar
Jérome Perrin committed
62
from AccessControl.SecurityManagement import newSecurityManager
63
from AccessControl import getSecurityManager
64
from Products.ERP5.Document.Document import NotConvertedError
65
from Products.ERP5Form.PreferenceTool import Priority
66
from Products.ERP5Type.tests.utils import createZODBPythonScript
67
from Products.ERP5Type.Globals import get_request
Bartek Górny's avatar
Bartek Górny committed
68
import os
69 70
from threading import Thread
import httplib
71
import urllib
72
import difflib
73
import re
74
from AccessControl import Unauthorized
75
from Products.ERP5Type import Permissions
76
from DateTime import DateTime
77
from ZTUtils import make_query
Bartek Górny's avatar
Bartek Górny committed
78

Bartek Górny's avatar
Bartek Górny committed
79 80
QUIET = 0

81
TEST_FILES_HOME = os.path.join(os.path.dirname(__file__), 'test_document')
Nicolas Delaby's avatar
Nicolas Delaby committed
82
FILENAME_REGULAR_EXPRESSION = "(?P<reference>[A-Z]{3,10})-(?P<language>[a-z]{2})-(?P<version>[0-9]{3})"
83
REFERENCE_REGULAR_EXPRESSION = "(?P<reference>[A-Z]{3,10})(-(?P<language>[a-z]{2}))?(-(?P<version>[0-9]{3}))?"
84

Bartek Górny's avatar
Bartek Górny committed
85
def makeFilePath(name):
86
  return os.path.join(os.path.dirname(__file__), 'test_document', name)
Bartek Górny's avatar
Bartek Górny committed
87

88 89 90 91 92
def makeFileUpload(name, as_name=None):
  if as_name is None:
    as_name = name
  path = makeFilePath(name)
  return FileUpload(path, as_name)
Bartek Górny's avatar
Bartek Górny committed
93

Ivan Tyagov's avatar
Ivan Tyagov committed
94 95 96 97 98 99 100
def getFileSize(name):
  path = makeFilePath(name)
  f = open(path, "r")
  file_size = len(f.read())
  f.close()
  return file_size

101
class TestDocumentMixin(ERP5TypeTestCase):
102

Arnaud Fontaine's avatar
Arnaud Fontaine committed
103 104
  business_template_list = ['erp5_core_proxy_field_legacy',
                            'erp5_jquery',
105
                            'erp5_full_text_mroonga_catalog',
Arnaud Fontaine's avatar
Arnaud Fontaine committed
106
                            'erp5_base',
107
                            'erp5_ingestion_mysql_innodb_catalog',
Arnaud Fontaine's avatar
Arnaud Fontaine committed
108
                            'erp5_ingestion',
109
                            'erp5_web',
Arnaud Fontaine's avatar
Arnaud Fontaine committed
110
                            'erp5_dms']
Ivan Tyagov's avatar
Ivan Tyagov committed
111

112
  def setUpOnce(self):
113 114
    # set a dummy localizer (because normally it is cookie based)
    self.portal.Localizer = DummyLocalizer()
Bartek Górny's avatar
Bartek Górny committed
115

116
  def afterSetUp(self):
117
    TestDocumentMixin.login(self)
118 119 120
    self.setDefaultSitePreference()
    self.setSystemPreference()
    self.tic()
121
    self.login()
122

123 124
  def setDefaultSitePreference(self):
    default_pref = self.portal.portal_preferences.default_site_preference
Jérome Perrin's avatar
Jérome Perrin committed
125
    conversion_dict = _getConversionServerDict()
126
    default_pref.setPreferredDocumentConversionServerUrl(conversion_dict['url'])
Nicolas Delaby's avatar
Nicolas Delaby committed
127
    default_pref.setPreferredDocumentFilenameRegularExpression(FILENAME_REGULAR_EXPRESSION)
128
    default_pref.setPreferredDocumentReferenceRegularExpression(REFERENCE_REGULAR_EXPRESSION)
129
    if self.portal.portal_workflow.isTransitionPossible(default_pref, 'enable'):
130
      default_pref.enable()
131
    return default_pref
132

133 134 135 136 137
  def setSystemPreference(self):
    portal_type = 'System Preference'
    preference_list = self.portal.portal_preferences.contentValues(
                                                       portal_type=portal_type)
    if not preference_list:
138 139 140 141
      # create a Cache Factory for tests
      cache_factory = self.portal.portal_caches.newContent(portal_type = 'Cache Factory')
      cache_factory.cache_duration = 36000
      cache_plugin = cache_factory.newContent(portal_type='Ram Cache')
142
      cache_plugin.cache_expire_check_interval = 54000
143
      preference = self.portal.portal_preferences.newContent(title="Default System Preference",
144
                                                             # use local RAM based cache as some tests need it
145
                                                             preferred_conversion_cache_factory = cache_factory.getId(),
146
                                                             portal_type=portal_type)
147 148
    else:
      preference = preference_list[0]
149
    if self.portal.portal_workflow.isTransitionPossible(preference, 'enable'):
150 151 152
      preference.enable()
    return preference

Bartek Górny's avatar
Bartek Górny committed
153 154 155 156
  def getDocumentModule(self):
    return getattr(self.getPortal(),'document_module')

  def getBusinessTemplateList(self):
Arnaud Fontaine's avatar
Arnaud Fontaine committed
157
    return self.business_template_list
Bartek Górny's avatar
Bartek Górny committed
158 159

  def getNeededCategoryList(self):
160
    return ()
Bartek Górny's avatar
Bartek Górny committed
161

162
  def beforeTearDown(self):
163 164 165 166
    """
      Do some stuff after each test:
      - clear document module
    """
167
    self.abort()
168
    self.clearRestrictedSecurityHelperScript()
169
    activity_tool = self.portal.portal_activities
170 171
    activity_status = {m.processing_node < -1
                       for m in activity_tool.getMessageList()}
172 173 174 175
    if True in activity_status:
      activity_tool.manageClearActivities()
    else:
      assert not activity_status
176
    self.clearDocumentModule()
177

178 179
  conversion_format_permission_script_id_list = [
      'Document_checkConversionFormatPermission',
180
      'Image_checkConversionFormatPermission',
181
      'PDF_checkConversionFormatPermission']
182
  def clearRestrictedSecurityHelperScript(self):
183 184 185 186
    for script_id in self.conversion_format_permission_script_id_list:
      custom = self.getPortal().portal_skins.custom
      if script_id in custom.objectIds():
        custom.manage_delObjects(ids=[script_id])
187
        self.commit()
188

189
  def clearDocumentModule(self):
Bartek Górny's avatar
Bartek Górny committed
190
    """
191
      Remove everything after each run
Bartek Górny's avatar
Bartek Górny committed
192
    """
193
    self.abort()
194
    doc_module = self.getDocumentModule()
195
    doc_module.manage_delObjects(list(doc_module.objectIds()))
196 197
    self.tic()

198 199 200 201 202 203 204 205 206 207
class TestDocument(TestDocumentMixin):
  """
    Test basic document - related operations
  """

  def getTitle(self):
    return "DMS"

  ## setup

208

209
  ## helper methods
Bartek Górny's avatar
Bartek Górny committed
210

Nicolas Delaby's avatar
Nicolas Delaby committed
211
  def createTestDocument(self, filename=None, portal_type='Text', reference='TEST', version='002', language='en'):
Bartek Górny's avatar
Bartek Górny committed
212 213 214
    """
      Creates a text document
    """
215
    dm=self.getPortal().document_module
216
    doctext=dm.newContent(portal_type=portal_type)
Nicolas Delaby's avatar
Nicolas Delaby committed
217 218
    if filename is not None:
      f = open(makeFilePath(filename), 'rb')
Bartek Górny's avatar
Bartek Górny committed
219 220 221 222 223
      doctext.setTextContent(f.read())
      f.close()
    doctext.setReference(reference)
    doctext.setVersion(version)
    doctext.setLanguage(language)
224
    return doctext
Bartek Górny's avatar
Bartek Górny committed
225

Bartek Górny's avatar
Bartek Górny committed
226 227 228 229 230 231 232 233
  def getDocument(self, id):
    """
      Returns a document with given ID in the
      document module.
    """
    document_module = self.portal.document_module
    return getattr(document_module, id)

234 235 236 237 238 239 240 241 242 243
  def getPreferences(self, image_display):
    preference_tool = self.portal.getPortalObject().portal_preferences
    height_preference = 'preferred_%s_image_height' % (image_display,)
    width_preference = 'preferred_%s_image_width' % (image_display,)
    height = int(preference_tool.getPreference(height_preference))
    width = int(preference_tool.getPreference(width_preference))
    return (width, height)

  def getURLSizeList(self, uri, **kw):
    # __ac=RVJQNVR5cGVUZXN0Q2FzZTo%3D is encoded ERP5TypeTestCase with empty password
244
    url = '%s?%s&__ac=%s' %(uri, make_query(kw), 'RVJQNVR5cGVUZXN0Q2FzZTo%3D')
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
    format=kw.get('format', 'jpeg')
    infile = urllib.urlopen(url)
    # save as file with proper incl. format filename (for some reasons PIL uses this info)
    filename = "%s%stest-image-format-resize.%s" %(os.getcwd(), os.sep, format)
    f = open(filename, "w")
    image_data = infile.read()
    f.write(image_data)
    f.close()
    infile.close()
    file_size = len(image_data)
    try:
      from PIL import Image
      image = Image.open(filename)
      image_size = image.size
    except ImportError:
      identify_output = Popen(['identify', filename],
                              stdout=PIPE).communicate()[0]
      image_size = tuple(map(lambda x:int(x),
                             identify_output.split()[2].split('x')))
    os.remove(filename)
    return image_size, file_size

267 268
  ## tests

269
  def test_01_HasEverything(self):
Bartek Górny's avatar
Bartek Górny committed
270 271 272
    """
      Standard test to make sure we have everything we need - all the tools etc
    """
273 274 275 276 277 278
    self.assertNotEqual(self.getCategoryTool(), None)
    self.assertNotEqual(self.getSimulationTool(), None)
    self.assertNotEqual(self.getTypeTool(), None)
    self.assertNotEqual(self.getSQLConnection(), None)
    self.assertNotEqual(self.getCatalogTool(), None)
    self.assertNotEqual(self.getWorkflowTool(), None)
279

280
  def test_02_RevisionSystem(self):
Bartek Górny's avatar
Bartek Górny committed
281 282 283 284
    """
      Test revision mechanism
    """
    # create a test document
285
    # revision should be 1
Bartek Górny's avatar
Bartek Górny committed
286
    # upload file (can be the same) into it
287
    # revision should now be 2
288 289
    # edit the document with any value or no values
    # revision should now be 3
Bartek Górny's avatar
Bartek Górny committed
290
    # contribute the same file through portal_contributions
291 292
    # the same document should now have revision 4 (because it should have done mergeRevision)
    # getRevisionList should return (1, 2, 3, 4)
293 294 295 296 297 298 299
    filename = 'TEST-en-002.doc'
    file = makeFileUpload(filename)
    document = self.portal.portal_contributions.newContent(file=file)
    self.tic()
    document_url = document.getRelativeUrl()
    def getTestDocument():
      return self.portal.restrictedTraverse(document_url)
300
    self.assertEqual(getTestDocument().getRevision(), '1')
301 302
    getTestDocument().edit(file=file)
    self.tic()
303
    self.assertEqual(getTestDocument().getRevision(), '2')
304 305
    getTestDocument().edit(title='Hey Joe')
    self.tic()
306
    self.assertEqual(getTestDocument().getRevision(), '3')
307 308
    another_document = self.portal.portal_contributions.newContent(file=file)
    self.tic()
309 310
    self.assertEqual(getTestDocument().getRevision(), '4')
    self.assertEqual(getTestDocument().getRevisionList(), ['1', '2', '3', '4'])
Bartek Górny's avatar
Bartek Górny committed
311

312
  def test_03_Versioning(self):
Bartek Górny's avatar
Bartek Górny committed
313 314 315
    """
      Test versioning
    """
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
    # create a document 1, set coordinates (reference=TEST, version=002, language=en)
    # create a document 2, set coordinates (reference=TEST, version=002, language=en)
    # create a document 3, set coordinates (reference=TEST, version=004, language=en)
    # run isVersionUnique on 1, 2, 3 (should return False, False, True)
    # change version of 2 to 003
    # run isVersionUnique on 1, 2, 3  (should return True)
    # run getLatestVersionValue on all (should return 3)
    # run getVersionValueList on 2 (should return [3, 2, 1])
    document_module = self.getDocumentModule()
    docs = {}
    docs[1] = self.createTestDocument(reference='TEST', version='002', language='en')
    docs[2] = self.createTestDocument(reference='TEST', version='002', language='en')
    docs[3] = self.createTestDocument(reference='TEST', version='004', language='en')
    docs[4] = self.createTestDocument(reference='ANOTHER', version='002', language='en')
    self.tic()
331 332 333
    self.assertFalse(docs[1].isVersionUnique())
    self.assertFalse(docs[2].isVersionUnique())
    self.assertTrue(docs[3].isVersionUnique())
334 335
    docs[2].setVersion('003')
    self.tic()
336 337 338 339 340 341
    self.assertTrue(docs[1].isVersionUnique())
    self.assertTrue(docs[2].isVersionUnique())
    self.assertTrue(docs[3].isVersionUnique())
    self.assertTrue(docs[1].getLatestVersionValue() == docs[3])
    self.assertTrue(docs[2].getLatestVersionValue() == docs[3])
    self.assertTrue(docs[3].getLatestVersionValue() == docs[3])
342
    version_list = [br.getRelativeUrl() for br in docs[2].getVersionValueList()]
343
    self.assertTrue(version_list == [docs[3].getRelativeUrl(), docs[2].getRelativeUrl(), docs[1].getRelativeUrl()])
Bartek Górny's avatar
Bartek Górny committed
344

345
  def test_04_VersioningWithLanguage(self):
Bartek Górny's avatar
Bartek Górny committed
346 347 348 349 350 351 352 353 354
    """
      Test versioning with multi-language support
    """
    # create empty test documents, set their coordinates as follows:
    # (1) TEST, 002, en
    # (2) TEST, 002, fr
    # (3) TEST, 002, pl
    # (4) TEST, 003, en
    # (5) TEST, 003, sp
355
    # the following calls (on any doc) should produce the following output:
Bartek Górny's avatar
Bartek Górny committed
356 357 358 359 360 361 362
    # getOriginalLanguage() = 'en'
    # getLanguageList = ('en', 'fr', 'pl', 'sp')
    # getLatestVersionValue() = 4
    # getLatestVersionValue('en') = 4
    # getLatestVersionValue('fr') = 2
    # getLatestVersionValue('pl') = 3
    # getLatestVersionValue('ru') = None
363
    # change user language into 'sp'
Bartek Górny's avatar
Bartek Górny committed
364
    # getLatestVersionValue() = 5
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
    # add documents:
    # (6) TEST, 004, pl
    # (7) TEST, 004, en
    # getLatestVersionValue() = 7
    localizer = self.portal.Localizer
    document_module = self.getDocumentModule()
    docs = {}
    docs[1] = self.createTestDocument(reference='TEST', version='002', language='en')
    time.sleep(1) # time span here because catalog records only full seconds
    docs[2] = self.createTestDocument(reference='TEST', version='002', language='fr')
    time.sleep(1)
    docs[3] = self.createTestDocument(reference='TEST', version='002', language='pl')
    time.sleep(1)
    docs[4] = self.createTestDocument(reference='TEST', version='003', language='en')
    time.sleep(1)
    docs[5] = self.createTestDocument(reference='TEST', version='003', language='sp')
    time.sleep(1)
    self.tic()
    doc = docs[2] # can be any
384 385 386 387 388 389 390
    self.assertTrue(doc.getOriginalLanguage() == 'en')
    self.assertTrue(doc.getLanguageList() == ['en', 'fr', 'pl', 'sp'])
    self.assertTrue(doc.getLatestVersionValue() == docs[4]) # there are two latest - it chooses the one in user language
    self.assertTrue(doc.getLatestVersionValue('en') == docs[4])
    self.assertTrue(doc.getLatestVersionValue('fr') == docs[2])
    self.assertTrue(doc.getLatestVersionValue('pl') == docs[3])
    self.assertTrue(doc.getLatestVersionValue('ru') == None)
391
    localizer.changeLanguage('sp') # change user language
392
    self.assertTrue(doc.getLatestVersionValue() == docs[5]) # there are two latest - it chooses the one in user language
393 394 395
    docs[6] = document_module.newContent(reference='TEST', version='004', language='pl')
    docs[7] = document_module.newContent(reference='TEST', version='004', language='en')
    self.tic()
396
    self.assertTrue(doc.getLatestVersionValue() == docs[7]) # there are two latest, neither in user language - it chooses the one in original language
Bartek Górny's avatar
Bartek Górny committed
397

398
  def test_06_testExplicitRelations(self):
Bartek Górny's avatar
Bartek Górny committed
399 400 401 402 403 404 405 406 407 408 409 410
    """
      Test explicit relations.
      Explicit relations are just like any other relation, so no need to test them here
      except for similarity cloud which we test.
    """
    # create test documents:
    # (1) TEST, 002, en
    # (2) TEST, 003, en
    # (3) ONE, 001, en
    # (4) TWO, 001, en
    # (5) THREE, 001, en
    # set 3 similar to 1, 4 to 3, 5 to 4
Romain Courteaud's avatar
Romain Courteaud committed
411
    # getSimilarCloudValueList on 4 should return 1, 3 and 5
Bartek Górny's avatar
Bartek Górny committed
412
    # getSimilarCloudValueList(depth=1) on 4 should return 3 and 5
Fabien Morin's avatar
Fabien Morin committed
413

414 415 416 417 418 419 420 421
    # create documents for test version and language
    # reference, version, language
    kw = {'portal_type': 'Drawing'}
    document1 = self.portal.document_module.newContent(**kw)
    document2 = self.portal.document_module.newContent(**kw)
    document3 = self.portal.document_module.newContent(**kw)
    document4 = self.portal.document_module.newContent(**kw)
    document5 = self.portal.document_module.newContent(**kw)
Fabien Morin's avatar
Fabien Morin committed
422 423

    document6 = self.portal.document_module.newContent(reference='SIX', version='001',
424
                                                                                    language='en',  **kw)
Fabien Morin's avatar
Fabien Morin committed
425
    document7 = self.portal.document_module.newContent(reference='SEVEN', version='001',
426
                                                                                    language='en',  **kw)
Fabien Morin's avatar
Fabien Morin committed
427
    document8 = self.portal.document_module.newContent(reference='SEVEN', version='001',
428
                                                                                    language='fr',  **kw)
Fabien Morin's avatar
Fabien Morin committed
429
    document9 = self.portal.document_module.newContent(reference='EIGHT', version='001',
430
                                                                                    language='en',  **kw)
Fabien Morin's avatar
Fabien Morin committed
431
    document10 = self.portal.document_module.newContent(reference='EIGHT', version='002',
432
                                                                                      language='en',  **kw)
Fabien Morin's avatar
Fabien Morin committed
433
    document11 = self.portal.document_module.newContent(reference='TEN', version='001',
434
                                                                                      language='en',  **kw)
Fabien Morin's avatar
Fabien Morin committed
435
    document12 = self.portal.document_module.newContent(reference='TEN', version='001',
436
                                                                                      language='fr',  **kw)
Fabien Morin's avatar
Fabien Morin committed
437
    document13 = self.portal.document_module.newContent(reference='TEN', version='002',
438
                                                                                      language='en',  **kw)
Romain Courteaud's avatar
Romain Courteaud committed
439 440 441 442

    document3.setSimilarValue(document1)
    document4.setSimilarValue(document3)
    document5.setSimilarValue(document4)
Fabien Morin's avatar
Fabien Morin committed
443

444 445 446
    document6.setSimilarValueList([document8,  document13])
    document7.setSimilarValue([document9])
    document11.setSimilarValue(document7)
Romain Courteaud's avatar
Romain Courteaud committed
447 448

    self.tic()
Fabien Morin's avatar
Fabien Morin committed
449

450 451
    #if user language is 'en'
    self.portal.Localizer.changeLanguage('en')
Romain Courteaud's avatar
Romain Courteaud committed
452

453
    # 4 is similar to 3 and 5, 3 similar to 1, last version are the same
Romain Courteaud's avatar
Romain Courteaud committed
454 455 456 457
    self.assertSameSet([document1, document3, document5],
                       document4.getSimilarCloudValueList())
    self.assertSameSet([document3, document5],
                       document4.getSimilarCloudValueList(depth=1))
Bartek Górny's avatar
Bartek Górny committed
458

Fabien Morin's avatar
Fabien Morin committed
459
    self.assertSameSet([document7, document13],
460
                       document6.getSimilarCloudValueList())
Fabien Morin's avatar
Fabien Morin committed
461
    self.assertSameSet([document10, document13],
462
                       document7.getSimilarCloudValueList())
Fabien Morin's avatar
Fabien Morin committed
463
    self.assertSameSet([document7, document13],
464
                       document9.getSimilarCloudValueList())
Fabien Morin's avatar
Fabien Morin committed
465
    self.assertSameSet([],
466 467
                       document10.getSimilarCloudValueList())
    # 11 similar to 7, last version of 7 (en) is 7, similar of 7 is 9, last version of 9 (en) is 10
Fabien Morin's avatar
Fabien Morin committed
468
    self.assertSameSet([document7, document10],
469
                       document11.getSimilarCloudValueList())
Fabien Morin's avatar
Fabien Morin committed
470
    self.assertSameSet([document6, document7],
471 472
                       document13.getSimilarCloudValueList())

473
    self.commit()
Fabien Morin's avatar
Fabien Morin committed
474

475 476
    # if user language is 'fr', test that latest documents are prefferable returned in user_language (if available)
    self.portal.Localizer.changeLanguage('fr')
Fabien Morin's avatar
Fabien Morin committed
477 478

    self.assertSameSet([document8, document13],
479
                       document6.getSimilarCloudValueList())
Fabien Morin's avatar
Fabien Morin committed
480
    self.assertSameSet([document6, document13],
481
                       document8.getSimilarCloudValueList())
Fabien Morin's avatar
Fabien Morin committed
482
    self.assertSameSet([document8, document10],
483
                       document11.getSimilarCloudValueList())
Fabien Morin's avatar
Fabien Morin committed
484
    self.assertSameSet([],
485
                       document12.getSimilarCloudValueList())
Fabien Morin's avatar
Fabien Morin committed
486
    self.assertSameSet([document6, document8],
487
                       document13.getSimilarCloudValueList())
Fabien Morin's avatar
Fabien Morin committed
488

489
    self.commit()
Fabien Morin's avatar
Fabien Morin committed
490

491 492
    # if user language is "bg"
    self.portal.Localizer.changeLanguage('bg')
Fabien Morin's avatar
Fabien Morin committed
493
    self.assertSameSet([document8, document13],
494 495
                       document6.getSimilarCloudValueList())

496
  def test_07_testImplicitRelations(self):
Bartek Górny's avatar
Bartek Górny committed
497 498 499 500
    """
      Test implicit (wiki-like) relations.
    """
    # XXX this test should be extended to check more elaborate language selection
501 502 503 504

    def sqlresult_to_document_list(result):
      return [i.getObject() for i in result]

Bartek Górny's avatar
Bartek Górny committed
505 506
    # create docs to be referenced:
    # (1) TEST, 002, en
507 508 509 510
    filename = 'TEST-en-002.odt'
    file = makeFileUpload(filename)
    document1 = self.portal.portal_contributions.newContent(file=file)

Bartek Górny's avatar
Bartek Górny committed
511
    # (2) TEST, 002, fr
512 513
    as_name = 'TEST-fr-002.odt'
    file = makeFileUpload(filename, as_name)
514 515
    document2 = self.portal.portal_contributions.newContent(file=file)

Bartek Górny's avatar
Bartek Górny committed
516
    # (3) TEST, 003, en
517 518
    as_name = 'TEST-en-003.odt'
    file = makeFileUpload(filename, as_name)
519 520
    document3 = self.portal.portal_contributions.newContent(file=file)

Bartek Górny's avatar
Bartek Górny committed
521 522
    # create docs to contain references in text_content:
    # REF, 001, en; "I use reference to look up TEST"
523 524 525 526
    filename = 'REF-en-001.odt'
    file = makeFileUpload(filename)
    document4 = self.portal.portal_contributions.newContent(file=file)

Bartek Górny's avatar
Bartek Górny committed
527
    # REF, 002, en; "I use reference to look up TEST"
528 529 530 531
    filename = 'REF-en-002.odt'
    file = makeFileUpload(filename)
    document5 = self.portal.portal_contributions.newContent(file=file)

Bartek Górny's avatar
Bartek Górny committed
532
    # REFLANG, 001, en: "I use reference and language to look up TEST-fr"
533 534 535 536
    filename = 'REFLANG-en-001.odt'
    file = makeFileUpload(filename)
    document6 = self.portal.portal_contributions.newContent(file=file)

Bartek Górny's avatar
Bartek Górny committed
537
    # REFVER, 001, en: "I use reference and version to look up TEST-002"
538 539 540 541
    filename = 'REFVER-en-001.odt'
    file = makeFileUpload(filename)
    document7 = self.portal.portal_contributions.newContent(file=file)

Bartek Górny's avatar
Bartek Górny committed
542
    # REFVERLANG, 001, en: "I use reference, version and language to look up TEST-002-en"
543 544 545 546 547
    filename = 'REFVERLANG-en-001.odt'
    file = makeFileUpload(filename)
    document8 = self.portal.portal_contributions.newContent(file=file)

    self.tic()
548 549
    # the implicit predecessor will find documents by reference.
    # version and language are not used.
Bartek Górny's avatar
Bartek Górny committed
550
    # the implicit predecessors should be:
551 552 553 554 555 556 557 558 559

    # for (1): REF-002, REFLANG, REFVER, REFVERLANG
    # document1's reference is TEST. getImplicitPredecessorValueList will
    # return latest version of documents which contains string "TEST".
    self.assertSameSet(
      [document5, document6, document7, document8],
      sqlresult_to_document_list(document1.getImplicitPredecessorValueList()))

    # clear transactional variable cache
560
    self.commit()
561 562 563 564 565 566 567 568 569 570 571 572

    # the implicit successors should be return document with appropriate
    # language.

    # if user language is 'en'.
    self.portal.Localizer.changeLanguage('en')

    self.assertSameSet(
      [document3],
      sqlresult_to_document_list(document5.getImplicitSuccessorValueList()))

    # clear transactional variable cache
573
    self.commit()
574 575 576 577 578 579 580 581

    # if user language is 'fr'.
    self.portal.Localizer.changeLanguage('fr')
    self.assertSameSet(
      [document2],
      sqlresult_to_document_list(document5.getImplicitSuccessorValueList()))

    # clear transactional variable cache
582
    self.commit()
583 584 585 586 587 588

    # if user language is 'ja'.
    self.portal.Localizer.changeLanguage('ja')
    self.assertSameSet(
      [document3],
      sqlresult_to_document_list(document5.getImplicitSuccessorValueList()))
Bartek Górny's avatar
Bartek Górny committed
589

590 591 592 593 594 595 596
    # with empty reference no implicit relation should exists even though some documents
    # used to reference us with previous reference
    document1.setReference(None)
    self.tic()
    self.assertSameSet([],
      sqlresult_to_document_list(document1.getImplicitPredecessorValueList()))

597 598 599 600
  def testOOoDocument_get_size(self):
    # test get_size on OOoDocument
    doc = self.portal.document_module.newContent(portal_type='Spreadsheet')
    doc.edit(file=makeFileUpload('import_data_list.ods'))
601
    self.assertEqual(len(makeFileUpload('import_data_list.ods').read()),
602 603 604 605 606 607
                      doc.get_size())

  def testTempOOoDocument_get_size(self):
    # test get_size on temporary OOoDocument
    from Products.ERP5Type.Document import newTempOOoDocument
    doc = newTempOOoDocument(self.portal, 'tmp')
608
    doc.edit(data='OOo')
609
    self.assertEqual(len('OOo'), doc.get_size())
610

611 612 613
  def testOOoDocument_hasData(self):
    # test hasData on OOoDocument
    doc = self.portal.document_module.newContent(portal_type='Spreadsheet')
614
    self.assertFalse(doc.hasData())
615
    doc.edit(file=makeFileUpload('import_data_list.ods'))
616
    self.assertTrue(doc.hasData())
617 618 619 620 621

  def testTempOOoDocument_hasData(self):
    # test hasData on TempOOoDocument
    from Products.ERP5Type.Document import newTempOOoDocument
    doc = newTempOOoDocument(self.portal, 'tmp')
622
    self.assertFalse(doc.hasData())
623
    doc.edit(file=makeFileUpload('import_data_list.ods'))
624
    self.assertTrue(doc.hasData())
625

626
  def test_Owner_Base_download(self):
627 628 629
    # tests that owners can download original documents and OOo
    # documents, and all headers (including filenames) are set
    # correctly
630
    doc = self.portal.document_module.newContent(
Nicolas Delaby's avatar
Nicolas Delaby committed
631
                                  filename='test.ods',
632
                                  portal_type='Spreadsheet')
633 634
    doc.edit(file=makeFileUpload('TEST-en-002.doc'))
    self.tic()
635 636 637 638 639 640 641 642

    uf = self.portal.acl_users
    uf._doAddUser('member_user1', 'secret', ['Member', 'Owner'], [])
    user = uf.getUserById('member_user1').__of__(uf)
    newSecurityManager(None, user)

    response = self.publish('%s/Base_download' % doc.getPath(),
                            basic='member_user1:secret')
643
    self.assertEqual(makeFileUpload('TEST-en-002.doc').read(),
Nicolas Delaby's avatar
Nicolas Delaby committed
644
                      response.getBody())
645
    self.assertEqual('application/msword',
646
                      response.headers['content-type'])
647 648 649 650 651 652 653
    self.assertEqual('attachment; filename="TEST-en-002.doc"',
                      response.headers['content-disposition'])
    response = self.publish('%s/OOoDocument_getOOoFile' % doc.getPath(),
                            basic='member_user1:secret')
    self.assertEqual('application/vnd.oasis.opendocument.text',
                      response.headers['content-type'])
    self.assertEqual('attachment; filename="TEST-en-002.odt"',
654 655 656 657 658 659
                      response.headers['content-disposition'])

  def test_Member_download_pdf_format(self):
    # tests that members can download OOo documents in pdf format (at least in
    # published state), and all headers (including filenames) are set correctly
    doc = self.portal.document_module.newContent(
Nicolas Delaby's avatar
Nicolas Delaby committed
660
                                  filename='test.ods',
661
                                  portal_type='Spreadsheet')
662
    doc.edit(file=makeFileUpload('import.file.with.dot.in.filename.ods'))
663
    doc.publish()
664
    self.tic()
665 666 667 668 669 670

    uf = self.portal.acl_users
    uf._doAddUser('member_user2', 'secret', ['Member'], [])
    user = uf.getUserById('member_user2').__of__(uf)
    newSecurityManager(None, user)

671
    response = self.publish('%s?format=pdf' % doc.getPath(),
672
                            basic='member_user2:secret')
673 674
    self.assertEqual('application/pdf', response.getHeader('content-type'))
    self.assertEqual('attachment; filename="import.file.with.dot.in.filename.pdf"',
Nicolas Delaby's avatar
Nicolas Delaby committed
675
                      response.getHeader('content-disposition'))
676 677 678 679 680 681
    response_body = response.getBody()
    conversion = str(doc.convert('pdf')[1])
    diff = '\n'+'\n'.join(difflib.unified_diff(response_body.splitlines(),
                                          conversion.splitlines(),
                                          fromfile='first_call.pdf',
                                          tofile='second_call.pdf'))
682
    self.assertEqual(response_body, conversion, diff)
683

684 685
    # test Print icon works on OOoDocument
    response = self.publish('%s/OOoDocument_print' % doc.getPath())
686
    self.assertEqual('application/pdf',
687
                      response.headers['content-type'])
688
    self.assertEqual('attachment; filename="import.file.with.dot.in.filename.pdf"',
689 690
                      response.headers['content-disposition'])

691
  def test_05_getCreationDate(self):
692
    """
Ivan Tyagov's avatar
Ivan Tyagov committed
693
    Check getCreationDate on all document types.
694 695 696 697 698 699 700 701 702 703
    """
    portal = self.getPortalObject()
    for document_type in portal.getPortalDocumentTypeList():
      module = portal.getDefaultModule(document_type)
      obj = module.newContent(portal_type=document_type)
      self.assertNotEquals(obj.getCreationDate(),
                           module.getCreationDate())
      self.assertNotEquals(obj.getCreationDate(),
                           portal.CreationDate())

704
  def test_06_ProcessingStateOfAClonedDocument(self):
705 706 707 708 709 710 711 712
    """
    Check that the processing state of a cloned document
    is not draft
    """
    filename = 'TEST-en-002.doc'
    file = makeFileUpload(filename)
    document = self.portal.portal_contributions.newContent(file=file)

713
    self.assertEqual('converting', document.getExternalProcessingState())
714
    self.commit()
715
    self.assertEqual('converting', document.getExternalProcessingState())
716 717 718 719 720 721 722

    # Clone a uploaded document
    container = document.getParentValue()
    clipboard = container.manage_copyObjects(ids=[document.getId()])
    paste_result = container.manage_pasteObjects(cb_copy_data=clipboard)
    new_document = container[paste_result[0]['new_id']]

723
    self.assertEqual('converting', new_document.getExternalProcessingState())
724
    self.commit()
725
    self.assertEqual('converting', new_document.getExternalProcessingState())
726 727

    # Change workflow state to converted
728
    self.tic()
729 730
    self.assertEqual('converted', document.getExternalProcessingState())
    self.assertEqual('converted', new_document.getExternalProcessingState())
731

732
    # Clone a converted document
733 734 735 736 737
    container = document.getParentValue()
    clipboard = container.manage_copyObjects(ids=[document.getId()])
    paste_result = container.manage_pasteObjects(cb_copy_data=clipboard)
    new_document = container[paste_result[0]['new_id']]

738
    self.assertEqual('converted', new_document.getExternalProcessingState())
739
    self.commit()
740
    self.assertEqual('converted', new_document.getExternalProcessingState())
741
    self.tic()
742
    self.assertEqual('converted', new_document.getExternalProcessingState())
743

744
  def test_07_EmbeddedDocumentOfAClonedDocument(self):
745 746 747 748
    """
    Check the validation state of embedded document when its container is
    cloned
    """
749
    document = self.portal.person_module.newContent(portal_type='Person')
750

751
    sub_document = document.newContent(portal_type='Embedded File')
752
    self.assertEqual('embedded', sub_document.getValidationState())
753
    self.tic()
754
    self.assertEqual('embedded', sub_document.getValidationState())
755 756 757 758 759 760 761 762

    # Clone document
    container = document.getParentValue()
    clipboard = container.manage_copyObjects(ids=[document.getId()])

    paste_result = container.manage_pasteObjects(cb_copy_data=clipboard)
    new_document = container[paste_result[0]['new_id']]

763
    new_sub_document_list = new_document.contentValues(portal_type='Embedded File')
764
    self.assertEqual(1, len(new_sub_document_list))
765
    new_sub_document = new_sub_document_list[0]
766
    self.assertEqual('embedded', new_sub_document.getValidationState())
767
    self.tic()
768
    self.assertEqual('embedded', new_sub_document.getValidationState())
769

770 771 772
  def test_08_NoImagesCreatedDuringHTMLConversion(self):
    """Converting an ODT to html no longer creates Images embedded in the
    document.
773 774 775 776 777 778 779
    """
    filename = 'EmbeddedImage-en-002.odt'
    file = makeFileUpload(filename)
    document = self.portal.portal_contributions.newContent(file=file)

    self.tic()

780
    self.assertEqual(0, len(document.contentValues(portal_type='Image')))
781 782
    document.convert(format='html')
    image_list = document.contentValues(portal_type='Image')
783
    self.assertEqual(0, len(image_list))
784

785
  def test_09_SearchableText(self):
786
    """
787
    Check DMS SearchableText capabilities.
788
    """
Ivan Tyagov's avatar
Ivan Tyagov committed
789
    portal = self.portal
790

791
    # Create a document.
Ivan Tyagov's avatar
Ivan Tyagov committed
792 793 794 795 796 797 798 799 800 801
    document_1 = self.portal.document_module.newContent(
                        portal_type = 'File',
                        description = 'Hello. ScriptableKey is very useful if you want to make your own search syntax.',
                        language = 'en',
                        version = '001')
    document_2 = self.portal.document_module.newContent(
                        portal_type='File',
                        description = 'This test make sure that scriptable key feature on ZSQLCatalog works.',
                        language='fr',
                        version = '002')
Ivan Tyagov's avatar
Ivan Tyagov committed
802 803 804 805 806 807
    document_3 = portal.document_module.newContent(
                   portal_type = 'Presentation',
                   title = "Complete set of tested reports with a long title.",
                   version = '003',
                   language = 'bg',
                   reference = 'tio-test-doc-3')
Ivan Tyagov's avatar
Ivan Tyagov committed
808 809 810 811 812
    person = portal.person_module.newContent(portal_type = 'Person', \
                                             reference= "john",
                                             title='John Doe Great')
    web_page = portal.web_page_module.newContent(portal_type = 'Web Page',
                                                 reference = "page_great_site",
Ivan Tyagov's avatar
Ivan Tyagov committed
813 814 815
                                                 text_content = 'Great website',
                                                 language='en',
                                                 version = '003')
Ivan Tyagov's avatar
Ivan Tyagov committed
816 817 818 819
    organisation = portal.organisation_module.newContent( \
                            portal_type = 'Organisation', \
                            reference = 'organisation-1',
                            title='Super nova organisation')
820
    self.tic()
Nicolas Delaby's avatar
Nicolas Delaby committed
821

822
    def getAdvancedSearchTextResultList(searchable_text, portal_type=None,src__=0):
823
      kw = {'full_text': searchable_text}
824 825
      if portal_type is not None:
        kw['portal_type'] = portal_type
826 827
      if src__==1:
        print portal.portal_catalog(src__=src__,**kw)
Ivan Tyagov's avatar
Ivan Tyagov committed
828
      return [x.getObject() for x in portal.portal_catalog(**kw)]
Nicolas Delaby's avatar
Nicolas Delaby committed
829

Ivan Tyagov's avatar
Ivan Tyagov committed
830 831 832 833 834
    # full text search
    self.assertSameSet([document_1], \
      getAdvancedSearchTextResultList('ScriptableKey'))
    self.assertEqual(len(getAdvancedSearchTextResultList('RelatedKey')), 0)
    self.assertSameSet([document_1, document_2], \
Ivan Tyagov's avatar
Ivan Tyagov committed
835
      getAdvancedSearchTextResultList('make', ('File',)))
Ivan Tyagov's avatar
Ivan Tyagov committed
836
    self.assertSameSet([web_page, person], \
837
      getAdvancedSearchTextResultList("Great", ('Person', 'Web Page')))
Ivan Tyagov's avatar
Ivan Tyagov committed
838 839
    # full text search with whole title of a document
    self.assertSameSet([document_3], \
840
      getAdvancedSearchTextResultList(document_3.getTitle(), ('Presentation',)))
841
    # full text search with reference part of searchable_text
Ivan Tyagov's avatar
Ivan Tyagov committed
842 843
    # (i.e. not specified with 'reference:' - simply part of search text)
    self.assertSameSet([document_3], \
844
      getAdvancedSearchTextResultList(document_3.getReference(), ('Presentation',)))
Ivan Tyagov's avatar
Ivan Tyagov committed
845 846 847 848 849 850 851 852 853

   # full text search with reference
    self.assertSameSet([web_page], \
      getAdvancedSearchTextResultList("reference:%s Great" %web_page.getReference()))
    self.assertSameSet([person],
          getAdvancedSearchTextResultList('reference:%s' %person.getReference()))

    # full text search with portal_type
    self.assertSameSet([person], \
Ivan Tyagov's avatar
Ivan Tyagov committed
854 855
      getAdvancedSearchTextResultList('%s portal_type:%s' %(person.getTitle(), person.getPortalType())))

Ivan Tyagov's avatar
Ivan Tyagov committed
856
    self.assertSameSet([organisation], \
Ivan Tyagov's avatar
Ivan Tyagov committed
857 858 859
      getAdvancedSearchTextResultList('%s portal_type:%s' \
                                       %(organisation.getTitle(),
                                         organisation.getPortalType())))
860 861 862

    # full text search with portal_type passed outside searchable_text
    self.assertSameSet([web_page, person],
863 864
                       getAdvancedSearchTextResultList('Great',
                          ('Person', 'Web Page')))
865 866 867 868
    self.assertSameSet([web_page], \
                       getAdvancedSearchTextResultList('Great', web_page.getPortalType()))
    self.assertSameSet([person], \
                       getAdvancedSearchTextResultList('Great', person.getPortalType()))
869

Ivan Tyagov's avatar
Ivan Tyagov committed
870 871
    # full text search with portal_type & reference
    self.assertSameSet([person], \
Ivan Tyagov's avatar
Ivan Tyagov committed
872 873 874 875 876 877
      getAdvancedSearchTextResultList('reference:%s portal_type:%s' \
                                        %(person.getReference(), person.getPortalType())))
    # full text search with language
    self.assertSameSet([document_1, web_page], \
      getAdvancedSearchTextResultList('language:en'))
    self.assertSameSet([document_1], \
878
      getAdvancedSearchTextResultList('ScriptableKey language:en'))
Ivan Tyagov's avatar
Ivan Tyagov committed
879 880 881 882 883 884 885 886 887 888 889 890 891 892
    self.assertSameSet([document_2], \
      getAdvancedSearchTextResultList('language:fr'))
    self.assertSameSet([web_page], \
      getAdvancedSearchTextResultList('%s reference:%s language:%s' \
                                       %(web_page.getTextContent(),
                                         web_page.getReference(),
                                         web_page.getLanguage())))
    # full text search with version
    self.assertSameSet([web_page], \
      getAdvancedSearchTextResultList('%s reference:%s language:%s version:%s' \
                                       %(web_page.getTextContent(),
                                         web_page.getReference(),
                                         web_page.getLanguage(),
                                         web_page.getVersion())))
893 894 895 896

    document = portal.document_module.newContent(
                   portal_type = 'Presentation',)
    # searchable text is empty by default
897
    self.assertEqual('', document.SearchableText())
898 899
    # it contains title
    document.setTitle('foo')
900
    self.assertEqual('foo', document.SearchableText())
901 902 903 904 905
    # and description
    document.setDescription('bar')
    self.assertTrue('bar' in document.SearchableText(),
      document.SearchableText())

906
  def test_10_SearchString(self):
907 908 909 910 911 912 913
    """
    Test search string search generation and parsing.
    """

    portal = self.portal
    assemble = portal.Base_assembleSearchString
    parse = portal.Base_parseSearchString
914

915
    # directly pasing searchable string
916
    self.assertEqual('searchable text',
917 918 919 920 921
                      assemble(**{'searchabletext': 'searchable text'}))
    kw = {'searchabletext_any': 'searchabletext_any',
          'searchabletext_phrase': 'searchabletext_phrase1 searchabletext_phrase1'}
    # exact phrase
    search_string = assemble(**kw)
922
    self.assertEqual('%s "%s"' %(kw['searchabletext_any'], kw['searchabletext_phrase']), \
923 924
                      search_string)
    parsed_string = parse(search_string)
925
    self.assertEqual(['searchabletext'], parsed_string.keys())
926

927

928 929 930
    # search "with all of the words"
    kw["searchabletext_all"] = "searchabletext_all1 searchabletext_all2"
    search_string = assemble(**kw)
931
    self.assertEqual('searchabletext_any "searchabletext_phrase1 searchabletext_phrase1"  +searchabletext_all1 +searchabletext_all2', \
932 933
                      search_string)
    parsed_string = parse(search_string)
934
    self.assertEqual(['searchabletext'], parsed_string.keys())
935 936

    # search without these words
937 938
    kw["searchabletext_without"] = "searchabletext_without1 searchabletext_without2"
    search_string = assemble(**kw)
939
    self.assertEqual('searchabletext_any "searchabletext_phrase1 searchabletext_phrase1"  +searchabletext_all1 +searchabletext_all2 -searchabletext_without1 -searchabletext_without2', \
940 941
                      search_string)
    parsed_string = parse(search_string)
942
    self.assertEqual(['searchabletext'], parsed_string.keys())
943

944 945 946
    # search limited to a certain date range
    kw['created_within'] = '1w'
    search_string = assemble(**kw)
947
    self.assertEqual('searchabletext_any "searchabletext_phrase1 searchabletext_phrase1"  +searchabletext_all1 +searchabletext_all2 -searchabletext_without1 -searchabletext_without2 created:1w', \
948 949 950
                      search_string)
    parsed_string = parse(search_string)
    self.assertSameSet(['searchabletext', 'creation_from'], parsed_string.keys())
951

952 953 954 955
    # search with portal_type
    kw['search_portal_type'] = 'Document'
    search_string = assemble(**kw)
    parsed_string = parse(search_string)
956
    self.assertEqual('searchabletext_any "searchabletext_phrase1 searchabletext_phrase1"  +searchabletext_all1 +searchabletext_all2 -searchabletext_without1 -searchabletext_without2 created:1w AND (portal_type:Document)', \
957 958 959
                      search_string)
    self.assertSameSet(['searchabletext', 'creation_from', 'portal_type'], \
                        parsed_string.keys())
960
    self.assertEqual(kw['search_portal_type'], parsed_string['portal_type'])
961

962 963 964 965
    # search by reference
    kw['reference'] = 'Nxd-test'
    search_string = assemble(**kw)
    parsed_string = parse(search_string)
966
    self.assertEqual('searchabletext_any "searchabletext_phrase1 searchabletext_phrase1"  +searchabletext_all1 +searchabletext_all2 -searchabletext_without1 -searchabletext_without2 created:1w AND (portal_type:Document) reference:Nxd-test', \
967 968 969
                      search_string)
    self.assertSameSet(['searchabletext', 'creation_from', 'portal_type', 'reference'], \
                        parsed_string.keys())
970 971
    self.assertEqual(kw['search_portal_type'], parsed_string['portal_type'])
    self.assertEqual(kw['reference'], parsed_string['reference'])
972

973 974 975 976
    # search by version
    kw['version'] = '001'
    search_string = assemble(**kw)
    parsed_string = parse(search_string)
977
    self.assertEqual('searchabletext_any "searchabletext_phrase1 searchabletext_phrase1"  +searchabletext_all1 +searchabletext_all2 -searchabletext_without1 -searchabletext_without2 created:1w AND (portal_type:Document) reference:Nxd-test version:001', \
978 979 980
                      search_string)
    self.assertSameSet(['searchabletext', 'creation_from', 'portal_type', 'reference', 'version'], \
                        parsed_string.keys())
981 982 983
    self.assertEqual(kw['search_portal_type'], parsed_string['portal_type'])
    self.assertEqual(kw['reference'], parsed_string['reference'])
    self.assertEqual(kw['version'], parsed_string['version'])
984

985 986 987 988
    # search by language
    kw['language'] = 'en'
    search_string = assemble(**kw)
    parsed_string = parse(search_string)
989
    self.assertEqual('searchabletext_any "searchabletext_phrase1 searchabletext_phrase1"  +searchabletext_all1 +searchabletext_all2 -searchabletext_without1 -searchabletext_without2 created:1w AND (portal_type:Document) reference:Nxd-test version:001 language:en', \
990 991 992 993
                      search_string)
    self.assertSameSet(['searchabletext', 'creation_from', 'portal_type', 'reference', \
                        'version', 'language'], \
                        parsed_string.keys())
994 995 996 997
    self.assertEqual(kw['search_portal_type'], parsed_string['portal_type'])
    self.assertEqual(kw['reference'], parsed_string['reference'])
    self.assertEqual(kw['version'], parsed_string['version'])
    self.assertEqual(kw['language'], parsed_string['language'])
998

999 1000 1001 1002
    # contributor title search
    kw['contributor_title'] = 'John'
    search_string = assemble(**kw)
    parsed_string = parse(search_string)
1003
    self.assertEqual('searchabletext_any "searchabletext_phrase1 searchabletext_phrase1"  +searchabletext_all1 +searchabletext_all2 -searchabletext_without1 -searchabletext_without2 created:1w AND (portal_type:Document) reference:Nxd-test version:001 language:en contributor_title:John', \
1004 1005 1006 1007
                      search_string)
    self.assertSameSet(['searchabletext', 'creation_from', 'portal_type', 'reference', \
                        'version', 'language', 'contributor_title'], \
                        parsed_string.keys())
1008 1009 1010 1011
    self.assertEqual(kw['search_portal_type'], parsed_string['portal_type'])
    self.assertEqual(kw['reference'], parsed_string['reference'])
    self.assertEqual(kw['version'], parsed_string['version'])
    self.assertEqual(kw['language'], parsed_string['language'])
1012

1013 1014 1015 1016
    # only my docs
    kw['mine'] = 'yes'
    search_string = assemble(**kw)
    parsed_string = parse(search_string)
1017
    self.assertEqual('searchabletext_any "searchabletext_phrase1 searchabletext_phrase1"  +searchabletext_all1 +searchabletext_all2 -searchabletext_without1 -searchabletext_without2 created:1w AND (portal_type:Document) reference:Nxd-test version:001 language:en contributor_title:John mine:yes', \
1018 1019 1020 1021
                      search_string)
    self.assertSameSet(['searchabletext', 'creation_from', 'portal_type', 'reference', \
                        'version', 'language', 'contributor_title', 'mine'], \
                        parsed_string.keys())
1022 1023 1024 1025 1026
    self.assertEqual(kw['search_portal_type'], parsed_string['portal_type'])
    self.assertEqual(kw['reference'], parsed_string['reference'])
    self.assertEqual(kw['version'], parsed_string['version'])
    self.assertEqual(kw['language'], parsed_string['language'])
    self.assertEqual(kw['mine'], parsed_string['mine'])
1027 1028

    # only newest versions
1029 1030 1031
    kw['newest'] = 'yes'
    search_string = assemble(**kw)
    parsed_string = parse(search_string)
1032
    self.assertEqual('searchabletext_any "searchabletext_phrase1 searchabletext_phrase1"  +searchabletext_all1 +searchabletext_all2 -searchabletext_without1 -searchabletext_without2 created:1w AND (portal_type:Document) reference:Nxd-test version:001 language:en contributor_title:John mine:yes newest:yes', \
1033 1034 1035 1036
                      search_string)
    self.assertSameSet(['searchabletext', 'creation_from', 'portal_type', 'reference', \
                        'version', 'language', 'contributor_title', 'mine', 'newest'], \
                        parsed_string.keys())
1037 1038 1039 1040 1041 1042
    self.assertEqual(kw['search_portal_type'], parsed_string['portal_type'])
    self.assertEqual(kw['reference'], parsed_string['reference'])
    self.assertEqual(kw['version'], parsed_string['version'])
    self.assertEqual(kw['language'], parsed_string['language'])
    self.assertEqual(kw['mine'], parsed_string['mine'])
    self.assertEqual(kw['newest'], parsed_string['newest'])
1043 1044

    # search mode
1045 1046 1047
    kw['search_mode'] = 'in_boolean_mode'
    search_string = assemble(**kw)
    parsed_string = parse(search_string)
1048
    self.assertEqual('searchabletext_any "searchabletext_phrase1 searchabletext_phrase1"  +searchabletext_all1 +searchabletext_all2 -searchabletext_without1 -searchabletext_without2 created:1w AND (portal_type:Document) reference:Nxd-test version:001 language:en contributor_title:John mine:yes newest:yes mode:boolean', \
1049 1050 1051 1052
                      search_string)
    self.assertSameSet(['searchabletext', 'creation_from', 'portal_type', 'reference', \
                        'version', 'language', 'contributor_title', 'mine', 'newest', 'mode'], \
                        parsed_string.keys())
1053 1054 1055 1056 1057 1058 1059
    self.assertEqual(kw['search_portal_type'], parsed_string['portal_type'])
    self.assertEqual(kw['reference'], parsed_string['reference'])
    self.assertEqual(kw['version'], parsed_string['version'])
    self.assertEqual(kw['language'], parsed_string['language'])
    self.assertEqual(kw['mine'], parsed_string['mine'])
    self.assertEqual(kw['newest'], parsed_string['newest'])
    self.assertEqual('boolean', parsed_string['mode'])
1060

1061
    # search with multiple portal_type
1062
    kw = {'search_portal_type': ['Document','Presentation','Web Page'],
1063 1064 1065
           'searchabletext_any': 'erp5'}
    search_string = assemble(**kw)
    parsed_string = parse(search_string)
1066
    self.assertEqual('erp5 AND (portal_type:Document OR portal_type:Presentation OR portal_type:"Web Page")', \
1067 1068 1069
                      search_string)
    self.assertSameSet(['searchabletext', 'portal_type'], \
                        parsed_string.keys())
1070
    #self.assertEqual(kw['search_portal_type'], parsed_string['portal_type'])
1071

1072
    # parse with multiple portal_type containing spaces in one portal_type
1073
    search_string = 'erp5 AND (portal_type:Document OR portal_type:Presentation OR portal_type:"Web Page")'
1074
    parsed_string = parse(search_string)
1075
    self.assertEqual(parsed_string['portal_type'], ['Document','Presentation','"Web Page"'])
1076

Ivan Tyagov's avatar
Ivan Tyagov committed
1077
  def test_11_Base_getAdvancedSearchResultList(self):
1078
    """
Ivan Tyagov's avatar
Ivan Tyagov committed
1079
    Test search string search capabilities using Base_getAdvancedSearchResultList script.
1080 1081 1082 1083 1084 1085 1086 1087
    """
    portal = self.portal
    assemble = portal.Base_assembleSearchString
    search = portal.Base_getAdvancedSearchResultList

    def getAdvancedSearchStringResultList(**kw):
      search_string = assemble(**kw)
      return [x.getObject() for x in search(search_string)]
1088

1089 1090 1091 1092 1093 1094 1095 1096 1097
    # create some objects
    document_1 = portal.document_module.newContent(
                   portal_type = 'File',
                   description = 'standalone software linux python free',
                   version = '001',
                   language = 'en',
                   reference = 'nxd-test-doc-1')
    document_2 = portal.document_module.newContent(
                   portal_type = 'Presentation',
1098
                   description = 'standalone free python linux knowledge system management different',
1099 1100 1101 1102 1103 1104 1105 1106 1107
                   version = '002',
                   language = 'fr',
                   reference = 'nxd-test-doc-2')
    document_3 = portal.document_module.newContent(
                   portal_type = 'Presentation',
                   description = 'just a copy',
                   version = '003',
                   language = 'en',
                   reference = 'nxd-test-doc-2')
1108
    # multiple revisions of a Web Page
1109 1110
    web_page_1 = portal.web_page_module.newContent(
                   portal_type = 'Web Page',
1111
                   text_content = 'software based solutions document management product standalone owner different',
1112 1113 1114
                   version = '003',
                   language = 'jp',
                   reference = 'nxd-test-web-page-3')
1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133
    web_page_2 = portal.web_page_module.newContent(
                   portal_type = 'Web Page',
                   text_content = 'new revision (004) of nxd-test-web-page-3',
                   version = '004',
                   language = 'jp',
                   reference = 'nxd-test-web-page-3')
    web_page_3 = portal.web_page_module.newContent(
                   portal_type = 'Web Page',
                   text_content = 'new revision (005) of nxd-test-web-page-3',
                   version = '005',
                   language = 'jp',
                   reference = 'nxd-test-web-page-3')
    # publish documents so we can test searching within owned documents for an user
    for document in (document_1, document_2, document_3, web_page_1, web_page_2, web_page_3):
      document.publish()
    # create test Person objects and add pseudo local security
    person1 =  self.createUser(reference='user1')
    person1.setTitle('Another Contributor')
    portal.document_module.manage_setLocalRoles('user1', ['Assignor',])
1134
    self.tic()
1135 1136

    # login as another user
1137
    super(TestDocument, self).login('user1')
1138 1139 1140 1141 1142 1143
    document_4 = portal.document_module.newContent(
                   portal_type = 'Presentation',
                   description = 'owner different user contributing document',
                   version = '003',
                   language = 'bg',
                   reference = 'tlv-test-doc-1')
1144
    self.login()
1145 1146 1147 1148
    contributor_list = document_4.getContributorValueList()
    contributor_list.append(person1)
    document_4.setContributorValueList(contributor_list)
    document_4.publish()
1149
    self.tic()
1150 1151 1152 1153

    # search arbitrary word
    kw = {'searchabletext_any': 'software'}
    self.assertSameSet([document_1,web_page_1], getAdvancedSearchStringResultList(**kw))
1154

1155
    # exact word search
1156
    kw = {'searchabletext_any': '',
1157
          'searchabletext_phrase': 'linux python'}
1158
    self.assertSameSet([document_1], getAdvancedSearchStringResultList(**kw))
1159
    kw = {'searchabletext_any': '',
1160
          'searchabletext_phrase': 'python linux'}
1161
    self.assertSameSet([document_2], getAdvancedSearchStringResultList(**kw))
1162
    kw = {'searchabletext_any': '',
1163 1164
          'searchabletext_phrase': 'python linux knowledge system'}
    self.assertSameSet([document_2], getAdvancedSearchStringResultList(**kw))
1165

1166 1167 1168 1169
    # search "with all of the words" - each word prefixed by "+"
    kw = {'searchabletext_any': 'standalone',
          'searchabletext_all': 'python'}
    self.assertSameSet([document_1, document_2], getAdvancedSearchStringResultList(**kw))
1170

1171 1172 1173 1174
    # search without these words - every word prefixed by "-"
    kw = {'searchabletext_any': 'standalone',
          'searchabletext_without': 'python'}
    self.assertSameSet([web_page_1], getAdvancedSearchStringResultList(**kw))
1175

1176 1177 1178 1179
    # only given portal_types - add "type:Type" or type:(Type1,Type2...)
    kw = {'searchabletext_any': 'python',
          'search_portal_type': 'Presentation'}
    self.assertSameSet([document_2], getAdvancedSearchStringResultList(**kw))
1180 1181 1182 1183 1184 1185
    kw = {'searchabletext_any': 'python',
          'search_portal_type': 'File'}
    self.assertSameSet([document_1], getAdvancedSearchStringResultList(**kw))
    kw = {'searchabletext_any': 'management',
          'search_portal_type': 'File'}
    self.assertSameSet([], getAdvancedSearchStringResultList(**kw))
1186

1187
    # search by reference
1188
    kw = {'reference': document_2.getReference()}
1189 1190 1191 1192
    self.assertSameSet([document_2, document_3], getAdvancedSearchStringResultList(**kw))
    kw = {'searchabletext_any': 'copy',
          'reference': document_2.getReference()}
    self.assertSameSet([document_3], getAdvancedSearchStringResultList(**kw))
1193 1194
    kw = {'searchabletext_any': 'copy',
          'reference': document_2.getReference(),
1195
          'search_portal_type': 'File'}
1196
    self.assertSameSet([], getAdvancedSearchStringResultList(**kw))
1197

1198
    # search by version
1199
    kw = {'reference': document_2.getReference(),
1200
          'version': document_2.getVersion()}
1201
    self.assertSameSet([document_2], getAdvancedSearchStringResultList(**kw))
1202
    kw = {'reference': document_2.getReference(),
1203 1204 1205
          'version': document_2.getVersion(),
          'search_portal_type': 'File'}
    self.assertSameSet([], getAdvancedSearchStringResultList(**kw))
1206

1207
    # search by language
1208
    kw = {'reference': document_2.getReference(),
1209 1210
          'language': document_2.getLanguage()}
    self.assertSameSet([document_2], getAdvancedSearchStringResultList(**kw))
1211
    kw = {'reference': document_2.getReference(),
1212 1213
          'language': document_3.getLanguage()}
    self.assertSameSet([document_3], getAdvancedSearchStringResultList(**kw))
1214
    kw = {'reference': document_2.getReference(),
1215
          'language': document_3.getLanguage(),
1216
          'search_portal_type': 'File'}
1217
    self.assertSameSet([], getAdvancedSearchStringResultList(**kw))
1218

1219
    # only my docs
1220
    super(TestDocument, self).login('user1')
1221 1222 1223 1224 1225 1226 1227 1228
    kw = {'searchabletext_any': 'owner'}
    # should return all documents matching a word no matter if we're owner or not
    self.assertSameSet([web_page_1, document_4], getAdvancedSearchStringResultList(**kw))
    kw = {'searchabletext_any': 'owner',
          'mine': 'yes'}
    # should return ONLY our own documents matching a word
    self.assertSameSet([document_4], getAdvancedSearchStringResultList(**kw))
    self.login()
1229

1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243
    # only newest versions
    # should return ALL documents for a reference
    kw = {'reference': web_page_1.getReference()}
    self.assertSameSet([web_page_1, web_page_2, web_page_3], getAdvancedSearchStringResultList(**kw))
    # should return ONLY newest document for a reference
    kw = {'reference': web_page_1.getReference(),
          'newest': 'yes'}
    self.assertSameSet([web_page_3], getAdvancedSearchStringResultList(**kw))

    # contributor title search
    kw = {'searchabletext_any': 'owner'}
    # should return all documents matching a word no matter of contributor
    self.assertSameSet([web_page_1, document_4], getAdvancedSearchStringResultList(**kw))
    kw = {'searchabletext_any': 'owner',
1244
          'contributor_title': '%Contributor%'}
1245
    self.assertSameSet([document_4], getAdvancedSearchStringResultList(**kw))
1246 1247 1248 1249 1250

    # multiple portal_type specified
    kw = {'search_portal_type': 'File,Presentation'}
    self.assertSameSet([document_1, document_2, document_3, document_4], getAdvancedSearchStringResultList(**kw))

1251
    # XXX: search limited to a certain date range
1252 1253
    # XXX: search mode

1254 1255 1256 1257
  # &nbsp; and &#160; are equivalent, and "pdftohtml" can generate
  # either depending on the version of the "poppler" package used.
  re_html_nbsp = re.compile('&(nbsp|#160);')

1258 1259 1260
  def test_PDFTextContent(self):
    upload_file = makeFileUpload('REF-en-001.pdf')
    document = self.portal.portal_contributions.newContent(file=upload_file)
1261 1262
    self.assertEqual('PDF', document.getPortalType())
    self.assertEqual('I use reference to look up TEST\n',
1263
                      document._convertToText())
1264 1265
    html_data = re.sub(self.re_html_nbsp, ' ', document._convertToHTML())
    self.assert_('I use reference to look up TEST' in html_data)
1266 1267 1268
    self.assert_('I use reference to look up TEST' in
                 document.SearchableText())

1269 1270 1271
  def test_PDFToImage(self):
    upload_file = makeFileUpload('REF-en-001.pdf')
    document = self.portal.portal_contributions.newContent(file=upload_file)
1272
    self.assertEqual('PDF', document.getPortalType())
Nicolas Delaby's avatar
Nicolas Delaby committed
1273

1274
    content_type, image_data = document.convert(format='png',
Nicolas Delaby's avatar
Nicolas Delaby committed
1275 1276
                                                frame=0,
                                                display='thumbnail')
1277
    # it's a valid PNG
1278
    self.assertEqual('PNG', image_data[1:4])
Fabien Morin's avatar
Fabien Morin committed
1279

1280 1281 1282
  def test_PDF_content_information(self):
    upload_file = makeFileUpload('REF-en-001.pdf')
    document = self.portal.portal_contributions.newContent(file=upload_file)
1283
    self.assertEqual('PDF', document.getPortalType())
1284
    content_information = document.getContentInformation()
1285 1286 1287 1288
    self.assertEqual('1', content_information['Pages'])
    self.assertEqual('subject', content_information['Subject'])
    self.assertEqual('title', content_information['Title'])
    self.assertEqual('application/pdf', document.getContentType())
1289

1290 1291 1292
  def test_PDF_content_information_extra_metadata(self):
    # Extra metadata, such as those stored by pdftk update_info are also
    # available in document.getContentInformation()
1293
    upload_file = makeFileUpload('metadata.pdf', as_name='REF-en-001.pdf')
1294
    document = self.portal.portal_contributions.newContent(file=upload_file)
1295
    self.tic()
1296
    self.assertEqual('PDF', document.getPortalType())
1297
    content_information = document.getContentInformation()
1298 1299 1300
    self.assertEqual('the value', content_information['NonStandardMetadata'])
    self.assertEqual('1', content_information['Pages'])
    self.assertEqual('REF', document.getReference())
1301 1302

    # contribute file which will be merged to current document in synchronous mode
1303
    # and check content_type recalculated
1304 1305 1306
    upload_file = makeFileUpload('Forty-Two.Pages-en-001.pdf', as_name='REF-en-001.pdf')
    contributed_document = self.portal.Base_contribute(file=upload_file, \
                                                       synchronous_metadata_discovery=True)
1307
    self.tic()
1308
    content_information = contributed_document.getContentInformation()
1309

1310 1311 1312 1313 1314 1315 1316 1317 1318 1319
    # we should have same data, respectively same PDF pages
    self.assertEqual(contributed_document.getSize(), document.getSize())
    self.assertEqual(contributed_document.getContentInformation()['Pages'], \
                     document.getContentInformation()['Pages'])
    self.assertEqual('42', \
                     document.getContentInformation()['Pages'])

    # upload with another file and check content_type recalculated
    upload_file = makeFileUpload('REF-en-001.pdf')
    document.setFile(upload_file)
1320
    self.tic()
1321
    content_information = document.getContentInformation()
1322
    self.assertEqual('1', content_information['Pages'])
1323

1324 1325 1326 1327
  def test_empty_PDF_content_information(self):
    document = self.portal.document_module.newContent(portal_type='PDF')
    content_information = document.getContentInformation()
    # empty PDF have no content information
1328
    self.assertEqual({}, content_information)
1329

1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344
  def test_apple_PDF_metadata(self):
    # PDF created with Apple software have a special 'AAPL:Keywords' info tag
    # and when pypdf extracts pdf information, it is returned as an
    # IndirectObject instance which is not picklable
    document = self.portal.document_module.newContent(
      portal_type='PDF',
      file=makeFileUpload('apple_metadata.pdf'))
    # content_information is picklable
    content_information = document.getContentInformation()
    from pickle import dumps
    dumps(content_information)
    # so document can be saved in ZODB
    self.commit()
    self.tic()

Aurel's avatar
Aurel committed
1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356
  def test_upload_bad_pdf_file(self):
    """ Test that pypdf2 handle wrong formatted PDF """
    path = os.path.join(os.path.dirname(__file__), 'test_document',
      'FEUILLE BLANCHE.pdf')
    file_upload = FileUpload(path, 'FEUILLE BLANCHE.pdf')
    pdf = self.portal.document_module.newContent(
      portal_type='PDF',
      file=file_upload,
      title='Bad PDF')
    self.tic()
    pdf.share()
    self.tic()
1357
    self.assertEqual(pdf.getValidationState(), "shared")
Aurel's avatar
Aurel committed
1358 1359 1360
    result = pdf.getContentInformation()
    self.assertNotEquals(result, None)

1361 1362
  def test_PDF_content_content_type(self):
    upload_file = makeFileUpload('REF-en-001.pdf')
1363 1364
    document = self.portal.document_module.newContent(portal_type='PDF')
    # Here we use edit instead of setFile,
Nicolas Delaby's avatar
Nicolas Delaby committed
1365
    # because only edit method set filename as filename.
1366
    document.edit(file=upload_file)
1367
    self.assertEqual('application/pdf', document.getContentType())
1368

1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427
  def test_PDF_watermark(self):
    original_document = self.portal.portal_contributions.newContent(
      file=makeFileUpload('REF-en-001.pdf'))
    # This watermark.pdf document is a pdf with a transparent background. Such
    # document can be created using GIMP
    watermark_document = self.portal.portal_contributions.newContent(
      file=makeFileUpload('watermark.pdf'))
    watermarked_data = original_document.getWatermarkedData(
      watermark_data=watermark_document.getData(),
      repeat_watermark=False)

    # this looks like a pdf
    self.assertTrue(watermarked_data.startswith('%PDF-1.3'))

    # and ERP5 can make a PDF Document out of it
    watermarked_document = self.portal.document_module.newContent(
      portal_type='PDF',
      data=watermarked_data)
    self.assertEqual('1', watermarked_document.getContentInformation()['Pages'])
    self.assertNotEqual(original_document.getData(),
      watermarked_document.getData())

  def test_PDF_watermark_repeat(self):
    # watermark a pdf, repeating the watermark
    original_document = self.portal.portal_contributions.newContent(
      file=makeFileUpload('Forty-Two.Pages-en-001.pdf'))
    watermark_document = self.portal.portal_contributions.newContent(
      file=makeFileUpload('watermark.pdf'))
    watermarked_data = original_document.getWatermarkedData(
      watermark_data=watermark_document.getData(),
      repeat_watermark=True)

    self.assertTrue(watermarked_data.startswith('%PDF-1.3'))
    watermarked_document = self.portal.document_module.newContent(
      portal_type='PDF',
      data=watermarked_data)
    self.assertEqual('42', watermarked_document.getContentInformation()['Pages'])
    self.assertNotEqual(original_document.getData(),
      watermarked_document.getData())

  def test_PDF_watermark_start_page(self):
    # watermark a pdf, starting on the second page
    original_document = self.portal.portal_contributions.newContent(
      file=makeFileUpload('Forty-Two.Pages-en-001.pdf'))
    watermark_document = self.portal.portal_contributions.newContent(
      file=makeFileUpload('watermark.pdf'))
    watermarked_data = original_document.getWatermarkedData(
      watermark_data=watermark_document.getData(),
      repeat_watermark=False,
      watermark_start_page=1) # This is 0 based.

    self.assertTrue(watermarked_data.startswith('%PDF-1.3'))
    watermarked_document = self.portal.document_module.newContent(
      portal_type='PDF',
      data=watermarked_data)
    self.assertEqual('42', watermarked_document.getContentInformation()['Pages'])
    self.assertNotEqual(original_document.getData(),
      watermarked_document.getData())

Nicolas Delaby's avatar
Nicolas Delaby committed
1428
  def test_Document_getStandardFilename(self):
1429 1430 1431
    upload_file = makeFileUpload('metadata.pdf')
    document = self.portal.document_module.newContent(portal_type='PDF')
    document.edit(file=upload_file)
1432 1433
    self.assertEqual(document.getStandardFilename(), 'metadata.pdf')
    self.assertEqual(document.getStandardFilename(format='png'),
1434 1435 1436
                      'metadata.png')
    document.setVersion('001')
    document.setLanguage('en')
1437 1438
    self.assertEqual(document.getStandardFilename(), 'metadata-001-en.pdf')
    self.assertEqual(document.getStandardFilename(format='png'),
1439
                      'metadata-001-en.png')
1440 1441 1442 1443
    # check when format contains multiple '.'
    upload_file = makeFileUpload('TEST-en-003.odp')
    document = self.portal.document_module.newContent(portal_type='Presentation')
    document.edit(file=upload_file)
1444 1445
    self.assertEqual(document.getStandardFilename(), 'TEST-en-003.odp')
    self.assertEqual('TEST-en-003.odg', document.getStandardFilename(format='odp.odg'))
1446

1447

1448 1449 1450
  def test_CMYKImageTextContent(self):
    upload_file = makeFileUpload('cmyk_sample.jpg')
    document = self.portal.portal_contributions.newContent(file=upload_file)
1451 1452
    self.assertEqual('Image', document.getPortalType())
    self.assertEqual('ERP5 is a free software\n\n', document.asText())
1453 1454 1455 1456

  def test_MonochromeImageResize(self):
    upload_file = makeFileUpload('monochrome_sample.tiff')
    document = self.portal.portal_contributions.newContent(file=upload_file)
1457
    self.assertEqual('Image', document.getPortalType())
1458 1459 1460
    resized_image = document.convert(format='png', display='small')[1]
    identify_output = Popen(['identify', '-verbose', '-'], stdin=PIPE, stdout=PIPE).communicate(resized_image)[0]
    self.assertFalse('1-bit' in identify_output)
1461
    self.assertEqual('ERP5 is a free software\n\n', document.asText())
1462

1463 1464 1465
  def test_Base_showFoundText(self):
    # Create document with good content
    document = self.portal.document_module.newContent(portal_type='Drawing')
1466
    self.assertEqual('empty', document.getExternalProcessingState())
1467

1468
    upload_file = makeFileUpload('TEST-en-002.odt')
1469
    document.edit(file=upload_file)
1470
    self.tic()
1471
    self.assertEqual('converted', document.getExternalProcessingState())
1472

Nicolas Delaby's avatar
Nicolas Delaby committed
1473 1474
    # Delete base_data
    document.edit(base_data=None)
1475

Nicolas Delaby's avatar
Nicolas Delaby committed
1476
    # As document is not converted, text conversion is impossible
1477 1478
    self.assertRaises(NotConvertedError, document.asText)
    self.assertRaises(NotConvertedError, document.getSearchableText)
1479
    self.assertEqual('This document is not converted yet.',
1480
                      document.Base_showFoundText())
1481

1482 1483 1484
    # upload again good content
    upload_file = makeFileUpload('TEST-en-002.odt')
    document.edit(file=upload_file)
1485
    self.tic()
1486
    self.assertEqual('converted', document.getExternalProcessingState())
1487

1488
  def test_Base_contribute(self):
1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502
    """
      Test contributing a file and attaching it to context.
    """
    person = self.portal.person_module.newContent(portal_type='Person')
    contributed_document = person.Base_contribute(
                                     portal_type=None,
                                     title=None,
                                     reference=None,
                                     short_title=None,
                                     language=None,
                                     version=None,
                                     description=None,
                                     attach_document_to_context=True,
                                     file=makeFileUpload('TEST-en-002.odt'))
1503
    self.assertEqual('Text', contributed_document.getPortalType())
1504
    self.tic()
1505
    document_list = person.getFollowUpRelatedValueList()
1506
    self.assertEqual(1, len(document_list))
1507
    document = document_list[0]
1508 1509 1510 1511
    self.assertEqual('converted', document.getExternalProcessingState())
    self.assertEqual('Text', document.getPortalType())
    self.assertEqual('title', document.getTitle())
    self.assertEqual(contributed_document, document)
1512

1513
  def test_Base_contribute_empty(self):
Ivan Tyagov's avatar
Typo.  
Ivan Tyagov committed
1514
    """
1515 1516 1517
      Test contributing an empty file and attaching it to context.
    """
    person = self.portal.person_module.newContent(portal_type='Person')
1518 1519 1520 1521 1522 1523
    empty_file_upload = ZPublisher.HTTPRequest.FileUpload(FieldStorage(
                            fp=StringIO.StringIO(),
                            environ=dict(REQUEST_METHOD='PUT'),
                            headers={"content-disposition":
                              "attachment; filename=empty;"}))

1524 1525 1526 1527 1528 1529 1530 1531 1532 1533
    contributed_document = person.Base_contribute(
                                    portal_type=None,
                                    title=None,
                                    reference=None,
                                    short_title=None,
                                    language=None,
                                    version=None,
                                    description=None,
                                    attach_document_to_context=True,
                                    file=empty_file_upload)
1534
    self.tic()
1535
    document_list = person.getFollowUpRelatedValueList()
1536
    self.assertEqual(1, len(document_list))
1537
    document = document_list[0]
1538 1539
    self.assertEqual('File', document.getPortalType())
    self.assertEqual(contributed_document, document)
1540

1541
  def test_Base_contribute_forced_type(self):
1542 1543 1544 1545 1546 1547
    """Test contributing while forcing the portal type.
    """
    person = self.portal.person_module.newContent(portal_type='Person')
    contributed_document = person.Base_contribute(
                                     portal_type='PDF',
                                     file=makeFileUpload('TEST-en-002.odt'))
1548
    self.assertEqual('PDF', contributed_document.getPortalType())
1549

1550 1551 1552 1553 1554 1555 1556 1557
  def test_Base_contribute_input_parameter_dict(self):
    """Test contributing while entering input parameters.
    """
    person = self.portal.person_module.newContent(portal_type='Person')
    contributed_document = person.Base_contribute(
                                     title='user supplied title',
                                     file=makeFileUpload('TEST-en-002.pdf'))
    self.tic()
1558
    self.assertEqual('user supplied title', contributed_document.getTitle())
1559

1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577
  def test_HTML_to_ODT_conversion_keep_enconding(self):
    """This test perform an PDF conversion of HTML content
    then to plain text.
    Check that encoding remains.
    """
    web_page_portal_type = 'Web Page'
    string_to_test = 'éààéôù'
    web_page = self.portal.getDefaultModule(web_page_portal_type)\
          .newContent(portal_type=web_page_portal_type)
    html_content = '<p>%s</p>' % string_to_test
    web_page.edit(text_content=html_content)
    mime_type, pdf_data = web_page.convert('pdf')
    text_content = self.portal.portal_transforms.\
                                      convertToData('text/plain',
                                          str(pdf_data),
                                          object=web_page, context=web_page,
                                          filename='test.pdf')
    self.assertTrue(string_to_test in text_content)
1578

1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597
  def test_HTML_to_ODT_conversion_keep_related_image_list(self):
    """This test create a Web Page and an Image.
    HTML content of Web Page referred to that Image with it's reference.
    Check that ODT conversion of Web Page embed image data.
    """
    # create web page
    web_page_portal_type = 'Web Page'
    web_page = self.portal.getDefaultModule(web_page_portal_type)\
          .newContent(portal_type=web_page_portal_type)
    image_reference = 'MY-TESTED-IMAGE'
    # Target image with it reference only
    html_content = '<p><img src="%s"/></p>' % image_reference
    web_page.edit(text_content=html_content)

    # Create image
    image_portal_type = 'Image'
    image = self.portal.getDefaultModule(image_portal_type)\
          .newContent(portal_type=image_portal_type)

1598
    # edit content and publish it
1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611
    upload_file = makeFileUpload('cmyk_sample.jpg')
    image.edit(reference=image_reference,
               version='001',
               language='en',
               file=upload_file)
    image.publish()

    self.tic()

    # convert web_page into odt
    mime_type, odt_archive = web_page.convert('odt')
    builder = OOoBuilder(odt_archive)
    image_count = builder._image_count
1612 1613
    failure_message = 'Expected image not found in ODF zipped archive'
    # fetch image from zipped archive content then compare with ERP5 Image
1614
    self.assertEqual(builder.extract('Pictures/%s.jpg' % image_count),
1615
                      image.getData(), failure_message)
1616

1617 1618 1619
    # Continue the test with image resizing support
    image_display = 'large'
    # Add url parameters
1620
    html_content = '<p><img src="%s?format=jpeg&amp;display=%s&amp;quality=75"/></p>' % \
1621 1622 1623 1624 1625 1626 1627 1628 1629
                                              (image_reference, image_display)
    web_page.edit(text_content=html_content)
    mime_type, odt_archive = web_page.convert('odt')
    builder = OOoBuilder(odt_archive)
    image_count = builder._image_count
    # compute resized image for comparison
    mime, converted_image = image.convert(format='jpeg', display=image_display)
    # fetch image from zipped archive content
    # then compare with resized ERP5 Image
1630
    self.assertEqual(builder.extract('Pictures/%s.jpeg' % image_count),
1631 1632
                      converted_image, failure_message)

1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651
    # Let's continue with Presentation Document as embbeded image
    document = self.portal.document_module.newContent(portal_type='Presentation')
    upload_file = makeFileUpload('TEST-en-003.odp')
    image_reference = 'IMAGE-odp'
    document.edit(file=upload_file, reference=image_reference)
    document.publish()
    self.tic()
    html_content = '<p><img src="%s?format=png&amp;display=%s&amp;quality=75"/></p>' % \
                                              (image_reference, image_display)
    web_page.edit(text_content=html_content)
    mime_type, odt_archive = web_page.convert('odt')
    builder = OOoBuilder(odt_archive)
    image_count = builder._image_count
    # compute resized image for comparison
    mime, converted_image = document.convert(format='png',
                                             display=image_display,
                                             quality=75)
    # fetch image from zipped archive content
    # then compare with resized ERP5 Image
1652
    self.assertEqual(builder.extract('Pictures/%s.png' % image_count),
1653 1654 1655
                      converted_image, failure_message)


1656 1657 1658 1659 1660 1661 1662 1663
  def test_addContributorToDocument(self):
    """
      Test if current authenticated user is added to contributor list of document
      (only if authenticated user is an ERP5 Person object)
    """
    portal = self.portal
    document_module = portal.document_module

1664
    # create Person objects and add pseudo local security
1665 1666 1667 1668
    person1 =  self.createUser(reference='contributor1')
    document_module.manage_setLocalRoles('contributor1', ['Assignor',])
    person2 =  self.createUser(reference='contributor2')
    document_module.manage_setLocalRoles('contributor2', ['Assignor',])
1669
    self.tic()
1670 1671

    # login as first one
1672
    super(TestDocument, self).login('contributor1')
1673
    doc = document_module.newContent(portal_type='File',
1674
                                     title='Test1')
1675
    self.tic()
1676
    self.login()
1677
    self.assertSameSet([person1],
1678 1679 1680
                       doc.getContributorValueList())

    # login as second one
1681
    super(TestDocument, self).login('contributor2')
1682
    doc.edit(title='Test2')
1683
    self.tic()
1684
    self.login()
1685
    self.assertSameSet([person1, person2],
1686 1687 1688 1689 1690
                       doc.getContributorValueList())

    # editing with non ERP5 Person object, nothing added to contributor
    self.login()
    doc.edit(title='Test3')
1691
    self.tic()
1692
    self.assertSameSet([person1, person2],
1693
                       doc.getContributorValueList())
1694

1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706
  def test_safeHTML_conversion(self):
    """This test create a Web Page and test asSafeHTML conversion.
    Test also with a very non well-formed html document
    to stress conversion engine.
    """
    # create web page
    web_page_portal_type = 'Web Page'
    module = self.portal.getDefaultModule(web_page_portal_type)
    web_page = module.newContent(portal_type=web_page_portal_type)

    html_content = """<html>
      <head>
1707 1708
        <meta http-equiv="refresh" content="5;url=http://example.com/"/>
        <meta http-equiv="Set-Cookie" content=""/>
1709
        <title>My dirty title</title>
1710 1711 1712
        <style type="text/css">
          a {color: #FFAA44;}
        </style>
Kazuhiko Shiozaki's avatar
Kazuhiko Shiozaki committed
1713
        <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/>
1714 1715 1716 1717 1718 1719
      </head>
      <body>
        <div>
          <h1>My splendid title</h1>
        </div>
        <script type="text/javascript" src="http://example.com/something.js"/>
1720 1721 1722 1723
        <script type="text/javascript">
          alert("da");
        </script>
        <a href="javascript:DosomethingNasty()">Link</a>
1724
        <a onclick="javascript:DosomethingNasty()">Another Link</a>
1725
        <p>éàèù</p>
1726
        <p class="Th&#232;mes Thèmes">Th&#232;mes Thèmes</p>
1727
        <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAIAAAACtmMCAAAGmklEQVRIiYXWSYwcVxkH8O+9V6+qurbepmfs8WQyduxgJ4AnyLEdCyl2HBEJriwSixB3jggkfIjEARFxCRyAwA0hEYKMFBSioCCQjSNvsbzEdvAy45lkxtPT3dPd1bW8V2/lYEtIkVH+9/9P3+mvDxVFAf8n40k1SittkFKgtTXW+C6iFPseacSu7zuPbKFHiv0tvrZRpFlhjQkDHwGilFCXAFgAsMbyotC8nF9ob9/R+RSxYHLlo1xZggCEkkYpz6WiEpub62C1MSpOGq3mVDlJyzwTUtTr8eIXdgeB92hxNObnLt1tNzthFCFkpVYVZ6vLt86dOfXBtUt5PuGsqjfazx87fujgc54XamOkEBjZA88+OdVpfFIcp/z8xaVxOm61Op3pju95RTF556033v373/q9nlRKKWON1lq5bu1z+xe/+e3vNZtt0CIbjxljL3zpuW3bOwBATpw4AQDDYXr631eGg25VZul4pLQCBO+f++c/3j45Ho4xwsZYa7S1BiFsrb2/vsbLbNf8TJkNgbi0lqysdhd2zrrUwQ8OPPPeNc5Fs729MzMfRQ3G2OrSrRtXzhGwgUfjwI1DjziYEGKMEUIorS6cP3f71odSKsYZr5iU1elTlwEAA8Dt26tZxrQSgDB1a0GUBGGUpaPN7kZZybwssFXYKGu01YqAIUgbrSZ5trS8QqhPCGHFmCAtWLa+tukAwNmz72upiyJjZVZvzIRxExGc55PBKB1ujUMsP78wu3J/OErzkOIgcCptR6yqtF1auXcwz+Oo7jpOkaXZiJ8/PXb6g7TISyOZ70esSD3q14IoqMVgNEa4GQVfPba4b7516sIHg9FEK12jOOcVQdjBpuIyrjcBXLBEGzbeGuR56qytb9Wb2wcbd5FRCAznudHS8/xdncbibCtuJUcPPTPsrUvOAwfvfmK25HLENps+KiqBMAbsuV5oijRO2hiRvJw4lYAwSvIwMaryXNdoyYuM82JbI9q/I2EeTXwwob+4e2737La9T3/m7KWbm4MUYRTHwWMLO6QoMXE4mwAiylqHAPnOd7+vlDRKa608P/BqMSaEc54k8Y7QTndiSqAqsnQ4HA9TjJ3l5XUpqlbkCwtx4rNyODP7hF9LXNenrtfv3XOUgiSpS86sloAspR71AmtMrmm3tPMJohQqlo8nhR9G712+fvn2eiPwpKpKYfv9npQqmbq577MHBeP1uD49PYd7vR5jIojiqN70/AAZ6Tm4UW8KqV87+e7bZ64xVnDJe3lZCa1KTTEphMqlDRuNOAytVbduXsrSrVazwVhJECXPH32RM04wppQiQIJn1to4aVqAe/dWsJosTAdCVkHouwRfvb024cIg6/hee2bKdVDJi8FggAAlcRQnzbjeJt/4+reySTYeDpBVfhAQgl3XrzdbtaDW6Mwb68l81Ot32502xmSYFhPGLXFqzamCM6OlFzbGBR8O1nrdZQzG931n58JjV0fXaz5Nh/eN4UncotShFEou7nYnK1vJxY2ou5wSfcdokRfccUhUj5AfV8WE55kvAQMN4qmsVP+5cTEddZ09exZW7n1sjPaITrc2A79Gw1Cw8vqFC+MP70DKdF5Z5K0NdF5OMDaUkAqwQ7uuSzNZWYv2Pv3s/J7FkrOlmxc2eiOn2YzKrNvf3HCQRoB760vZuNcbZO/89U1VZDUMDlhPmchRkhKutJLaTti2dj32qeckCEgcuBRVRpZhGIHOHQDYu3fn2X+9VQmpLVXG6fbT+xv9vMgJRsgYCsZDoK1BRhOLDICQemVjFEe10POV1Cu3ri7fubKVi5pHf/DDH2EAOPzFo9ppXr8zunKze/nG6urHm0WeaSWU1MpAadBImcrYhkNnPbdNcYyRZnxrKyOOhzByCKLIYF12pjpHj7/0cMPX1u5/5ctfGw1HYMFoq7VCCADAWoswAgxaCQ/BlOcGhEitlTW1KNi+axphqawGbLyg9uov/zAzM/twcefmZl/77S+01qJiUnFjtDHGGGOttdYCEELcyqKuEF0h+1JhTPbOPQ40LImEUNpA//SV38zMzD5c3Ac5fPjAn0/+PklCCwrQQ8sisADWggWMEAFEJMG5VkybmpC7m3OCTVdV8PKPf7Vz4ckHzv9EADhy5NBf3vzTU0/tAzCADCB46FoN1gDC1oIFizEeSfFRf9CS8oUjx37369cX9x/4lA/g9T++8fNXXl1b3wBAAIAQAkCAEIAGMMgia83+XY//7CcvP/PS8U90/wv0LRSL/rwEwgAAAABJRU5ErkJggg==" />
1728 1729
      </body>
    </html>
1730
    """.decode('utf-8').encode('iso-8859-1')
1731 1732
    # content encoded into another codec
    # than utf-8 comes from necessarily an external file
1733
    # (Ingestion, or FileField), not from user interface
1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744
    # which is always utf-8.
    # Edit web_page with a file to force conversion to base_format
    # as it is done in reality

    # Mimic the behaviour of a FileUpload from WebPage_view
    file_like = StringIO.StringIO()
    file_like.write(html_content)
    setattr(file_like, 'filename', 'something.htm')
    web_page.edit(file=file_like)
    # run conversion to base format
    self.tic()
1745

1746
    # Check that outputted stripped html is safe
1747
    safe_html = web_page.asStrippedHTML()
1748 1749 1750
    self.assertTrue('My splendid title' in safe_html)
    self.assertTrue('script' not in safe_html, safe_html)
    self.assertTrue('something.js' not in safe_html, safe_html)
1751 1752 1753 1754
    self.assertTrue('<body>' not in safe_html)
    self.assertTrue('<head>' not in safe_html)
    self.assertTrue('<style' not in safe_html)
    self.assertTrue('#FFAA44' not in safe_html)
1755 1756
    self.assertTrue('5;url=http://example.com/' not in safe_html)
    self.assertTrue('Set-Cookie' not in safe_html)
1757 1758 1759
    self.assertTrue('javascript' not in safe_html)
    self.assertTrue('alert("da");' not in safe_html)
    self.assertTrue('javascript:DosomethingNasty()' not in safe_html)
1760
    self.assertTrue('onclick' not in safe_html)
1761 1762
    self.assertTrue('<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAIAAAACtmMCAAAG' in safe_html)
    self.assertTrue('7CcvP/PS8U90/wv0LRSL/rwEwgAAAABJRU5ErkJggg=="' in safe_html)
1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773

    # Check that outputed entire html is safe
    entire_html = web_page.asEntireHTML()
    self.assertTrue('My splendid title' in entire_html)
    self.assertTrue('script' not in entire_html, entire_html)
    self.assertTrue('something.js' not in entire_html, entire_html)
    self.assertTrue('<title>' in entire_html)
    self.assertTrue('<body>' in entire_html)
    self.assertTrue('<head>' in entire_html)
    self.assertTrue('<style' in entire_html)
    self.assertTrue('#FFAA44' in entire_html)
1774
    self.assertTrue('charset=utf-8' in entire_html)
1775 1776 1777
    self.assertTrue('javascript' not in entire_html)
    self.assertTrue('alert("da");' not in entire_html)
    self.assertTrue('javascript:DosomethingNasty()' not in entire_html)
1778
    self.assertTrue('onclick' not in entire_html)
1779 1780
    self.assertTrue('<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAIAAAACtmMCAAAG' in safe_html)
    self.assertTrue('7CcvP/PS8U90/wv0LRSL/rwEwgAAAABJRU5ErkJggg=="' in safe_html)
1781 1782

    # now check converted value is stored in cache
1783
    format = 'html'
1784 1785 1786 1787 1788
    self.assertTrue(web_page.hasConversion(format=format))
    web_page.edit(text_content=None)
    self.assertFalse(web_page.hasConversion(format=format))

    # test with not well-formed html document
1789
    html_content = r"""
1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801
    <HTML dir=3Dltr><HEAD>=0A=
<META http-equiv=3DContent-Type content=3D"text/html; charset=3Dunicode">=0A=
<META content=3D"DIRTYHTML 6.00.2900.2722" name=3DGENERATOR></HEAD>=0A=

<BODY>=0A=
<DIV><FONT face=3D"Times New Roman" color=3D#000000 size=3D3>blablalba</FONT></DIV>=0A=
<DIV>&nbsp;</DIV>=0A=
<DIV></DIV>=0A=
<DIV>&nbsp;</DIV>=0A=
<DIV>&nbsp;</DIV>=0A=
<DIV>&nbsp;</DIV>=0A=
<br>=
1802 1803 1804 1805 1806
<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\\=
" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">=
=0A<html xmlns=3D\"http://www.w3.org/1999/xhtml\">=0A<head>=0A<m=
eta http-equiv=3D\"Content-Type\" content=3D\"text/html; c=
harset=3Diso-8859-1\" />=0A<style type=3D\"text/css\">=0A<=
1807 1808
!--=0A.style1 {font-size: 8px}=0A.style2 {font-family: Arial, Helvetica, san=
s-serif}=0A.style3 {font-size: 8px; font-family: Arial, Helvetica, sans-seri=
1809 1810
f; }=0A-->=0A</style>=0A</head>=0A=0A<body>=0A<div>=0A  <p><span class=3D\=
\"style1\"><span class=3D\"style2\"><strong>I'm inside very broken HTML code</strong><br />=0A    ERP5<br />=0A
1811 1812
ERP5
<br />=0A    =
1813 1814
</span></span></p>=0A  <p class=3D\"sty=
le3\">ERP5:<br />=0A   </p>=0A  <p class=3D\"style3\"><strong>ERP5</strong>=
1815 1816 1817 1818

<br />=0A    ERP5</p>=0A</di=
v>=0A</body>=0A</html>=0A
<br>=
Nicolas Delaby's avatar
Nicolas Delaby committed
1819 1820
<!-- This is a comment, This string AZERTYY shouldn't be dislayed-->
<style>
1821
<!-- a {color: #FFAA44;} -->
Nicolas Delaby's avatar
Nicolas Delaby committed
1822
</style>
1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834
<table class=3DMoNormalTable border=3D0 cellspacing=3D0 cellpadding=3D0 =
width=3D64
 style=3D'width:48.0pt;margin-left:-.75pt;border-collapse:collapse'>
 <tr style=3D'height:15.0pt'>
  <td width=3D64 nowrap valign=3Dbottom =
style=3D'width:48.0pt;padding:0cm 5.4pt 0cm 5.4pt;
  height:15.0pt'>
  <p class=3DMoNormal><span =
style=3D'color:black'>05D65812<o:p></o:p></span></p>
  </td>
 </tr>
</table>
1835 1836 1837
<script LANGUAGE="JavaScript" type="text/javascript">
document.write('<sc'+'ript type="text/javascript" src="http://somosite.bg/utb.php"></sc'+'ript>');
</script>
1838
<p class="Th&#232;mes">Th&#232;mes</p>
1839 1840 1841
</BODY></HTML>
    """
    web_page.edit(text_content=html_content)
1842
    safe_html = web_page.asStrippedHTML()
1843 1844
    self.assertTrue('inside very broken HTML code' in safe_html)
    self.assertTrue('AZERTYY' not in safe_html)
1845
    self.assertTrue('#FFAA44' not in safe_html)
1846

1847 1848 1849 1850 1851
    filename = 'broken_html.html'
    file_object = makeFileUpload(filename)
    web_page.edit(file=file_object)
    converted = web_page.convert('html')[1]

Nicolas Delaby's avatar
Nicolas Delaby committed
1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866
  def test_safeHTML_impossible_conversion(self):
    """Some html are not parsable.
    """
    web_page_portal_type = 'Web Page'
    module = self.portal.getDefaultModule(web_page_portal_type)
    web_page = module.newContent(portal_type=web_page_portal_type)
    # very dirty html
    html_content = """
    <html>
      <body>
        <p><a href="http://www.example.com/category/html/" style="font-weight: bold; color: rgb(0, 0, 0); font-size: 90.8777%; text-decoration: none;" title="catégorie how to write valid html d" alt="Diancre pas d" accord="" :="" 6="" articles="">Its french</a></p>
      </body>
    </html>
"""
    web_page.edit(text_content=html_content)
1867
    from HTMLParser import HTMLParseError
Nicolas Delaby's avatar
Nicolas Delaby committed
1868 1869
    try:
      web_page.asStrippedHTML()
1870 1871 1872
    except HTMLParseError:
      expectedFailure(self.fail)(
        'Even BeautifulSoup is not able to parse such HTML')
Nicolas Delaby's avatar
Nicolas Delaby committed
1873

1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893
  def test_safeHTML_unknown_codec(self):
    """Some html declare unknown codecs.
    """
    web_page_portal_type = 'Web Page'
    module = self.portal.getDefaultModule(web_page_portal_type)
    web_page = module.newContent(portal_type=web_page_portal_type)

    html_content = """
    <html>
      <head>
        <meta http-equiv="Content-Type" content="text/html; charset=unicode" />
        <title>BLa</title>
      </head>
      <body><p> blablabla</p></body>
    </html>"""
    web_page.edit(text_content=html_content)
    safe_html = web_page.convert('html')[1]
    self.assertTrue('unicode' not in safe_html)
    self.assertTrue('utf-8' in safe_html)

1894 1895
  def test_parallel_conversion(self):
    """Check that conversion engine is able to fill in
Arnaud Fontaine's avatar
Arnaud Fontaine committed
1896
    cache without overwriting previous conversion
1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907
    when processed at the same time.
    """
    portal_type = 'PDF'
    document_module = self.portal.getDefaultModule(portal_type)
    document = document_module.newContent(portal_type=portal_type)

    upload_file = makeFileUpload('Forty-Two.Pages-en-001.pdf')
    document.edit(file=upload_file)
    pages_number = int(document.getContentInformation()['Pages'])
    self.tic()

1908 1909 1910 1911 1912 1913 1914 1915
    preference_tool = getToolByName(self.portal, 'portal_preferences')
    image_size = preference_tool.getPreferredThumbnailImageHeight(),\
                              preference_tool.getPreferredThumbnailImageWidth()
    convert_kw = {'format': 'png',
                  'quality': 75,
                  'display': 'thumbnail',
                  'resolution': None}

1916
    class ThreadWrappedConverter(Thread):
Arnaud Fontaine's avatar
Arnaud Fontaine committed
1917
      """Use this class to run different conversions
1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930
      inside distinct Thread.
      """
      def __init__(self, publish_method, document_path,
                   frame_list, credential):
        self.publish_method = publish_method
        self.document_path = document_path
        self.frame_list = frame_list
        self.credential = credential
        Thread.__init__(self)

      def run(self):
        for frame in self.frame_list:
          # Use publish method to dispatch conversion among
1931 1932 1933 1934
          # all available ZServer threads.
          convert_kw['frame'] = frame
          response = self.publish_method(self.document_path,
                                         basic=self.credential,
1935
                                         extra=convert_kw.copy())
1936 1937 1938 1939

          assert response.getHeader('content-type') == 'image/png', \
                                             response.getHeader('content-type')
          assert response.getStatus() == httplib.OK
1940 1941 1942 1943

    # assume there is no password
    credential = '%s:' % (getSecurityManager().getUser().getId(),)
    tested_list = []
Arnaud Fontaine's avatar
Arnaud Fontaine committed
1944
    frame_list = range(pages_number)
1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961
    # assume that ZServer is configured with 4 Threads
    conversion_per_tread = pages_number / 4
    while frame_list:
      local_frame_list = [frame_list.pop() for i in\
                            xrange(min(conversion_per_tread, len(frame_list)))]
      instance = ThreadWrappedConverter(self.publish, document.getPath(),
                                        local_frame_list, credential)
      tested_list.append(instance)
      instance.start()

    # Wait until threads finishing
    [tested.join() for tested in tested_list]

    self.tic()

    convert_kw = {'format': 'png',
                  'quality': 75,
1962
                  'display': 'thumbnail',
1963 1964
                  'resolution': None}

1965 1966 1967
    result_list = []
    for i in xrange(pages_number):
      # all conversions should succeeded and stored in cache storage
1968
      convert_kw['frame'] = i
1969 1970
      if not document.hasConversion(**convert_kw):
        result_list.append(i)
1971
    self.assertEqual(result_list, [])
1972

1973
  def test_conversionCache_reseting(self):
1974
    """Check that modifying a document with edit method,
1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985
    compute a new cache key and refresh cached conversions.
    """
    web_page_portal_type = 'Web Page'
    module = self.portal.getDefaultModule(web_page_portal_type)
    web_page = module.newContent(portal_type=web_page_portal_type)
    html_content = """<html>
      <head>
        <title>My dirty title</title>
        <style type="text/css">
          a {color: #FFAA44;}
        </style>
Kazuhiko Shiozaki's avatar
Kazuhiko Shiozaki committed
1986
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004
     </head>
      <body>
        <div>
          <h1>My splendid title</h1>
        </div>
        <script type="text/javascript" src="http://example.com/something.js"/>
      </body>
    </html>
    """
    web_page.edit(text_content=html_content)
    web_page.convert(format='txt')
    self.assertTrue(web_page.hasConversion(format='txt'))
    web_page.edit(title='Bar')
    self.assertFalse(web_page.hasConversion(format='txt'))
    web_page.convert(format='txt')
    web_page.edit()
    self.assertFalse(web_page.hasConversion(format='txt'))

Nicolas Delaby's avatar
Nicolas Delaby committed
2005 2006 2007 2008 2009 2010 2011 2012
  def test_TextDocument_conversion_to_base_format(self):
    """Check that any files is converted into utf-8
    """
    web_page_portal_type = 'Web Page'
    module = self.portal.getDefaultModule(web_page_portal_type)
    upload_file = makeFileUpload('TEST-text-iso8859-1.txt')
    web_page = module.newContent(portal_type=web_page_portal_type,
                                 file=upload_file)
Nicolas Delaby's avatar
Nicolas Delaby committed
2013
    self.tic()
Nicolas Delaby's avatar
Nicolas Delaby committed
2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028
    text_content = web_page.getTextContent()
    my_utf_eight_token = 'ùééàçèîà'
    text_content = text_content.replace('\n', '\n%s\n' % my_utf_eight_token)
    web_page.edit(text_content=text_content)
    self.assertTrue(my_utf_eight_token in web_page.asStrippedHTML())
    self.assertTrue(isinstance(web_page.asEntireHTML().decode('utf-8'), unicode))

  def test_PDFDocument_asTextConversion(self):
    """Test a PDF document with embedded images
    To force usage of Ocropus portal_transform chain
    """
    portal_type = 'PDF'
    module = self.portal.getDefaultModule(portal_type)
    upload_file = makeFileUpload('TEST.Embedded.Image.pdf')
    document = module.newContent(portal_type=portal_type, file=upload_file)
2029
    self.assertEqual(document.asText(), 'ERP5 is a free software.\n\n')
Nicolas Delaby's avatar
Nicolas Delaby committed
2030

2031
  def createRestrictedSecurityHelperScript(self):
2032
    script_content_list = ['format=None, **kw', """
2033 2034 2035
if not format:
  return 0
return 1
2036 2037 2038 2039
"""]
    for script_id in self.conversion_format_permission_script_id_list:
      createZODBPythonScript(self.getPortal().portal_skins.custom,
      script_id, *script_content_list)
2040
      self.commit()
2041 2042

  def _test_document_conversion_to_base_format_no_original_format_access(self,
Nicolas Delaby's avatar
Nicolas Delaby committed
2043
      portal_type, filename):
2044
    module = self.portal.getDefaultModule(portal_type)
Nicolas Delaby's avatar
Nicolas Delaby committed
2045
    upload_file = makeFileUpload(filename)
2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086
    document = module.newContent(portal_type=portal_type,
                                 file=upload_file)

    self.tic()

    self.createRestrictedSecurityHelperScript()

    from AccessControl import Unauthorized
    # check that it is not possible to access document in original format
    self.assertRaises(Unauthorized, document.convert, format=None)
    # check that it is possible to convert document to text format
    dummy = document.convert(format='text')

  def test_WebPage_conversion_to_base_format_no_original_format_access(self):
    """Checks Document.TextDocument"""
    self._test_document_conversion_to_base_format_no_original_format_access(
      'Web Page',
      'TEST-text-iso8859-1.txt'
    )

  def test_PDF_conversion_to_base_format_no_original_format_access(self):
    """Checks Document.PDFDocument"""
    self._test_document_conversion_to_base_format_no_original_format_access(
      'PDF',
      'TEST-en-002.pdf'
    )

  def test_Text_conversion_to_base_format_no_original_format_access(self):
    """Checks Document.OOoDocument"""
    self._test_document_conversion_to_base_format_no_original_format_access(
      'Text',
      'TEST-en-002.odt'
    )

  def test_Image_conversion_to_base_format_no_original_format_access(self):
    """Checks Document.Image"""
    self._test_document_conversion_to_base_format_no_original_format_access(
      'Image',
      'TEST-en-002.png'
    )

2087 2088 2089 2090
  def test_getExtensibleContent(self):
    """
      Test extensible content of some DMS types. As this is possible only on URL traversal use publish.
    """
2091 2092 2093 2094
    # Create a root level zope user
    root_user_folder = self.getPortalObject().aq_parent.acl_users
    if not root_user_folder.getUser('zope_user'):
      root_user_folder._doAddUser('zope_user', '', ['Manager',], [])
2095
      self.commit()
2096 2097 2098 2099
    # Create document with good content
    document = self.portal.document_module.newContent(portal_type='Presentation')
    upload_file = makeFileUpload('TEST-en-003.odp')
    document.edit(file=upload_file)
2100
    self.tic()
2101
    self.assertEqual('converted', document.getExternalProcessingState())
2102
    for object_url in ('img1.html', 'img2.html', 'text1.html', 'text2.html'):
2103 2104 2105 2106 2107 2108 2109 2110 2111
      for credential in ['ERP5TypeTestCase:', 'zope_user:']:
        response = self.publish('%s/%s' %(document.getPath(), object_url),
                                basic=credential)
        self.assertTrue('Status: 200 OK' in response.getOutput())
        # OOod produced HTML navigation, test it
        self.assertTrue('First page' in response.getBody())
        self.assertTrue('Back' in response.getBody())
        self.assertTrue('Continue' in response.getBody())
        self.assertTrue('Last page' in response.getBody())
2112

2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124
  def test_getTargetFormatItemList(self):
    """
     Test getting target conversion format item list.
     Note: this tests assumes the default formats do exists for some content types.
     as this is a matter of respective oinfiguration of mimetypes_registry & portal_transforms
     only the basic minium of transorm to formats is tested.
    """
    portal_type = 'PDF'
    module = self.portal.getDefaultModule(portal_type)

    upload_file = makeFileUpload('TEST.Large.Document.pdf')
    pdf = module.newContent(portal_type=portal_type, file=upload_file)
2125

2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136
    self.assertTrue('html' in pdf.getTargetFormatList())
    self.assertTrue('png' in pdf.getTargetFormatList())
    self.assertTrue('txt' in pdf.getTargetFormatList())

    web_page=self.portal.web_page_module.newContent(portal_type='Web Page',
                                                    content_type='text/html')
    self.assertTrue('odt' in web_page.getTargetFormatList())
    self.assertTrue('txt' in web_page.getTargetFormatList())

    image=self.portal.image_module.newContent(portal_type='Image',
                                                    content_type='image/png')
2137
    self.assertTrue(image.getTargetFormatList())
2138

2139 2140 2141
    # test Not converted (i.e. empty) OOoDocument instances
    presentation=self.portal.document_module.newContent(portal_type='Presentation')
    self.assertSameSet([], presentation.getTargetFormatList())
2142

2143 2144 2145
    # test uploading some data
    upload_file = makeFileUpload('Foo_001.odg')
    presentation.edit(file=upload_file)
2146
    self.tic()
2147 2148 2149
    self.assertTrue('odg' in presentation.getTargetFormatList())
    self.assertTrue('jpg' in presentation.getTargetFormatList())
    self.assertTrue('png' in presentation.getTargetFormatList())
2150

2151
  def test_convertWebPageWithEmbeddedZODBImageToImageOnTraversal(self):
2152
    """
Ivan Tyagov's avatar
Ivan Tyagov committed
2153 2154
    Test Web Page conversion to image using embedded Images into its HTML body.
    Test various dumb ways to include an image (relative to instance or external ones).
2155
    """
Ivan Tyagov's avatar
Ivan Tyagov committed
2156
    display= 'thumbnail'
2157 2158
    convert_kw = {'display':display,
                  'format':'jpeg',
2159
                  'quality':100}
2160
    preffered_size_for_display = self.getPreferences(display)
2161
    web_page_document = self.portal.web_page_module.newContent(portal_type="Web Page")
2162 2163
    # use ERP5's favourite.png"
    web_page_document.setTextContent('<b> test </b><img src="images/favourite.png"/>')
2164
    self.tic()
2165

2166 2167
    web_page_document_url = '%s/%s' %(self.portal.absolute_url(), web_page_document.getRelativeUrl())
    web_page_image_size, web_page_file_size = self.getURLSizeList(web_page_document_url, **convert_kw)
Ivan Tyagov's avatar
Ivan Tyagov committed
2168
    self.assertTrue(max(preffered_size_for_display) - max(web_page_image_size) <= 1)
2169

Ivan Tyagov's avatar
Ivan Tyagov committed
2170
    # images from same instance accessed by reference and wrong conversion arguments (dispay NOT display)
Ivan Tyagov's avatar
Ivan Tyagov committed
2171 2172 2173 2174 2175 2176
    # code should be more resilient
    upload_file = makeFileUpload('cmyk_sample.jpg')
    image = self.portal.image_module.newContent(portal_type='Image',
                                               reference='Embedded-XXX',
                                               version='001',
                                               language='en')
Ivan Tyagov's avatar
Ivan Tyagov committed
2177
    image.setData(upload_file.read())
Ivan Tyagov's avatar
Ivan Tyagov committed
2178 2179 2180
    image.publish()
    convert_kw['quality'] = 99 # to not get cached
    web_page_document = self.portal.web_page_module.newContent(portal_type="Web Page")
Ivan Tyagov's avatar
Ivan Tyagov committed
2181
    web_page_document.setTextContent('''<b> test </b><img src="Embedded-XXX?format=jpeg&amp;dispay=medium&amp;quality=50"/>''')
2182
    self.tic()
Ivan Tyagov's avatar
Ivan Tyagov committed
2183 2184 2185 2186
    web_page_document_url = '%s/%s' %(self.portal.absolute_url(), web_page_document.getRelativeUrl())
    web_page_image_size, web_page_file_size = self.getURLSizeList(web_page_document_url, **convert_kw)
    self.assertTrue(max(preffered_size_for_display) - max(web_page_image_size) <= 1)

2187
    # external images
Ivan Tyagov's avatar
Ivan Tyagov committed
2188
    convert_kw['quality'] = 98
2189 2190 2191
    web_page_document = self.portal.web_page_module.newContent(portal_type="Web Page")
    web_page_document.setTextContent('''<b> test </b><img src="http://www.erp5.com/images/favourite.png"/>
<img style="width: 26px; height: 26px;" src="http://www.erp5.com//images/save2.png" />
Ivan Tyagov's avatar
Ivan Tyagov committed
2192 2193
<img style="width: 26px; height: 26px;" src="http:////www.erp5.com//images/save2.png" />
<img style="width: 26px; height: 26px;" src="http://www.erp5.com/./images/save2.png" />
2194
''')
2195
    self.tic()
2196 2197 2198 2199
    web_page_document_url = '%s/%s' %(self.portal.absolute_url(), web_page_document.getRelativeUrl())
    web_page_image_size, web_page_file_size = self.getURLSizeList(web_page_document_url, **convert_kw)
    self.assertTrue(max(preffered_size_for_display) - max(web_page_image_size) <= 1)

Ivan Tyagov's avatar
Ivan Tyagov committed
2200 2201 2202
    # XXX: how to simulate the case when web page contains (through reference) link to document for which based conversion failed?
    # XXX: how to fix case when web page contains (through reference) link to itself (causes infinite recursion)

2203

2204 2205 2206 2207 2208
  def test_convertToImageOnTraversal(self):
    """
    Test converting to image all Document portal types on traversal i.e.:
     - image_module/1?quality=100&display=xlarge&format=jpeg
     - document_module/1?quality=100&display=large&format=jpeg
2209 2210
     - document_module/1?quality=10&display=large&format=jpeg
     - document_module/1?display=large&format=jpeg
2211
     - web_page_module/1?quality=100&display=xlarge&format=jpeg
2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224
    """
    # Create OOo document
    ooo_document = self.portal.document_module.newContent(portal_type='Presentation')
    upload_file = makeFileUpload('TEST-en-003.odp')
    ooo_document.edit(file=upload_file)

    pdf_document = self.portal.document_module.newContent(portal_type='PDF')
    upload_file = makeFileUpload('TEST-en-002.pdf')
    pdf_document.edit(file=upload_file)

    image_document = self.portal.image_module.newContent(portal_type='Image')
    upload_file = makeFileUpload('TEST-en-002.png')
    image_document.edit(file=upload_file)
2225

2226
    web_page_document = self.portal.web_page_module.newContent(portal_type="Web Page")
2227 2228
    web_page_document.setTextContent('<b> test </b> $website_url $website_url')
    # a Web Page can generate dynamic text so test is as well
2229
    web_page_document.setTextContentSubstitutionMappingMethodId('WebPage_getStandardSubstitutionMappingDict')
2230
    self.tic()
2231 2232 2233 2234

    ooo_document_url = '%s/%s' %(self.portal.absolute_url(), ooo_document.getRelativeUrl())
    pdf_document_url = '%s/%s' %(self.portal.absolute_url(), pdf_document.getRelativeUrl())
    image_document_url = '%s/%s' %(self.portal.absolute_url(), image_document.getRelativeUrl())
2235
    web_page_document_url = '%s/%s' %(self.portal.absolute_url(), web_page_document.getRelativeUrl())
2236

2237 2238
    for display in ('nano', 'micro', 'thumbnail', 'xsmall', 'small', 'medium', 'large', 'xlarge',):
      max_tollerance_px = 1
2239
      preffered_size_for_display = self.getPreferences(display)
2240 2241 2242 2243
      for format in ('png', 'jpeg', 'gif',):
        convert_kw = {'display':display, \
                      'format':format, \
                      'quality':100}
2244
        # Note: due to some image interpolations it's possssible that we have a difference of max_tollerance_px
2245 2246 2247
        # so allow some tollerance which is produced by respective portal_transform command

        # any OOo based portal type
2248
        ooo_document_image_size, ooo_document_file_size = self.getURLSizeList(ooo_document_url, **convert_kw)
2249 2250 2251
        self.assertTrue(max(preffered_size_for_display) - max(ooo_document_image_size) <= max_tollerance_px)

        # PDF
2252
        pdf_document_image_size, pdf_document_file_size = self.getURLSizeList(pdf_document_url, **convert_kw)
2253 2254 2255
        self.assertTrue(max(preffered_size_for_display) - max(pdf_document_image_size) <= max_tollerance_px)

        # Image
2256
        image_document_image_size, image_document_file_size = self.getURLSizeList(image_document_url, **convert_kw)
2257
        self.assertTrue(max(preffered_size_for_display) - max(image_document_image_size) <= max_tollerance_px)
2258 2259 2260 2261 2262 2263
        self.assertTrue(abs(min(preffered_size_for_display) - min(image_document_image_size)) >= max_tollerance_px)

        cropped_image_document_image_size, cropped_image_document_file_size = \
            self.getURLSizeList(image_document_url, crop = 1, **convert_kw)
        self.assertEqual(max(preffered_size_for_display), max(cropped_image_document_image_size))
        self.assertEqual(min(preffered_size_for_display), min(cropped_image_document_image_size))
2264

2265 2266 2267
        # Web Page
        web_page_image_size, web_page_file_size = self.getURLSizeList(web_page_document_url, **convert_kw)
        self.assertTrue(max(preffered_size_for_display) - max(web_page_image_size) <= max_tollerance_px)
2268

2269

2270
    # test changing image quality will decrease its file size
2271
    for url in (image_document_url, pdf_document_url, ooo_document_url, web_page_document_url):
2272 2273 2274
      convert_kw = {'display':'xlarge', \
                    'format':'jpeg', \
                    'quality':100}
2275
      image_document_image_size_100p,image_document_file_size_100p = self.getURLSizeList(url, **convert_kw)
2276 2277
      # decrease in quality should decrease its file size
      convert_kw['quality'] = 5.0
2278
      image_document_image_size_5p,image_document_file_size_5p = self.getURLSizeList(url, **convert_kw)
2279 2280
      # removing quality should enable defaults settings which should be reasonable between 5% and 100%
      del convert_kw['quality']
2281
      image_document_image_size_no_quality,image_document_file_size_no_quality = self.getURLSizeList(url, **convert_kw)
2282
      # check file sizes
2283 2284
      self.assertTrue(image_document_file_size_100p >= image_document_file_size_no_quality and \
                      image_document_file_size_no_quality >= image_document_file_size_5p,
2285 2286 2287
                      "%s should be more then %s and %s should be more them %s" % \
                       (image_document_file_size_100p,
                        image_document_file_size_no_quality,
2288
                        image_document_file_size_no_quality,
2289 2290
                        image_document_file_size_5p)
                      )
2291 2292
      # no matter of quality image sizes whould be the same
      self.assertTrue(image_document_image_size_100p==image_document_image_size_5p and \
2293 2294 2295 2296 2297 2298 2299
                        image_document_image_size_5p==image_document_image_size_no_quality,
                      "%s should be equals to %s and %s should be equals to %s" % \
                       (image_document_image_size_100p,
                        image_document_image_size_5p,
                        image_document_image_size_5p,
                        image_document_image_size_no_quality)
                      )
2300

Ivan Tyagov's avatar
Ivan Tyagov committed
2301 2302
  def test_getOriginalContentOnTraversal(self):
    """
2303
      Return original content on traversal.
Ivan Tyagov's avatar
Ivan Tyagov committed
2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330
    """
    def getURL(uri, **kw):
      # __ac=RVJQNVR5cGVUZXN0Q2FzZTo%3D is encoded ERP5TypeTestCase with empty password
      url = '%s?%s&__ac=%s' %(uri, urllib.urlencode(kw), 'RVJQNVR5cGVUZXN0Q2FzZTo%3D')
      return urllib.urlopen(url)

    ooo_document = self.portal.document_module.newContent(portal_type='Presentation')
    upload_file = makeFileUpload('TEST-en-003.odp')
    ooo_document.edit(file=upload_file)

    pdf_document = self.portal.document_module.newContent(portal_type='PDF')
    upload_file = makeFileUpload('TEST-en-002.pdf')
    pdf_document.edit(file=upload_file)

    image_document = self.portal.image_module.newContent(portal_type='Image')
    upload_file = makeFileUpload('TEST-en-002.png')
    image_document.edit(file=upload_file)

    web_page_document = self.portal.web_page_module.newContent(portal_type="Web Page")
    web_page_document.setTextContent('<b> test </b> $website_url $website_url')
    # a Web Page can generate dynamic text so test is as well
    web_page_document.setTextContentSubstitutionMappingMethodId('WebPage_getStandardSubstitutionMappingDict')
    self.tic()

    response = getURL(image_document.absolute_url(), **{'format':''})
    self.assertTrue('Content-Type: image/png\r\n'  in response.info().headers)
    self.assertTrue('Content-Length: %s\r\n' %getFileSize('TEST-en-002.png') in response.info().headers)
2331

Ivan Tyagov's avatar
Ivan Tyagov committed
2332 2333 2334 2335 2336
    response = getURL(ooo_document.absolute_url(), **{'format':''})
    self.assertTrue('Content-Type: application/vnd.oasis.opendocument.presentation\r\n'  in response.info().headers)
    self.assertTrue('Content-Disposition: attachment; filename="TEST-en-003.odp"\r\n' in response.info().headers)
    self.assertTrue('Content-Length: %s\r\n' %getFileSize('TEST-en-003.odp') in response.info().headers)

2337
    response = getURL(pdf_document.absolute_url(), **{'format':''})
2338
    self.assertTrue('Content-Type: application/pdf\r\n'  in response.info().headers)
Ivan Tyagov's avatar
Ivan Tyagov committed
2339 2340
    self.assertTrue('Content-Disposition: attachment; filename="TEST-en-002.pdf"\r\n' in response.info().headers)

2341 2342
    response = getURL(pdf_document.absolute_url(), **{'format':'pdf'})
    self.assertTrue('Content-Type: application/pdf\r\n'  in response.info().headers)
2343
    self.assertTrue('Content-Disposition: attachment; filename="TEST-en-002.pdf"\r\n' in response.info().headers)
2344

Ivan Tyagov's avatar
Ivan Tyagov committed
2345 2346 2347
    response = getURL(web_page_document.absolute_url(), **{'format':''})
    self.assertTrue('Content-Type: text/html; charset=utf-8\r\n'  in response.info().headers)

2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358
  def test_checkConversionFormatPermission(self):
    """
     Test various use cases when conversion can be not allowed
    """
    portal_type = 'PDF'
    module = self.portal.getDefaultModule(portal_type)
    upload_file = makeFileUpload('TEST.Large.Document.pdf')
    pdf = module.newContent(portal_type=portal_type, file=upload_file)

    # if PDF size is larger than A4 format system should deny conversion
    self.assertRaises(Unauthorized, pdf.convert, format='jpeg')
Ivan Tyagov's avatar
Ivan Tyagov committed
2359 2360 2361 2362 2363 2364 2365

    # raster -> svg image should deny conversion if image width or height > 128 px
    portal_type = 'Image'
    module = self.portal.getDefaultModule(portal_type)
    upload_file = makeFileUpload('TEST-en-002.jpg')
    image = module.newContent(portal_type=portal_type, file=upload_file)
    self.assertRaises(Unauthorized, image.convert, format='svg')
2366

2367 2368 2369 2370 2371 2372 2373
  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')
2374
    doc.edit(file=upload_file)
2375
    doc.publish()
2376
    self.tic()
2377

2378 2379
    default_conversion_failure_image_size, default_conversion_failure_image_file_size = \
                            self.getURLSizeList('%s/default_conversion_failure_image' %self.portal.absolute_url())
2380

2381 2382 2383
    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})
2384
    self.assertTrue(doc.hasConversion(**{'format': 'png', 'quality': 70.0}))
2385

2386
    # try with new quality and pre_converted_only now a default image
2387 2388 2389 2390
    # 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)
2391

2392 2393 2394

    converted_image_size_80, converted_file_size_80 = self.getURLSizeList(doc_url, \
                                                             **{'format':'png', 'quality':80.0})
2395
    self.assertSameSet(converted_image_size_80, converted_image_size_70)
2396
    self.assertTrue(doc.hasConversion(**{'format': 'png', 'quality': 80.0}))
2397

2398
    # as conversion is cached we should get it
2399
    converted_image_size_80n, converted_file_size_80n = self.getURLSizeList(doc_url,
2400 2401
                                                               **{'format':'png', 'quality':80.0, 'pre_converted_only':1})
    self.assertSameSet(converted_image_size_80n, converted_image_size_70)
2402

2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426
  def test_getSearchText(self):
    """
     Test extracting search text script.
    """
    request = get_request()
    portal = self.portal

    # test direct passing argument_name_list
    request.set('MySearchableText', 'MySearchableText_value')
    self.assertEqual(request.get('MySearchableText'),
                     portal.Base_getSearchText(argument_name_list=['MySearchableText']))

    # simulate script being called in a listbox
    # to simulate this we set 'global_search_column' a listbox
    form = portal.DocumentModule_viewDocumentList
    listbox = form.listbox
    listbox.manage_edit_surcharged_xmlrpc(dict(
            global_search_column='advanced_search_text'))
    # render listbox
    listbox.render()
    request.set('advanced_search_text', 'advanced_search_text_value')
    self.assertEqual(request.get('advanced_search_text'),
                     portal.Base_getSearchText())

2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437
  def test_Document_getOtherVersionDocumentList(self):
    """
      Test getting list of other documents which have the same reference.
    """
    request = get_request()
    portal = self.portal

    kw={'reference': 'one_that_will_never_change',
        'language': 'en',
         'version': '001'}
    document1 = portal.document_module.newContent(portal_type="Presentation", **kw)
2438
    self.tic()
2439
    self.assertEqual(0, len(document1.Document_getOtherVersionDocumentList()))
2440 2441 2442

    kw['version'] == '002'
    document2 = portal.document_module.newContent(portal_type="Spreadsheet", **kw)
2443
    self.tic()
2444 2445 2446 2447 2448 2449 2450

    web_page1 = portal.web_page_module.newContent(portal_type="Web Page", \
                                                  **{'reference': 'embedded',
                                                     'version': '001'})
    web_page2 = portal.web_page_module.newContent(portal_type="Web Page", \
                                                 **{'reference': 'embedded',
                                                    'version': '002'})
2451
    self.tic()
2452 2453 2454 2455 2456 2457

    # both documents should be in other's document version list
    self.assertSameSet([x.getObject() for x in document1.Document_getOtherVersionDocumentList()], \
                        [document2])
    self.assertSameSet([x.getObject() for x in document2.Document_getOtherVersionDocumentList()], \
                        [document1])
2458

2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469
    # limit by portal type works
    self.assertSameSet([x.getObject() for x in document1.Document_getOtherVersionDocumentList(**{'portal_type':'Presentation'})], \
                        [])

    # current_web_document mode (i.e. embedded Web Page in Web Section) can override current context
    request.set('current_web_document', web_page1)
    self.assertSameSet([x.getObject() for x in document1.Document_getOtherVersionDocumentList()], \
                        [web_page2])
    request.set('current_web_document', web_page2)
    self.assertSameSet([x.getObject() for x in document1.Document_getOtherVersionDocumentList()], \
                        [web_page1])
2470

2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485
  def test_Base_getWorkflowEventInfoList(self):
    """
      Test getting history of an object.
    """
    portal = self.portal
    document = portal.document_module.newContent(portal_type="Presentation")
    document.edit(title='New')
    document.publish()
    document.reject()
    document.share()
    logged_in_user = str(self.portal.portal_membership.getAuthenticatedMember())
    event_list = document.Base_getWorkflowEventInfoList()
    event_list.reverse()
    # all actions by logged in user
    for event in event_list:
2486 2487 2488 2489 2490
      self.assertEqual(event.actor, logged_in_user)
    self.assertEqual(event_list[0].action, 'Edit')
    self.assertEqual(event_list[-1].action, 'Share Document')
    self.assertEqual(event_list[-2].action, 'Reject Document')
    self.assertEqual(event_list[-3].action, 'Publish Document')
Nicolas Delaby's avatar
Nicolas Delaby committed
2491

2492 2493 2494 2495 2496 2497 2498 2499
  def test_ContributeToExistingDocument(self):
    """
      Test various cases of contributing to an existing document
    """
    request = get_request()
    portal = self.portal
    # contribute a document, then make it not editable and check we can not contribute to it
    upload_file = makeFileUpload('TEST-en-002.doc')
2500
    kw = dict(file=upload_file, synchronous_metadata_discovery=True)
2501
    document = self.portal.Base_contribute(**kw)
2502
    self.tic()
2503 2504 2505
    # passing another portal type allows to create a
    # new document, but in draft state.
    # Then User takes a decision to choose which document to publish
2506
    kw['portal_type'] = "Spreadsheet"
2507
    new_document = self.portal.Base_contribute(**kw)
2508
    self.assertEqual(new_document.getValidationState(), 'draft')
2509
    self.tic()
Nicolas Delaby's avatar
Nicolas Delaby committed
2510

2511 2512
    # make it read only
    document.manage_permission(Permissions.ModifyPortalContent, [])
2513
    new_document.manage_permission(Permissions.ModifyPortalContent, [])
2514
    self.tic()
2515 2516
    kw.pop('portal_type')
    self.assertRaises(Unauthorized, self.portal.Base_contribute, **kw)
Nicolas Delaby's avatar
Nicolas Delaby committed
2517

2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528
  def test_ContributeWithMergingToExistingDocument(self):
    """
      Test various cases of merging to an existing document
    """
    request = get_request()
    portal = self.portal
    # contribute a document, then make it not editable and check we can not contribute to it
    kw=dict(synchronous_metadata_discovery=True)
    upload_file = makeFileUpload('TEST-en-002.doc')
    kw = dict(file=upload_file, synchronous_metadata_discovery=True)
    document = self.portal.Base_contribute(**kw)
2529
    self.tic()
2530

2531 2532 2533
    upload_file = makeFileUpload('TEST-en-003.odp', 'TEST-en-002.doc')
    kw = dict(file=upload_file, synchronous_metadata_discovery=True)
    document = self.portal.Base_contribute(**kw)
2534
    self.tic()
2535 2536 2537
    self.assertEqual('test-en-003-description', document.getDescription())
    self.assertEqual('test-en-003-title', document.getTitle())
    self.assertEqual('test-en-003-keywords', document.getSubject())
2538 2539 2540 2541 2542 2543 2544 2545 2546 2547

  def test_DocumentIndexation(self):
    """
      Test how a document is being indexed in MySQL.
    """
    portal = self.portal
    document = portal.document_module.newContent(
                                        portal_type='Presentation', \
                                        reference='XXX-YYY-ZZZZ',
                                        subject_list = ['subject1', 'subject2'])
2548
    self.tic()
2549 2550 2551 2552 2553
    # full text indexation
    full_text_result = portal.erp5_sql_connection.manage_test('select * from full_text where uid="%s"' %document.getUid())
    self.assertTrue('subject2' in full_text_result[0]['searchabletext'])
    self.assertTrue('subject1' in full_text_result[0]['searchabletext'])
    self.assertTrue(document.getReference() in full_text_result[0]['searchabletext'])
2554

2555
    # subject indexation
2556 2557 2558
    for subject_list in (['subject1',], ['subject2',],
                         ['subject1', 'subject2',],):
      subject_result = portal.portal_catalog(subject=subject_list)
2559 2560
      self.assertEqual(len(subject_result), 1)
      self.assertEqual(subject_result[0].getPath(), document.getPath())
2561

2562 2563 2564 2565 2566 2567 2568 2569 2570 2571
  def test_base_convertable_behaviour_with_successive_updates(self):
    """Check that update content's document (with setData and setFile)
    will refresh base_data and content_md5 as expected.

    When cloning a document base_data must not be computed once again.
    """
    # create a document
    upload_file = makeFileUpload('TEST-en-002.doc')
    kw = dict(file=upload_file, synchronous_metadata_discovery=True)
    document = self.portal.Base_contribute(**kw)
2572
    self.tic()
2573 2574 2575 2576 2577
    previous_md5 = document.getContentMd5()
    previous_base_data = document.getBaseData()

    # Clone document: base_data must not be computed once again
    cloned_document = document.Base_createCloneDocument(batch_mode=True)
2578 2579 2580 2581
    self.assertEqual(previous_md5, cloned_document.getContentMd5())
    self.assertEqual(document.getData(), cloned_document.getData())
    self.assertEqual(document.getBaseData(), cloned_document.getBaseData())
    self.assertEqual(document.getExternalProcessingState(),
2582
                      cloned_document.getExternalProcessingState())
2583
    self.assertEqual(document.getExternalProcessingState(), 'converted')
2584 2585 2586 2587

    # Update document with another content by using setData:
    # base_data must be recomputed
    document.edit(data=makeFileUpload('TEST-en-002.odt').read())
2588
    self.tic()
2589 2590 2591 2592
    self.assertTrue(document.hasBaseData())
    self.assertNotEquals(previous_base_data, document.getBaseData(),
                         'base data is not refreshed')
    self.assertNotEquals(previous_md5, document.getContentMd5())
2593
    self.assertEqual(document.getExternalProcessingState(), 'converted')
2594 2595 2596 2597 2598 2599
    previous_md5 = document.getContentMd5()
    previous_base_data = document.getBaseData()

    # Update document with another content by using setFile:
    # base_data must be recomputed
    document.edit(file=makeFileUpload('TEST-en-002.doc'))
2600
    self.tic()
2601 2602 2603 2604
    self.assertTrue(document.hasBaseData())
    self.assertNotEquals(previous_base_data, document.getBaseData(),
                         'base data is not refreshed')
    self.assertNotEquals(previous_md5, document.getContentMd5())
2605
    self.assertEqual(document.getExternalProcessingState(), 'converted')
2606

2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622
  # Currently, 'empty' state in processing_status_workflow is only set
  # when creating a document and before uploading any file. Once the
  # document has been uploaded and then the content is cleared, the
  # document state stays at 'converting' state as empty base_data is
  # not handled
  @expectedFailure
  def test_base_convertable_behaviour_when_deleted(self):
    """
    Check that deleting the content of a previously uploaded document
    actually clear base_data and md5 and check that the document goes
    back to empty state
    """
    # create a document
    upload_file = makeFileUpload('TEST-en-002.doc')
    kw = dict(file=upload_file, synchronous_metadata_discovery=True)
    document = self.portal.Base_contribute(**kw)
2623
    self.tic()
2624 2625 2626
    self.assertTrue(document.hasBaseData())
    self.assertTrue(document.hasContentMd5())

2627 2628
    # Delete content: base_data must be deleted
    document.edit(data=None)
2629
    self.tic()
2630 2631
    self.assertFalse(document.hasBaseData())
    self.assertFalse(document.hasContentMd5())
2632
    self.assertEqual(document.getExternalProcessingState(), 'empty')
2633

2634 2635 2636 2637 2638 2639
  def _test_document_publication_workflow(self, portal_type, transition):
    document = self.getDocumentModule().newContent(portal_type=portal_type)
    self.portal.portal_workflow.doActionFor(document, transition)

  def test_document_publication_workflow_Drawing_publish(self):
    self._test_document_publication_workflow('Drawing', 'publish_action')
2640 2641

  def test_document_publication_workflow_Drawing_publish_alive(self):
2642 2643 2644 2645 2646
    self._test_document_publication_workflow('Drawing',
        'publish_alive_action')

  def test_document_publication_workflow_Drawing_release(self):
    self._test_document_publication_workflow('Drawing', 'release_action')
2647 2648

  def test_document_publication_workflow_Drawing_release_alive(self):
2649 2650 2651 2652 2653
    self._test_document_publication_workflow('Drawing',
        'release_alive_action')

  def test_document_publication_workflow_Drawing_share(self):
    self._test_document_publication_workflow('Drawing', 'share_action')
2654 2655

  def test_document_publication_workflow_Drawing_share_alive(self):
2656 2657 2658 2659 2660
    self._test_document_publication_workflow('Drawing',
        'share_alive_action')

  def test_document_publication_workflow_File_publish(self):
    self._test_document_publication_workflow('File', 'publish_action')
2661 2662

  def test_document_publication_workflow_File_publish_alive(self):
2663 2664 2665 2666 2667
    self._test_document_publication_workflow('File',
        'publish_alive_action')

  def test_document_publication_workflow_File_release(self):
    self._test_document_publication_workflow('File', 'release_action')
2668 2669

  def test_document_publication_workflow_File_release_alive(self):
2670 2671 2672 2673 2674
    self._test_document_publication_workflow('File',
        'release_alive_action')

  def test_document_publication_workflow_File_share(self):
    self._test_document_publication_workflow('File', 'share_action')
2675 2676

  def test_document_publication_workflow_File_share_alive(self):
2677 2678 2679 2680 2681
    self._test_document_publication_workflow('File',
        'share_alive_action')

  def test_document_publication_workflow_PDF_publish(self):
    self._test_document_publication_workflow('PDF', 'publish_action')
2682 2683

  def test_document_publication_workflow_PDF_publish_alive(self):
2684 2685 2686 2687 2688
    self._test_document_publication_workflow('PDF',
        'publish_alive_action')

  def test_document_publication_workflow_PDF_release(self):
    self._test_document_publication_workflow('PDF', 'release_action')
2689 2690

  def test_document_publication_workflow_PDF_release_alive(self):
2691 2692 2693 2694 2695
    self._test_document_publication_workflow('PDF',
        'release_alive_action')

  def test_document_publication_workflow_PDF_share(self):
    self._test_document_publication_workflow('PDF', 'share_action')
2696 2697

  def test_document_publication_workflow_PDF_share_alive(self):
2698 2699 2700 2701 2702
    self._test_document_publication_workflow('PDF',
        'share_alive_action')

  def test_document_publication_workflow_Presentation_publish(self):
    self._test_document_publication_workflow('Presentation', 'publish_action')
2703 2704

  def test_document_publication_workflow_Presentation_publish_alive(self):
2705 2706 2707 2708 2709
    self._test_document_publication_workflow('Presentation',
        'publish_alive_action')

  def test_document_publication_workflow_Presentation_release(self):
    self._test_document_publication_workflow('Presentation', 'release_action')
2710 2711

  def test_document_publication_workflow_Presentation_release_alive(self):
2712 2713 2714 2715 2716
    self._test_document_publication_workflow('Presentation',
        'release_alive_action')

  def test_document_publication_workflow_Presentation_share(self):
    self._test_document_publication_workflow('Presentation', 'share_action')
2717 2718

  def test_document_publication_workflow_Presentation_share_alive(self):
2719 2720 2721 2722 2723
    self._test_document_publication_workflow('Presentation',
        'share_alive_action')

  def test_document_publication_workflow_Spreadsheet_publish(self):
    self._test_document_publication_workflow('Spreadsheet', 'publish_action')
2724 2725

  def test_document_publication_workflow_Spreadsheet_publish_alive(self):
2726 2727 2728 2729 2730
    self._test_document_publication_workflow('Spreadsheet',
        'publish_alive_action')

  def test_document_publication_workflow_Spreadsheet_release(self):
    self._test_document_publication_workflow('Spreadsheet', 'release_action')
2731 2732

  def test_document_publication_workflow_Spreadsheet_release_alive(self):
2733 2734 2735 2736 2737
    self._test_document_publication_workflow('Spreadsheet',
        'release_alive_action')

  def test_document_publication_workflow_Spreadsheet_share(self):
    self._test_document_publication_workflow('Spreadsheet', 'share_action')
2738 2739

  def test_document_publication_workflow_Spreadsheet_share_alive(self):
2740 2741 2742 2743 2744
    self._test_document_publication_workflow('Spreadsheet',
        'share_alive_action')

  def test_document_publication_workflow_Text_publish(self):
    self._test_document_publication_workflow('Text', 'publish_action')
2745 2746

  def test_document_publication_workflow_Text_publish_alive(self):
2747 2748 2749 2750 2751
    self._test_document_publication_workflow('Text',
        'publish_alive_action')

  def test_document_publication_workflow_Text_release(self):
    self._test_document_publication_workflow('Text', 'release_action')
2752 2753

  def test_document_publication_workflow_Text_release_alive(self):
2754 2755 2756 2757 2758
    self._test_document_publication_workflow('Text',
        'release_alive_action')

  def test_document_publication_workflow_Text_share(self):
    self._test_document_publication_workflow('Text', 'share_action')
2759 2760

  def test_document_publication_workflow_Text_share_alive(self):
2761 2762 2763
    self._test_document_publication_workflow('Text',
        'share_alive_action')

2764
  def test_document_publication_workflow_archiveVersion(self):
2765
    """ Test "visible" instances of a doc are auto archived when a new
2766 2767
    instance is made "visible" except when they have a future effective date.
    """
2768
    portal = self.portal
2769

2770 2771 2772 2773
    upload_file = makeFileUpload('TEST-en-002.doc')
    kw = dict(file=upload_file, synchronous_metadata_discovery=True)
    document_002 = self.portal.Base_contribute(**kw)
    document_002.publish()
2774
    self.tic()
2775

2776
    document_003 = document_002.Base_createCloneDocument(batch_mode=1)
2777
    document_003.setEffectiveDate(DateTime() - 1)
2778
    document_003.publish()
2779 2780 2781
    document_future_003 = document_002.Base_createCloneDocument(batch_mode=1)
    document_future_003.setEffectiveDate(DateTime() + 10)
    document_future_003.publish()
2782
    self.tic()
2783 2784
    self.assertEqual('published', document_003.getValidationState())
    self.assertEqual('archived', document_002.getValidationState())
2785
    self.assertEqual('published', document_future_003.getValidationState())
2786

2787
    # check if in any case document doesn't archive itself
2788
    # (i.e. shared_alive -> published or any other similar chain)
2789
    document_004 = document_002.Base_createCloneDocument(batch_mode=1)
2790
    document_004.shareAlive()
2791
    self.tic()
2792 2793

    document_004.publish()
2794
    self.tic()
2795
    self.assertEqual('published', document_004.getValidationState())
2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806
    # document_future_003 must not have been archived, as its effective date is
    # in the future.
    self.assertEqual('published', document_future_003.getValidationState())

    document_005 = document_004.Base_createCloneDocument(batch_mode=1)
    document_005.setEffectiveDate(DateTime() + 5)
    document_005.publish()
    self.tic()
    # Also, document_004 must not have been archived, as document_005's
    # effective_date is in the future.
    self.assertEqual('published', document_004.getValidationState())
2807 2808 2809 2810 2811

    # check case when no language is used
    document_nolang_005 = document_004.Base_createCloneDocument(batch_mode=1)
    document_nolang_005.setLanguage(None)
    document_nolang_005.publish()
2812
    self.tic()
2813 2814 2815 2816
    self.assertEqual('published', document_nolang_005.getValidationState())

    document_nolang_006 = document_nolang_005.Base_createCloneDocument(batch_mode=1)
    document_nolang_006.shareAlive()
2817
    self.tic()
2818

2819 2820 2821
    self.assertEqual('archived', document_nolang_005.getValidationState())
    self.assertEqual('shared_alive', document_nolang_006.getValidationState())

2822 2823 2824 2825 2826 2827
    # should ignore already archived document
    document_nolang_007 = document_nolang_006.Base_createCloneDocument(batch_mode=1)
    document_nolang_006.archive()
    document_nolang_007.shareAlive()
    self.tic()

2828 2829 2830 2831 2832 2833 2834 2835
  def testFileWithNotDefinedMimeType(self):
    upload_file = makeFileUpload('TEST-001-en.dummy')
    kw = dict(file=upload_file, synchronous_metadata_discovery=True,
              portal_type='File')
    document = self.portal.Base_contribute(**kw)
    document.setReference('TEST')
    request = self.app.REQUEST
    download_file = document.index_html(REQUEST=request, format=None)
2836
    self.assertEqual(download_file, 'foo\n')
2837
    document_format = None
2838
    self.assertEqual('TEST-001-en.dummy', document.getStandardFilename(
2839 2840
                      document_format))

2841
class TestDocumentWithSecurity(TestDocumentMixin):
2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853

  username = 'yusei'

  def getTitle(self):
    return "DMS with security"

  def login(self):
    uf = self.getPortal().acl_users
    uf._doAddUser(self.username, '', ['Auditor', 'Author'], [])
    user = uf.getUserById(self.username).__of__(uf)
    newSecurityManager(None, user)

2854
  def test_ShowPreviewAfterSubmitted(self):
2855 2856 2857 2858 2859 2860
    """
    Make sure that uploader can preview document after submitted.
    """
    filename = 'REF-en-001.odt'
    upload_file = makeFileUpload(filename)
    document = self.portal.portal_contributions.newContent(file=upload_file)
2861
    self.tic()
2862 2863 2864 2865 2866

    document.submit()

    preview_html = document.Document_getPreviewAsHTML().replace('\n', ' ')

2867
    self.tic()
2868 2869 2870

    self.assert_('I use reference to look up TEST' in preview_html)

2871 2872 2873 2874
  def test_DownloadableDocumentSize(self):
    '''Check that once the document is converted and cached, its size is
    correctly set'''
    portal = self.getPortalObject()
2875 2876
    portal_type = 'Text'
    document_module = portal.getDefaultModule(portal_type)
2877 2878

    # create a text document in document module
2879
    text_document = document_module.newContent(portal_type=portal_type,
2880 2881 2882 2883 2884
                                               reference='Foo_001',
                                               title='Foo_OO1')
    f = makeFileUpload('Foo_001.odt')
    text_document.edit(file=f.read())
    f.close()
2885
    self.tic()
2886 2887

    # the document should be automatically converted to html
2888
    self.assertEqual(text_document.getExternalProcessingState(), 'converted')
2889 2890 2891 2892 2893

    # check there is nothing in the cache for pdf conversion
    self.assertFalse(text_document.hasConversion(format='pdf'))

    # call pdf conversion, in this way, the result should be cached
2894 2895
    mime_type, pdf_data = text_document.convert(format='pdf')
    pdf_size = len(pdf_data)
2896 2897 2898 2899 2900 2901


    # check there is a cache entry for pdf conversion of this document
    self.assertTrue(text_document.hasConversion(format='pdf'))

    # check the size of the pdf conversion
2902
    self.assertEqual(text_document.getConversionSize(format='pdf'), pdf_size)
2903

2904 2905 2906 2907 2908 2909
  def test_ImageSizePreference(self):
    """
    Tests that when user defines image sizes are already defined in preferences
    those properties are taken into account when the user
    views an image
    """
2910
    super(TestDocumentWithSecurity, self).login('yusei')
2911 2912 2913 2914 2915 2916 2917
    preference_tool = self.portal.portal_preferences
    #get the thumbnail sizes defined by default on default site preference
    default_thumbnail_image_height = \
       preference_tool.default_site_preference.getPreferredThumbnailImageHeight()
    default_thumbnail_image_width = \
       preference_tool.default_site_preference.getPreferredThumbnailImageWidth()
    self.assertTrue(default_thumbnail_image_height > 0)
2918
    self.assertTrue(default_thumbnail_image_width > 0)
2919 2920 2921 2922
    self.assertEqual(default_thumbnail_image_height,
                     preference_tool.getPreferredThumbnailImageHeight())
    self.assertEqual(default_thumbnail_image_width,
                     preference_tool.getPreferredThumbnailImageWidth())
2923
    #create new user preference and set new sizes for image thumbnail display
2924 2925 2926 2927 2928
    user_pref = preference_tool.newContent(
                          portal_type='Preference',
                          priority=Priority.USER)
    self.portal.portal_workflow.doActionFor(user_pref, 'enable_action')
    self.assertEqual(user_pref.getPreferenceState(), 'enabled')
2929
    self.tic()
2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940
    user_pref.setPreferredThumbnailImageHeight(default_thumbnail_image_height + 10)
    user_pref.setPreferredThumbnailImageWidth(default_thumbnail_image_width + 10)
    #Verify that the new values defined are the ones used by default
    self.assertEqual(default_thumbnail_image_height + 10,
                     preference_tool.getPreferredThumbnailImageHeight())
    self.assertEqual(default_thumbnail_image_height + 10,
                     preference_tool.getPreferredThumbnailImageHeight(0))
    self.assertEqual(default_thumbnail_image_width + 10,
                     preference_tool.getPreferredThumbnailImageWidth())
    self.assertEqual(default_thumbnail_image_width + 10,
                     preference_tool.getPreferredThumbnailImageWidth(0))
2941
    #Now lets check that when we try to view an image as thumbnail,
2942 2943
    #the sizes of that image are the ones defined in user preference
    image_portal_type = 'Image'
2944 2945
    image_module = self.portal.getDefaultModule(image_portal_type)
    image = image_module.newContent(portal_type=image_portal_type)
2946
    self.assertEqual('thumbnail',
2947
       image.Image_view._getOb('my_thumbnail', None).get_value('image_display'))
2948 2949 2950
    self.assertEqual((user_pref.getPreferredThumbnailImageWidth(),
                    user_pref.getPreferredThumbnailImageHeight()),
                     image.getSizeFromImageDisplay('thumbnail'))
2951 2952 2953 2954 2955 2956 2957 2958 2959 2960

class TestDocumentPerformance(TestDocumentMixin):

  def test_01_LargeOOoDocumentToImageConversion(self):
    """
      Test large OOoDocument to image conversion
    """
    ooo_document = self.portal.document_module.newContent(portal_type='Spreadsheet')
    upload_file = makeFileUpload('import_big_spreadsheet.ods')
    ooo_document.edit(file=upload_file)
2961
    self.tic()
2962 2963 2964
    before = time.time()
    # converting any OOoDocument -> PDF -> Image
    # make sure that this can happen in less tan XXX seconds i.e. code doing convert
2965
    # uses only first PDF frame (not entire PDF) to make an image - i.e.optimized enough to not kill
2966 2967 2968 2969
    # entire system performance by doing extensive calculations over entire PDF (see r37102-37103)
    ooo_document.convert(format='png')
    after = time.time()
    req_time = (after - before)
2970 2971 2972
    # we should have image converted in less than Xs
    # the 100s value is estimated one, it's equal to time for cloudood conversion (around 52s) +
    # time for gs conversion. As normally test are executed in parallel some tollerance is needed.
2973
    self.assertTrue(req_time < 100.0,
2974
      "Conversion took %s seconds and it is not less them 100.0 seconds" % \
2975
        req_time)
Ivan Tyagov's avatar
Ivan Tyagov committed
2976

2977 2978 2979
def test_suite():
  suite = unittest.TestSuite()
  suite.addTest(unittest.makeSuite(TestDocument))
Ivan Tyagov's avatar
Ivan Tyagov committed
2980 2981
  suite.addTest(unittest.makeSuite(TestDocumentWithSecurity))
  suite.addTest(unittest.makeSuite(TestDocumentPerformance))
2982
  return suite
Bartek Górny's avatar
Bartek Górny committed
2983 2984


Fabien Morin's avatar
Fabien Morin committed
2985
# vim: syntax=python shiftwidth=2