[Zope3-checkins] CVS: Zope3/src/zope/app/content - i18nfile.py:1.1.2.1 configure.zcml:1.1.2.3 i18nimage.py:1.1.2.4 sql.py:1.1.2.5

Jim Fulton jim@zope.com
Tue, 24 Dec 2002 11:26:29 -0500


Update of /cvs-repository/Zope3/src/zope/app/content
In directory cvs.zope.org:/tmp/cvs-serv4174/zope/app/content

Modified Files:
      Tag: NameGeddon-branch
	configure.zcml i18nimage.py sql.py 
Added Files:
      Tag: NameGeddon-branch
	i18nfile.py 
Log Message:
got zope.app.content tests passing

=== Added File Zope3/src/zope/app/content/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 2002/12/24 16:25:58 jim Exp $
"""

import persistence
from zope.app.interfaces.content.i18nfile import II18nFile
from zope.app.content.file import File

# XXX We shouldn't be dependent on Browser here! Aaaargh.
from zope.publisher.browser import FileUpload

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 hasattr(data, '__class__') and data.__class__ is 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 = 1




=== Zope3/src/zope/app/content/configure.zcml 1.1.2.2 => 1.1.2.3 ===
--- Zope3/src/zope/app/content/configure.zcml:1.1.2.2	Tue Dec 24 07:51:00 2002
+++ Zope3/src/zope/app/content/configure.zcml	Tue Dec 24 11:25:58 2002
@@ -224,9 +224,9 @@
 
 </content>
 
-<content class="zope.app.interfaces.content.18nfile.I18nFile">
+<content class="zope.app.content.18nfile.I18nFile">
   <factory
-      id="I18nFile"
+      id="zope.app.content.I18nFile"
       permission="zope.ManageContent"
       title="I18n File"
       description="An Internationalized File" />


=== Zope3/src/zope/app/content/i18nimage.py 1.1.2.3 => 1.1.2.4 ===
--- Zope3/src/zope/app/content/i18nimage.py:1.1.2.3	Tue Dec 24 11:20:24 2002
+++ Zope3/src/zope/app/content/i18nimage.py	Tue Dec 24 11:25:58 2002
@@ -17,7 +17,8 @@
 """
 
 from zope.app.content.image import IImage, Image, getImageInfo
-from zope.app.interfaces.content.i18nfile import II18nFile, I18nFile
+from zope.app.interfaces.content.i18nfile import II18nFile
+from zope.app.content.i18nfile import I18nFile
 from zope.app.interfaces.annotation import IAnnotatable
 
 


=== Zope3/src/zope/app/content/sql.py 1.1.2.4 => 1.1.2.5 ===
--- Zope3/src/zope/app/content/sql.py:1.1.2.4	Tue Dec 24 11:16:06 2002
+++ Zope3/src/zope/app/content/sql.py	Tue Dec 24 11:25:58 2002
@@ -176,6 +176,8 @@
 parmre = re.compile(r'([\000- ]*([^\000- ="]+)=([^\000- ="]+))')
 qparmre = re.compile(r'([\000- ]*([^\000- ="]+)="([^"]*)")')
 
+valid_type = {'int':1, 'float':1, 'string':1, 'nb': 1}.has_key
+
 
 class InvalidParameter(Exception):
     pass
@@ -482,12 +484,282 @@
 
     __call__ = render
 
-
 # SQL compliant comparison operators
 comparison_operators = { 'eq': '=', 'ne': '<>',
                          'lt': '<', 'le': '<=', 'lte': '<=',
                          'gt': '>', 'ge': '>=', 'gte': '>=' }
 
+
+class SQLGroup:
+    blockContinuations = 'and', 'or'
+    name = 'sqlgroup'
+    required = None
+    where = None
+
+    def __init__(self, blocks):
+        self.blocks = blocks
+        tname, args, section = blocks[0]
+        self.__name__ = "%s %s" % (tname, args)
+        args = parse_params(args, required=1, where=1)
+        if args.has_key(''):
+            args[args['']] = 1
+        if args.has_key('required'):
+            self.required = args['required']
+        if args.has_key('where'):
+            self.where = args['where']
+
+
+    def render(self, md):
+        result = []
+        for tname, args, section in self.blocks:
+            __traceback_info__ = tname
+            s = section(None, md).strip()
+            if s:
+                if result:
+                    result.append(tname)
+                result.append("%s\n" % s)
+
+        if result:
+            if len(result) > 1:
+                result = "(%s)\n" %(' '.join(result))
+            else:
+                result = result[0]
+            if self.where:
+                result = "where\n" + result
+            return result
+
+        if self.required:
+            raise 'Input Error', 'Not enough input was provided!'
+
+        return ''
+
+    __call__ = render
+
+
+class SQLVar:
+    name = 'sqlvar'
+
+    # Some defaults
+    sql_delimiter = '\0'
+
+    def sql_quote__(self, v):
+        if v.find("\'") >= 0:
+            v = "''".join(v.split("\'"))
+        return "'%s'" %v
+
+    def __init__(self, args):
+        args = parse_params(args, name='', expr='', type=None, optional=1)
+
+        name, expr = name_param(args, 'sqlvar', 1)
+        if expr is None:
+            expr = name
+        else:
+            expr = expr.eval
+        self.__name__, self.expr = name, expr
+
+        self.args = args
+        if not args.has_key('type'):
+            raise ParseError, ('the type attribute is required', 'dtvar')
+
+        t = args['type']
+        if not valid_type(t):
+            raise ParseError, ('invalid type, %s' % t, 'dtvar')
+
+
+    def render(self, md):
+        name = self.__name__
+        args = self.args
+        t = args['type']
+        try:
+            expr = self.expr
+            if isinstance(expr, StringTypes):
+                v = md[expr]
+            else:
+                v = expr(md)
+        except:
+            if args.has_key('optional') and args['optional']:
+                return 'null'
+            if not isinstance(expr, StringTypes):
+                raise
+            raise ('Missing Input',
+                   'Missing input variable, **%s**' % name)
+
+        # XXX Shrug, should these tyoes be really hard coded? What about
+        # Dates and other types a DB supports; I think we should make this
+        # a plugin.
+        if t == 'int':
+            try:
+                if isinstance(v, StringTypes):
+                    int(v)
+                else:
+                    v = str(int(v))
+            except:
+                if not v and args.has_key('optional') and args['optional']:
+                    return 'null'
+                raise ValueError, (
+                    'Invalid integer value for **%s**' % name)
+
+        elif t == 'float':
+            try:
+                if isinstance(v, StringTypes):
+                    float(v)
+                else:
+                    v = str(float(v))
+            except:
+                if not v and args.has_key('optional') and args['optional']:
+                    return 'null'
+                raise ValueError, (
+                    'Invalid floating-point value for **%s**' % name)
+
+        else:
+            orig_v = v
+            v = str(v)
+            if (not v or orig_v is None) and t == 'nb':
+                if args.has_key('optional') and args['optional']:
+                    return 'null'
+                else:
+                    raise ValueError, (
+                        'Invalid empty string value for **%s**' % name)
+
+            v = self.sql_quote__(v)
+
+        return v
+
+    __call__ = render
+
+
+class SQLDTML(HTML):
+    __name__ = 'SQLDTML'
+
+    commands = {}
+
+    for k, v in HTML.commands.items():
+        commands[k]=v
+
+    # add the new tags to the DTML
+    commands['sqlvar' ] = SQLVar
+    commands['sqltest'] = SQLTest
+    commands['sqlgroup' ] = SQLGroup
+
+
+class SQLScript(SQLCommand, Persistent):
+
+    __implements__ = ISQLScript, IFileContent, IAttributeAnnotatable
+
+    def __init__(self, connectionName='', source='', arguments=''):
+        self.template = SQLDTML(source)
+        self.setConnectionName(connectionName)
+        # In our case arguments should be a string that is parsed
+        self.setArguments(arguments)
+
+    def setArguments(self, arguments):
+        'See ISQLScript'
+        assert isinstance(arguments, StringTypes), \
+               '"arguments" argument of setArguments() must be a string'
+        self._arg_string = arguments
+        self._arguments = parseArguments(arguments)
+
+    def getArguments(self):
+        'See ISQLScript'
+        return self._arguments
+
+    def getArgumentsString(self):
+        'See ISQLScript'
+        return self._arg_string
+
+    def setSource(self, source):
+        'See ISQLScript'
+        self.template.munge(source)
+
+    def getSource(self):
+        'See ISQLScript'
+        return self.template.read_raw()
+
+    def getTemplate(self):
+        'See ISQLScript'
+        return self.template
+
+    def setConnectionName(self, name):
+        'See ISQLScript'
+        self._connectionName = name
+        cache = getCacheForObj(self)
+        location = getLocationForCache(self)
+
+        if cache and location:
+            cache.invalidate(location)
+
+    setConnectionName = ContextMethod(setConnectionName)
+
+    def getConnectionName(self):
+        'See ISQLScript'
+        return self._connectionName
+
+    def getConnection(self):
+        'See ISQLCommand'
+        connection_service = getService(self, "SQLDatabaseConnections")
+        connection = connection_service.getConnection(self.connectionName)
+        return connection
+
+    getConnection = ContextMethod(getConnection)
+
+    def __call__(self, **kw):
+        'See ISQLCommand'
+
+        # Try to resolve arguments
+        arg_values = {}
+        missing = []
+        for name in self._arguments.keys():
+            name = name.encode('UTF-8')
+            try:
+                # Try to find argument in keywords
+                arg_values[name] = kw[name]
+            except:
+                # Okay, the first try failed, so let's try to find the default
+                arg = self._arguments[name]
+                try:
+                    arg_values[name] = arg['default']
+                except:
+                    # Now the argument might be optional anyways; let's check
+                    try:
+                        if not arg['optional']:
+                            missing.append(name)
+                    except:
+                        missing.append(name)
+
+        try:
+            connection = self.getConnection()
+        except AttributeError:
+            raise AttributeError, (
+                "The database connection **%s** cannot be found." % (
+                self.connectionName))
+
+        if connection is None:
+            raise 'Database Error', (
+                '%s is not connected to a database' %'foo')# self.id)
+
+        query = apply(self.template, (), arg_values)
+        cache = getCacheForObj(self)
+        location = getLocationForCache(self)
+        if cache and location:
+            _marker = []
+            result = cache.query(location, {'query': query}, default=_marker)
+            if result is not _marker:
+                return result
+        result = queryForResults(connection, query)
+        if cache and location:
+            cache.set(result, location, {'query': query})
+        return result
+
+    __call__ = ContextMethod(__call__)
+
+
+    # See ISQLScript
+    arguments = property(getArgumentsString, setArguments, None,
+                         "Set the arguments that are used for the SQL Script.")
+    source = property(getSource, setSource, None,
+                      "Set the SQL template source.")
+    connectionName = property(getConnectionName, setConnectionName, None,
+                              "Connection Name for the SQL scripts.")
 
 class SQLDTML(HTML):
     __name__ = 'SQLDTML'