[Zope3-checkins] CVS: Zope3/src/zope/products/i18nfile - __init__.py:1.1.2.1 browser.py:1.1.2.1 configure.zcml:1.1.2.1 edit.pt:1.1.2.1 i18nfile.py:1.1.2.1 interfaces.py:1.1.2.1 tests.py:1.1.2.1

Philipp von Weitershausen philikon at philikon.de
Wed Feb 11 11:29:23 EST 2004


Update of /cvs-repository/Zope3/src/zope/products/i18nfile
In directory cvs.zope.org:/tmp/cvs-serv21715/i18nfile

Added Files:
      Tag: philikon-movecontent-branch
	__init__.py browser.py configure.zcml edit.pt i18nfile.py 
	interfaces.py tests.py 
Log Message:
Get rid of zope.products.content and zope.products.codecontent and move
content components in their own packages at zope.products.

See the package geddon proposal: http://dev.zope.org/Zope3/PackageGeddon


=== Added File Zope3/src/zope/products/i18nfile/__init__.py ===
#
# This file is necessary to make this directory a package.


=== Added File Zope3/src/zope/products/i18nfile/browser.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""I18n versions of several content objects.

$Id: browser.py,v 1.1.2.1 2004/02/11 16:29:21 philikon Exp $
"""
from zope.i18n.negotiator import negotiator

__metaclass__ = type

class I18nFileView:

    def __call__(self):
        """Call the File
        """
        request = self.request
        language = None
        if request is not None:
            langs = self.context.getAvailableLanguages()
            language = negotiator.getLanguage(langs, request)

            request.response.setHeader('Content-Type',
                                       self.context.getContentType())
            request.response.setHeader('Content-Length',
                                       self.context.getSize(language))

        return self.context.getData(language)


class I18nFileEdit:

    name = 'editForm'
    title = 'Edit Form'
    description = ('This edit form allows you to make changes to the ' +
                   'properties of this file.')

    def action(self, contentType, data, language, defaultLanguage,
               selectLanguage=None, removeLanguage=None,
               addLanguage=None, newLanguage=None):
        if selectLanguage:
            pass
        elif removeLanguage:
            self.context.removeLanguage(language)
            language = self.context.getDefaultLanguage()
        else:
            if addLanguage:
                language = newLanguage
            self.context.setDefaultLanguage(defaultLanguage)
            self.context.edit(data, contentType, language)
        return self.request.response.redirect(self.request.URL[-1] +
                      "/editForm.html?language=%s" %language)  # XXX url_quote


=== Added File Zope3/src/zope/products/i18nfile/configure.zcml ===
<configure
    xmlns='http://namespaces.zope.org/zope'
    xmlns:browser='http://namespaces.zope.org/browser'
    i18n_domain='zope'
    >

  <interface 
      interface=".i18nfile.II18nFile" 
      type="zope.app.interfaces.content.IContentType"
      />

  <content class=".i18nfile.I18nFile">
    <factory
        id=".I18nFile"
        permission="zope.ManageContent"
        title="I18n File"
        description="An Internationalized File"
        />

    <require
        permission="zope.View"
        interface="zope.products.file.interfaces.IReadFile"
        />

    <require
        permission="zope.ManageContent"
        interface="zope.products.file.interfaces.IWriteFile"
        />

    <require
        permission="zope.View"
        attributes="getDefaultLanguage getAvailableLanguages"
        />

    <require
        permission="zope.ManageContent"
        attributes="setDefaultLanguage removeLanguage"
        />

    <implements
       interface="zope.app.interfaces.annotation.IAttributeAnnotatable"
       />
  </content>


  <!-- browser directives -->


  <browser:page
      name="index.html"
      for=".interfaces.II18nFile"
      permission="zope.View"
      class=".browser.I18nFileView"
      />

  <browser:pages
      for=".interfaces.II18nFile"
      permission="zope.View"
      class=".browser.I18nFileEdit">

    <browser:page name="editForm.html" template="edit.pt" />
    <browser:page name="edit.html" attribute="action" />

  </browser:pages>

  <browser:menuItems
      menu="zmi_views"
      for=".interfaces.II18nFile">

      <!-- Keep original edit view, for now -->
      <browser:menuItem title="Edit" action="editForm.html" />

      <!-- Supress the upload view from file -->
      <browser:menuItem title="Upload" action="editForm.html"
                        filter="python: False" />

  </browser:menuItems>

  <browser:menuItem
      menu="add_content"
      for="zope.app.interfaces.container.IAdding"
      title="I18n File"
      action="zope.products.i18nfile.I18nFile"
      description="A file that supports multiple locales."
      permission="zope.ManageContent"
      />

</configure>


=== Added File Zope3/src/zope/products/i18nfile/edit.pt ===
<html metal:use-macro="views/standard_macros/page">
<head>
  <style metal:fill-slot="headers" type="text/css">
    <!--
    .ContentIcon {
        width: 20px;
    }

    .ContentTitle {
        text-align: left;
    }
    -->
  </style>
</head>

<body>
<div metal:fill-slot="body">

  <p tal:content="context/msg"
     tal:condition="python: hasattr(context, 'msg')">
    Message will go here.
  </p>

  <p tal:content="view/description">
    Description of the Form.
  </p>

  <form action="edit.html" method="post">

    <div class="row">
      <div class="label" i18n:translate="">Content Type</div>
      <div class="field">
        <input name="contentType" type="text" size="20"
               tal:attributes="value context/getContentType" />
      </div>
    </div>
    
    <div class="row">
      <div class="label" i18n:translate="">Default Language</div>
      <div class="field">
        <select name="defaultLanguage">
          <span tal:repeat="lang context/getAvailableLanguages"
                tal:omit-tag="">
          <option tal:attributes="
                      value lang;
                      selected python:context.getDefaultLanguage() == lang"
                  tal:content="lang" />
         </span>
        </select>
      </div>
    </div>
    <hr />
    <div class="row">
      <div class="label" i18n:translate="">Language</div>
      <div class="field">
        <select name="language">
          <span tal:repeat="lang context/getAvailableLanguages"
                tal:omit-tag="">
          <option tal:attributes="
                      value lang;
                      selected python:request.get('language',
                                      context.getDefaultLanguage()) == lang"
                  tal:content="lang" />
         </span>
        </select>
        <input type="submit" name="selectLanguage" value="Show" 
               i18n:attributes="value show-button"/>
        <input type="submit" name="removeLanguage"
               i18n:attributes="value remove-button"/>
        &nbsp;&nbsp;
        <input type="submit" name="addLanguage"
               value="Add new language"
               i18n:attributes="value" />
        <input type="text" name="newLanguage" size="10" />
      </div>
    </div>
    <div class="row">
      <div class="label" i18n:translate="">Data</div>
      <div class="field">
        <textarea name="data" cols="70" rows="10"
             tal:content="python:context.getData(request.get('language'))" />
      </div>
    </div>

    <div class="row">
      <div class="controls">
        <input type="submit" name="edit" value="Save Changes" 
               i18n:attributes="value save-changes-button"/>
      </div>
    </div>

  </form>

</div>
</body>
</html>




=== Added File Zope3/src/zope/products/i18nfile/i18nfile.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
$Id: i18nfile.py,v 1.1.2.1 2004/02/11 16:29:21 philikon Exp $
"""

import persistence
from zope.interface import implements
from zope.publisher.browser import FileUpload
from zope.products.file.file import File

from interfaces import II18nFile

class I18nFile(persistence.Persistent):
    """I18n aware file object.  It contains a number of File objects --
    one for each language.
    """

    implements(II18nFile)

    def __init__(self, data='', contentType=None, defaultLanguage='en'):
        self._data = {}
        self.defaultLanguage = defaultLanguage
        self.setData(data, language=defaultLanguage)

        if contentType is None:
            self.setContentType('')
        else:
            self.setContentType(contentType)

    def __len__(self):
        return self.getSize()

    def _create(self, data):
        """Create a new subobject of the appropriate type.  Should be
        overriden in subclasses.
        """
        return File(data)

    def _get(self, language):
        """Helper function -- return a subobject for a given language,
        and if it does not exist, return a subobject for the default
        language.
        """
        file = self._data.get(language)
        if not file:
            file = self._data[self.defaultLanguage]
        return file

    def _get_or_add(self, language, data=''):
        """Helper function -- return a subobject for a given language,
        and if it does not exist, create and return a new subobject.
        """
        if language is None:
            language = self.defaultLanguage
        file = self._data.get(language)
        if not file:
            self._data[language] = file = self._create(data)
            self._p_changed = 1
        return file

    def setContentType(self, contentType):
        '''See interface IFile'''
        self._contentType = contentType

    def getContentType(self):
        '''See interface IFile'''
        return self._contentType

    contentType = property(getContentType, setContentType)

    def edit(self, data, contentType=None, language=None):
        '''See interface IFile'''

        # XXX This seems broken to me, as setData can override the
        # content type explicitly passed in.

        if contentType is not None:
            self.setContentType(contentType)
        if isinstance(data, FileUpload) and not data.filename:
            data = None          # Ignore empty files
        if data is not None:
            self.setData(data, language)

    def getData(self, language=None):
        '''See interface IFile'''
        return self._get(language).getData()

    def setData(self, data, language=None):
        '''See interface IFile'''
        self._get_or_add(language).setData(data)

    data = property(getData, setData)

    def getSize(self, language=None):
        '''See interface IFile'''
        return self._get(language).getSize()

    def getDefaultLanguage(self):
        'See II18nAware'
        return self.defaultLanguage

    def setDefaultLanguage(self, language):
        'See II18nAware'
        if not self._data.has_key(language):
            raise ValueError, \
                  'cannot set nonexistent language (%s) as default' % language
        self.defaultLanguage = language

    def getAvailableLanguages(self):
        'See II18nAware'
        return self._data.keys()

    def removeLanguage(self, language):
        '''See interface II18nFile'''

        if language == self.defaultLanguage:
            raise ValueError, 'cannot remove default language (%s)' % language
        if self._data.has_key(language):
            del self._data[language]
            self._p_changed = True


=== Added File Zope3/src/zope/products/i18nfile/interfaces.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
$Id: interfaces.py,v 1.1.2.1 2004/02/11 16:29:21 philikon Exp $
"""

from zope.i18n.interfaces import II18nAware
from zope.products.file.interfaces import IFile

class II18nFile(IFile, II18nAware):
    """I18n aware file interface."""

    def removeLanguage(language):
        """Remove translated content for a given language.
        """


=== Added File Zope3/src/zope/products/i18nfile/tests.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""

$Id: tests.py,v 1.1.2.1 2004/02/11 16:29:21 philikon Exp $
"""

import unittest
from zope.interface.verify import verifyClass
from zope.i18n.tests.testii18naware import TestII18nAware
from zope.products.file.file import FileChunk

from i18nfile import I18nFile

def sorted(list):
    list.sort()
    return list

class Test(TestII18nAware):

    def _makeFile(self, *args, **kw):
        return I18nFile(*args, **kw)

    def _createObject(self):
        obj = self._makeFile(defaultLanguage='fr')
        obj.setData('', 'lt')
        obj.setData('', 'en')
        return obj

    def testEmpty(self):
        file = self._makeFile()

        self.assertEqual(file.getContentType(), '')
        self.assertEqual(file.getData(), '')
        self.assertEqual(file.getDefaultLanguage(), 'en')

    def testConstructor(self):
        file = self._makeFile('Foobar')
        self.assertEqual(file.getContentType(), '')
        self.assertEqual(file.getData(), 'Foobar')
        self.assertEqual(file.getData('en'), 'Foobar')
        self.assertEqual(file.getData('nonexistent'), 'Foobar')
        self.assertEqual(file.getDefaultLanguage(), 'en')
        self.assertEqual(sorted(file.getAvailableLanguages()), ['en'])


        file = self._makeFile('Foobar', 'text/plain')
        self.assertEqual(file.getContentType(), 'text/plain')
        self.assertEqual(file.getData(), 'Foobar')
        self.assertEqual(file.getData('en'), 'Foobar')
        self.assertEqual(file.getData('nonexistent'), 'Foobar')
        self.assertEqual(file.getDefaultLanguage(), 'en')
        self.assertEqual(sorted(file.getAvailableLanguages()), ['en'])


        file = self._makeFile(data='Foobar', contentType='text/plain')
        self.assertEqual(file.getContentType(), 'text/plain')
        self.assertEqual(file.getData(), 'Foobar')
        self.assertEqual(file.getData('en'), 'Foobar')
        self.assertEqual(file.getData('nonexistent'), 'Foobar')
        self.assertEqual(file.getDefaultLanguage(), 'en')
        self.assertEqual(sorted(file.getAvailableLanguages()), ['en'])

        file = self._makeFile(data='Foobar', contentType='text/plain',
                              defaultLanguage='fr')
        self.assertEqual(file.getContentType(), 'text/plain')
        self.assertEqual(file.getData(), 'Foobar')
        self.assertEqual(file.getData('en'), 'Foobar')
        self.assertEqual(file.getData('nonexistent'), 'Foobar')
        self.assertEqual(file.getDefaultLanguage(), 'fr')
        self.assertEqual(sorted(file.getAvailableLanguages()), ['fr'])

    def testMutators(self):
        file = self._makeFile()

        file.setContentType('text/plain')
        self.assertEqual(file.getContentType(), 'text/plain')
        self.assertEqual(sorted(file.getAvailableLanguages()), ['en'])

        file.setData('Foobar')
        self.assertEqual(file.getData(), 'Foobar')
        self.assertEqual(sorted(file.getAvailableLanguages()), ['en'])

        file.setData('Barbaz', language='fr')
        self.assertEqual(file.getData(), 'Foobar')
        self.assertEqual(file.getData('fr'), 'Barbaz')
        self.assertEqual(sorted(file.getAvailableLanguages()), ['en', 'fr'])

        file.edit('Blah', 'text/html')
        self.assertEqual(file.getContentType(), 'text/html')
        self.assertEqual(file.getData(), 'Blah')
        self.assertEqual(file.getData('fr'), 'Barbaz')
        self.assertEqual(sorted(file.getAvailableLanguages()), ['en', 'fr'])

        file.edit('Quux', 'text/html', 'lt')
        self.assertEqual(file.getContentType(), 'text/html')
        self.assertEqual(file.getData(), 'Blah')
        self.assertEqual(file.getData('fr'), 'Barbaz')
        self.assertEqual(file.getData('lt'), 'Quux')
        self.assertEqual(file.getSize(), len('Blah'))
        self.assertEqual(file.getSize('fr'), len('Barbaz'))
        self.assertEqual(file.getSize('lt'), len('Quux'))
        self.assertEqual(sorted(file.getAvailableLanguages()), ['en', 'fr', 'lt'])

        file.removeLanguage('lt')
        self.assertEqual(file.getContentType(), 'text/html')
        self.assertEqual(file.getData(), 'Blah')
        self.assertEqual(file.getData('fr'), 'Barbaz')
        self.assertEqual(file.getSize(), len('Blah'))
        self.assertEqual(file.getSize('fr'), len('Barbaz'))
        self.assertEqual(sorted(file.getAvailableLanguages()), ['en', 'fr'])
        self.assertEqual(file.getData('lt'), 'Blah')
        self.assertEqual(file.getSize('lt'), len('Blah'))

        file.removeLanguage('nonexistent')
        self.assertEqual(file.getContentType(), 'text/html')
        self.assertEqual(file.getData(), 'Blah')
        self.assertEqual(file.getData('fr'), 'Barbaz')
        self.assertEqual(file.getSize(), len('Blah'))
        self.assertEqual(file.getSize('fr'), len('Barbaz'))
        self.assertEqual(sorted(file.getAvailableLanguages()), ['en', 'fr'])
        self.assertEqual(file.getData('lt'), 'Blah')
        self.assertEqual(file.getSize('lt'), len('Blah'))

        self.assertRaises(ValueError, file.removeLanguage, file.getDefaultLanguage())
        self.assertRaises(ValueError, file.setDefaultLanguage, 'nonexistent')

    def testLargeDataInput(self):
        file = self._makeFile()

        # Insert as string
        file.setData('Foobar'*60000, 'en')
        self.assertEqual(file.getSize('en'), 6*60000)
        self.assertEqual(file.getData('en'), 'Foobar'*60000)

        # Insert data as FileChunk
        fc = FileChunk('Foobar'*4000)
        file.setData(fc, 'lt')
        self.assertEqual(file.getSize('lt'), 6*4000)
        self.assertEqual(file.getData('lt'), 'Foobar'*4000)

        # Insert data from file object
        import cStringIO
        sio = cStringIO.StringIO()
        sio.write('Foobar'*100000)
        sio.seek(0)
        file.setData(sio, 'fr')
        self.assertEqual(file.getSize('fr'), 6*100000)
        self.assertEqual(file.getData('fr'), 'Foobar'*100000)

    def testInterface(self):
        from zope.products.file.interfaces import IFile
        from interfaces import II18nFile
        from zope.i18n.interfaces import II18nAware

        self.failUnless(IFile.isImplementedByInstancesOf(I18nFile))
        self.failUnless(verifyClass(IFile, I18nFile))

        self.failUnless(II18nAware.isImplementedByInstancesOf(I18nFile))
        self.failUnless(verifyClass(II18nAware, I18nFile))

        self.failUnless(II18nFile.isImplementedByInstancesOf(I18nFile))
        self.failUnless(verifyClass(II18nFile, I18nFile))

    def testSetDefaultLanguage(self):
        # getDefaultLanguage and getAvailableLanguages are tested in the
        # above tests
        file = self._makeFile()

        file.setData('', language='lt')
        file.setDefaultLanguage('lt')
        self.assertEqual(file.getDefaultLanguage(), 'lt')


def test_suite():
    loader = unittest.TestLoader()
    return loader.loadTestsFromTestCase(Test)

if __name__=='__main__':
    unittest.TextTestRunner().run(test_suite())




More information about the Zope3-Checkins mailing list