##############################################################################
#
# Copyright (c) 2004, 2005, 2006 Nexedi SARL and Contributors. 
# All Rights Reserved.
#          Romain Courteaud <romain@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.
#
##############################################################################

import unittest
import md5

import transaction
from AccessControl.SecurityManagement import newSecurityManager
from Testing import ZopeTestCase

from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from Products.ERP5Type.tests.utils import DummyTranslationService

try:
  # BACK: Zope 2.8
  # When we drop support, refactor the tests to use the i18n utilities
  # directly
  from Products.PageTemplates.GlobalTranslationService import \
                                setGlobalTranslationService
  def unregister_translation_domain_fallback():
    """dummy function to keep the beforeTearDown() code neat"""
 
except ImportError:
  # Zope 2.12, simulate setting the globalTranslationService with
  # zope.i18n utilities
  import zope.interface
  import zope.component
  global_translation_service = None
  
  from zope.i18n.interfaces import ITranslationDomain, \
                                   IFallbackTranslationDomainFactory
  class DummyTranslationDomainFallback(object):
    zope.interface.implements(ITranslationDomain)
    zope.interface.classProvides(IFallbackTranslationDomainFactory)
    
    def __init__(self, domain):
      self.domain = domain
    
    def translate(self, msgid, mapping=None, *args, **kw):
      return global_translation_service.translate(self.domain, msgid, mapping,
                                                  *args, **kw)
  
  def setGlobalTranslationService(translation_service):
    global global_translation_service
    global_translation_service = translation_service
    zope.component.provideUtility(DummyTranslationDomainFallback,
                                  provides=IFallbackTranslationDomainFactory)

  def unregister_translation_domain_fallback():
    from zope.component.globalregistry import base
    base.unregisterUtility(DummyTranslationDomainFallback)

HTTP_OK = 200
HTTP_UNAUTHORIZED = 401
HTTP_REDIRECT = 302

class TestERP5Core(ERP5TypeTestCase, ZopeTestCase.Functional):
  """Test for erp5_core business template.
  """
  run_all_test = 1
  quiet = 1

  manager_username = 'rc'
  manager_password = 'w'

  def getTitle(self):
    return "ERP5Core"

  def login(self, quiet=0, run=run_all_test):
    uf = self.getPortal().acl_users
    uf._doAddUser(self.manager_username, self.manager_password, ['Manager'], [])
    user = uf.getUserById(self.manager_username).__of__(uf)
    newSecurityManager(None, user)

  def afterSetUp(self):
    self.login()
    self.portal = self.getPortal()
    self.portal_id = self.portal.getId()
    self.auth = '%s:%s' % (self.manager_username, self.manager_password)

  def beforeTearDown(self):
    unregister_translation_domain_fallback()
    transaction.abort()
    if 'test_folder' in self.portal.objectIds():
      self.portal.manage_delObjects(['test_folder'])
    self.portal.portal_selections.setSelectionFor('test_selection', None)
    transaction.commit()
    self.tic()

  def test_01_ERP5Site_createModule(self, quiet=quiet, run=run_all_test):
    """
      Test that a module is created when ERP5Site_createModule is given the
      strict minimum number of arguments.
      A created module is composed of :
       - the module itself, directly in the portal object
       - a skin folder, directly in the skins tool
       - a portal type for the module
       - a portal type for the objects which can be contained in the module

      TODO: check more behaviours of the creation script, like skin priority, ...
    """
    if not run: return

    module_portal_type='UnitTest Module'
    portal_skins_folder='erp5_unittest'
    object_portal_type='UnitTest'
    object_title='UnitTest'
    module_id='unittest_module'
    module_title='UnitTests'
    
    
    skins_tool = self.portal.portal_skins
    types_tool = self.portal.portal_types
    self.failIf(self.portal._getOb(module_id, None) is not None)
    self.assertEqual(skins_tool._getOb(portal_skins_folder, None), None)
    self.assertEqual(types_tool._getOb(module_portal_type, None), None)
    self.assertEqual(types_tool._getOb(object_portal_type, None), None)

    self.portal.ERP5Site_createModule(module_portal_type=module_portal_type,
                                      portal_skins_folder=portal_skins_folder,
                                      object_portal_type=object_portal_type,
                                      object_title=object_title,
                                      module_id=module_id,
                                      module_title=module_title)
    self.failUnless(self.portal._getOb(module_id, None) is not None)
    self.assertEquals(module_title,
                      self.portal._getOb(module_id).getTitle())
    self.assertNotEqual(types_tool.getTypeInfo(module_portal_type), None)
    self.assertNotEqual(types_tool.getTypeInfo(object_portal_type), None)
    self.assertTrue(module_portal_type
                      in self.portal.getPortalModuleTypeList())
    
    skin_folder = skins_tool._getOb(portal_skins_folder, None)
    self.assertNotEqual(skin_folder, None)
    self.assert_('UnitTest_view' in skin_folder.objectIds())
    view_form = skin_folder.UnitTest_view
    self.assertEquals('form_view', view_form.pt)
    self.assertEquals('Base_edit', view_form.action)
    
    self.assert_('UnitTestModule_viewUnitTestList' in skin_folder.objectIds())
    list_form = skin_folder.UnitTestModule_viewUnitTestList
    self.assertEquals('form_list', list_form.pt)
    self.assertEquals('Base_doSelect', list_form.action)
    self.assert_('listbox' in [x.getId() for x in list_form.get_fields()])
    self.assert_('listbox' in
            [x.getId() for x in list_form.get_fields_in_group('bottom')])

    # make sure we can use our module
    self.portal.unittest_module.view()
    self.portal.unittest_module.newContent(id='document', portal_type='UnitTest')
    self.portal.unittest_module.document.view()

    # make sure translation domains are set correctly
    self.assertEquals('erp5_ui',
        self.portal.unittest_module.getTitleTranslationDomain())
    self.assertEquals('erp5_ui',
        self.portal.unittest_module.getShortTitleTranslationDomain())

    type_information = self.portal.portal_types[module_portal_type]
    self.assertTrue('business_application'
                    in type_information.getTypeBaseCategoryList())

  def test_02_FavouritesMenu(self, quiet=quiet, run=run_all_test):
    """
      Test that Manage members is not an entry in the My Favourites menu.
    """
    if not run: return
    portal_actions = getattr(self.getPortal(), 'portal_actions', None)
    global_action_list = portal_actions.listFilteredActionsFor(
                               self.getPortal())['global']
    action_name_list = []
    for action in global_action_list:
      if(action['visible']):
        action_name_list.append(action['title'])

    self.assertTrue('Create Module' in action_name_list)

    for action_name in action_name_list:
      self.assertNotEqual(action_name, "Manage Members")
      self.assertNotEqual(action_name, "Manage members")

  def test_frontpage(self):
    """Test we can view the front page.
    """
    response = self.publish('%s/view' % self.portal_id, self.auth)
    self.assertEquals(HTTP_OK, response.getStatus())
    
  def test_login_form(self):
    """Test anonymous user are redirected to login_form
    """
    response = self.publish('%s/view' % self.portal_id)
    self.assertEquals(HTTP_REDIRECT, response.getStatus())
    self.assertEquals('%s/login_form' % self.portal.absolute_url(),
                      response.getHeader('Location'))

  def test_view_tools(self):
    """Test we can view tools."""
    for tool in ('portal_categories',
                 'portal_templates',
                 'portal_rules',
                 'portal_alarms',):
      response = self.publish('%s/%s/view' % (self.portal_id, tool), self.auth)
      self.assertEquals(HTTP_OK, response.getStatus(),
                        "%s: %s" % (tool, response.getStatus()))
 
  def test_allowed_content_types_translated(self):
    """Tests allowed content types from the action menu are translated"""
    translation_service = DummyTranslationService()
    setGlobalTranslationService(translation_service)
    # assumes that we can add Business Template in template tool
    response = self.publish('%s/portal_templates/view' %
                                self.portal_id, self.auth)
    self.assertEquals(HTTP_OK, response.getStatus())
    self.failUnless(('Business Template', {})
                    in translation_service._translated['ui'])
    self.failUnless(
      ('Add ${portal_type}', {'portal_type': 'Business Template'}) in
      translation_service._translated['ui'])

  def test_jump_action_translated(self):
    """Tests jump actions are translated"""
    translation_service = DummyTranslationService()
    setGlobalTranslationService(translation_service)
    # adds a new jump action to Template Tool portal type
    self.getTypesTool().getTypeInfo('Template Tool').newContent(
                      portal_type='Action Information',
                      reference='dummy_jump_action',
                      title='Dummy Jump Action',
                      action_permission='View',
                      action_type='object_jump')
    response = self.publish('%s/portal_templates/view' %
                            self.portal_id, self.auth)
    self.assertEquals(HTTP_OK, response.getStatus())
    self.failUnless(('Dummy Jump Action', {}) in
                      translation_service._translated['ui'])

  def test_error_log(self):
    self.failUnless('error_log' in self.portal.objectIds())
    self.failUnless(self.portal.error_log.getProperties()['copy_to_zlog'])
    self.failIf('Unauthorized' in
                self.portal.error_log.getProperties()['ignored_exceptions'])

  def test_03_getDefaultModule(self, quiet=quiet, run=run_all_test):
    """
    test getDefaultModule method
    """
    if not run: 
      return
    portal_id = self.getPortal().getId()
    object_portal_type = ' '.join([part.capitalize() for part \
                                    in portal_id.split('_')])
    module_portal_type='%s Module' % object_portal_type
    portal_skins_folder='erp5_unittest'
    object_title=object_portal_type
    module_id="%s_module" % portal_id
    module_title='%ss' % object_portal_type
    
    # Create module for testing
    self.failIf(self.portal._getOb(module_id, None) is not None)
    self.assertEqual(self.portal.portal_skins._getOb(portal_skins_folder, None),
                     None)
    self.assertEqual(self.portal.portal_types.getTypeInfo(module_portal_type),
                     None)
    self.assertEqual(self.portal.portal_types.getTypeInfo(object_portal_type),
                     None)
    self.portal.ERP5Site_createModule(module_portal_type=module_portal_type,
                                      portal_skins_folder=portal_skins_folder,
                                      object_portal_type=object_portal_type,
                                      object_title=object_title,
                                      module_id=module_id,
                                      module_title=module_title)

    # Test
    self.assertEquals(
        module_id,
        self.portal.getDefaultModule(object_portal_type).getId())
    self.assertEquals(
        module_portal_type,
        self.portal.getDefaultModule(object_portal_type).getPortalType())
    self.assertEquals(
        module_id,
        self.portal.getDefaultModule(module_portal_type).getId())
    self.assertEquals(
        module_portal_type,
        self.portal.getDefaultModule(module_portal_type).getPortalType())

  def test_catalog_with_very_long_login_name(self, quiet=quiet, run=run_all_test):
    """Make sure that user with very long login name can find his document by catalog"""
    portal = self.getPortal()
    # Create user account with very long login name
    login_name = 'very_very_looooooooooooooooooooooooooooooooooooooooooooooooooooo' + \
    'oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_login_name'
    password = 'very_long_passworddddddddddddddddddddddddddddddddddddddddddddddddd' + \
    'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
    acl_users = portal.acl_users
    acl_users._doAddUser(login_name, password, ['Member'], [])
    user = acl_users.getUserById(login_name).__of__(acl_users)
    # Login as the above user
    newSecurityManager(None, user)
    self.auth = '%s:%s' % (login_name, password)
    transaction.commit()

    # Create preference
    portal.portal_preferences.newContent('Preference', title='My Test Preference')

    transaction.commit()
    self.tic()

    self.assertEqual(
      len(portal.portal_catalog(portal_type='Preference',
                                title='My Test Preference')),
      1)
    response = self.publish('%s/view' % self.portal_id, self.auth)
    self.assertEquals(HTTP_OK, response.getStatus())

  def test_Folder_delete(self):
    module = self.portal.newContent(portal_type='Folder', id='test_folder')
    document_1 = module.newContent(portal_type='Folder', id='1')
    document_2 = module.newContent(portal_type='Folder', id='2')
    uid_list = [document_1.getUid(), document_2.getUid()]
    self.portal.portal_selections.setSelectionParamsFor(
          'test_selection', dict(uids=uid_list))
    transaction.commit()
    self.tic()
    md5_string = md5.new(str(sorted([str(x) for x in uid_list]))).hexdigest()
    redirect = module.Folder_delete(selection_name='test_selection',
                                    uids=uid_list,
                                    md5_object_uid_list=md5_string)
    self.assert_('Deleted.' in redirect, redirect)
    transaction.savepoint(optimistic=True)
    self.assertEquals(len(module.objectValues()), 0)

  def test_Folder_delete_related_object(self):
    # deletion is refused if there are related objects
    module = self.portal.newContent(portal_type='Folder', id='test_folder')
    document_1 = module.newContent(portal_type='Folder', id='1')
    document_2 = module.newContent(portal_type='Folder', id='2')
    self.portal.portal_categories.setCategoryMembership(
                                context=document_1,
                                base_category_list=('source',),
                                category_list=document_2.getRelativeUrl())
    uid_list = [document_1.getUid(), document_2.getUid()]
    self.portal.portal_selections.setSelectionParamsFor(
                          'test_selection', dict(uids=uid_list))
    transaction.commit()
    self.tic()
    self.assertEquals([document_1],
        self.portal.portal_categories.getRelatedValueList(document_2))
    md5_string = md5.new(str(sorted([str(x) for x in uid_list]))).hexdigest()
    redirect = module.Folder_delete(selection_name='test_selection',
                                    uids=uid_list,
                                    md5_object_uid_list=md5_string)
    self.assert_('Sorry, 1 item is in use.' in redirect, redirect)
    transaction.savepoint(optimistic=True)
    self.assertEquals(len(module.objectValues()), 2)


  def test_Folder_delete_non_accessible_object(self):
    # deletion is refused if there are related objects, even if those related
    # objects cannot be accessed
    module = self.portal.newContent(portal_type='Folder', id='test_folder')
    document_1 = module.newContent(portal_type='Folder', id='1')
    document_2 = module.newContent(portal_type='Folder', id='2')
    self.portal.portal_categories.setCategoryMembership(
                                context=document_1,
                                base_category_list=('source',),
                                category_list=document_2.getRelativeUrl())
    uid_list = [document_2.getUid(), ]
    self.portal.portal_selections.setSelectionParamsFor(
                          'test_selection', dict(uids=uid_list))
    transaction.commit()
    self.tic()
    self.assertEquals([document_1],
        self.portal.portal_categories.getRelatedValueList(document_2))
    md5_string = md5.new(str(sorted([str(x) for x in uid_list]))).hexdigest()

    document_1.manage_permission('View', [], acquire=0)
    document_1.manage_permission('Access contents information', [], acquire=0)
    redirect = module.Folder_delete(selection_name='test_selection',
                                    uids=uid_list,
                                    md5_object_uid_list=md5_string)
    self.assert_('Sorry, 1 item is in use.' in redirect, redirect)
    transaction.savepoint(optimistic=True)
    self.assertEquals(len(module.objectValues()), 2)


def test_suite():
  suite = unittest.TestSuite()
  suite.addTest(unittest.makeSuite(TestERP5Core))
  return suite