[Zope3-checkins] CVS: Zope3/lib/python/Zope/App/OFS/Container/Views/VFS - VFSContainerView.py:1.4

Stephan Richter srichter@cbu.edu
Fri, 20 Dec 2002 04:26:08 -0500


Update of /cvs-repository/Zope3/lib/python/Zope/App/OFS/Container/Views/VFS
In directory cvs.zope.org:/tmp/cvs-serv31450/Zope/App/OFS/Container/Views/VFS

Modified Files:
	VFSContainerView.py 
Log Message:
Refactoring and fixing VFS and FTP

I am glad to make this commit that fixes up a lot of the FTP
implementation. I fixed the behavior of many of the FTP commands,
including LIST, SIZE, and CWD.

I moved the original VFSFile/DirectoryView into abstract classes and wrote
a special implementation for each content type, which makes the code much
more flexible.

Also I finally implemented a smart way of adding files via VFS through
file extension introspection, based on Jim's ExtensionViewName proposal.

I am adding documentation in the DevelCookbook right now and will later
add a README file.

TODOs:

- make VFS View names flexible, so that file extensions specify views.

- Simplify ZCML directives, so that one can add new extensions for Add views
  quicker. A solution might look like that:

      <vfs:view
          name=".dtml"
          for="Zope.App.OFS.Container.IAdding."
          factory=".DTMLPageAdd."
          permission="Zope.ManageContent">
        <vfs:extension name=".html" />
        <vfs:extension name=".xul" /> 
        <vfs:extension name=".xml" /> 
        ...
      </vfs:view>
			   
  This method would also be good for defining a default fiel extension.

- Show an object with its default file extension.
								     


=== Zope3/lib/python/Zope/App/OFS/Container/Views/VFS/VFSContainerView.py 1.3 => 1.4 ===
--- Zope3/lib/python/Zope/App/OFS/Container/Views/VFS/VFSContainerView.py:1.3	Sat Jun 29 11:41:37 2002
+++ Zope3/lib/python/Zope/App/OFS/Container/Views/VFS/VFSContainerView.py	Fri Dec 20 04:25:37 2002
@@ -11,30 +11,39 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""
+"""VFS-View for IContainer
+
+VFS-view implementation for a generic container. It should really work for
+all container-like objects. There is not much that can be done differently.
 
 $Id$
 """
 import fnmatch
-import time
+import datetime
+zerotime = datetime.datetime.fromtimestamp(0)
+
+from Zope.ComponentArchitecture import \
+     getView, queryView, getAdapter, queryAdapter, createObject
+
+from Zope.Proxy.ProxyIntrospection import removeAllProxies
 
-from Zope.ComponentArchitecture import getView
+from Zope.Publisher.Exceptions import NotFound
 from Zope.Publisher.VFS.VFSView import VFSView
 from Zope.Publisher.VFS.IVFSPublisher import IVFSPublisher
 from Zope.Publisher.VFS.VFSRequest import VFSRequest
-
 from Zope.Publisher.VFS.IVFSDirectoryPublisher import IVFSDirectoryPublisher
-from Zope.App.OFS.Container.IContainer import IContainer 
 
-# XXX hard coded object types.
-from Zope.App.OFS.Content.File.File import File
-from Zope.App.OFS.Content.Folder.Folder import Folder
+from Zope.App.OFS.Container.IContainer import IContainer
+from Zope.App.DublinCore.IZopeDublinCore import IZopeDublinCore
 
 
 class VFSContainerView(VFSView):
 
     __implements__ =  IVFSDirectoryPublisher, VFSView.__implements__
 
+    # This attribute specifies which type of container (factory id) should be
+    # used when a directory is created.
+    _directory_type = 'Container'
 
     ############################################################
     # Implementation methods for interface
@@ -42,12 +51,14 @@
 
     def exists(self, name):
         'See Zope.Publisher.VFS.IVFSDirectoryPublisher.IVFSDirectoryPublisher'
-        return name in self.context
-    
+        try:
+            self.publishTraverse(self.request, name)
+        except NotFound:
+            return 0
+        return 1
 
     def listdir(self, with_stats=0, pattern='*'):
         'See Zope.Publisher.VFS.IVFSDirectoryPublisher.IVFSDirectoryPublisher'
-        t = time.time()
         file_list = self.context.keys()
         # filter them using the pattern
         file_list = list(
@@ -61,55 +72,67 @@
             result = []
             for file in file_list:
                 obj = self.context[file]
-                size = 0
-                # XXX Should be much nicer
-                if IContainer.isImplementedBy(obj):
-                    dir_mode = 16384 + 511
+                view = queryView(obj, 'vfs', self.request)
+                if view is not None:
+                    stat = view.stat()
                 else:
-                    dir_mode = 511
-                if hasattr(obj, 'getSize'):
-                    size = obj.getSize()
-                stat = (dir_mode, 0, 0, 0, 0, 0, size, t, t, t)
-                if stat is not None:
-                    result.append((file, stat))
+                    # Even though this object has no VFS view, we should
+                    # display it.
+                    stat = (16384+511, 0, 0, 0, "nouser", "nogroup", 0,
+                            zerotime, zerotime, zerotime)
+                result.append((file, stat))
+                
         return result
     
 
     def mkdir(self, name, mode=777):
         'See Zope.Publisher.VFS.IVFSDirectoryPublisher.IVFSDirectoryPublisher'
         if not (name in self.context):
-            obj = Folder()
-            self.context.setObject(name, obj)
+            adding = getView(self.context, "+", self.request)
+            adding.setContentName(name)
+            add = queryView(adding, self._directory_type, self.request)
+            add()
 
     def remove(self, name):
         'See Zope.Publisher.VFS.IVFSDirectoryPublisher.IVFSDirectoryPublisher'
-        del self.context[name]
+        container = removeAllProxies(self.context)
+        container.__delitem__(name)
+        # XXX: We should have a ObjectRemovedEvent here
 
     def rmdir(self, name):
         'See Zope.Publisher.VFS.IVFSDirectoryPublisher.IVFSDirectoryPublisher'
-        del self.context[name]
+        self.remove(name)
 
     def rename(self, old, new):
         'See Zope.Publisher.VFS.IVFSDirectoryPublisher.IVFSDirectoryPublisher'
-        obj = self.context[old]
-        del self.context[old]
-        self.context.setObject(new, obj)        
-
+        container = self.context
+        content = container[old]
+        self.remove(old)
+        # Re-add the object
+        adding = getView(container, "+", self.request)
+        adding.setContentName(new)
+        content = adding.add(content)
 
     def writefile(self, name, mode, instream, start=0):
         'See Zope.Publisher.VFS.IVFSDirectoryPublisher.IVFSDirectoryPublisher'
-        # XXX This should become much, much smarter later. Based on the
-        # data and the file ending, it should pick the right object type. 
-        # *** Waiting for Jim's file extension proposal and code to land ***
-        if not (name in self.context):
-            obj = File()
-            self.context.setObject(name, obj)
+        # Find the extension
+        ext_start = name.rfind('.')
+        if ext_start > 0:
+            ext = name[ext_start:]
         else:
-            obj = self.context[name]
+            ext = "."
 
-        vfs_view = getView(obj, 'vfs', self.request)
-        vfs_view.write(mode, instream, start)
+        # Create and add a new content object.
+        adding = getView(self.context, "+", self.request)
+        adding.setContentName(name)
+        add = queryView(adding, ext, self.request)
+        if add is None:
+            # We do not know about this content type, so choose the generic
+            # one.
+            add = queryView(adding, ".", self.request)
 
+        add(mode, instream, start)
+                    
     def check_writable(self, name):
         'See Zope.Publisher.VFS.IVFSDirectoryPublisher.IVFSDirectoryPublisher'
         # XXX Cheesy band aid :-)
@@ -129,11 +152,25 @@
 
     def stat(self):
         'See Zope.Publisher.VFS.IVFSObjectPublisher.IVFSObjectPublisher'
-        dir_mode = 16384
-        t = time.time()
-        uid = 0
-        gid = 0
-        return (dir_mode+512, 0, 0, 0, uid, gid, 4096, t, t, t)
+        dc = getAdapter(self.context, IZopeDublinCore)
+        if dc is not None:
+            created = dc.created
+            modified = dc.modified
+        else:
+            created = zerotime
+            modified = zerotime
+
+        # It happens that modified might still be None, so make sure we return
+        # a date. *nix then uses the created date as the modified one, which we
+        # do too. ;)
+        if modified is None:
+            modified = created
+
+        dir_mode = 16384 + 504
+        uid = "nouser"
+        gid = "nogroup"
+        return (dir_mode, 0, 0, 0, uid, gid, 4096, modified, modified,
+                created)
 
 
     ######################################
@@ -141,7 +178,12 @@
 
     def publishTraverse(self, request, name):
         'See Zope.Publisher.VFS.IVFSPublisher.IVFSPublisher'
-        return getattr(self, name)
+        # This is a nice way of doing the name lookup; this way we can keep
+        # all the extension handeling code in the Traverser code.
+        traverser = getView(self.context, '_traverse', request)
+        return traverser.publishTraverse(request, name)
 
     #
     ############################################################
+
+