[Checkins] SVN: z3c.dav/trunk/src/z3c/dav/ifvalidator.py * matchesIfHeader should only match the locktokens of the object been tested,

Michael Kerrin michael.kerrin at openapp.ie
Thu Jun 21 13:49:19 EDT 2007


Log message for revision 76907:
  * matchesIfHeader should only match the locktokens of the object been tested,
    but these lock tokens can be matched against any of its parents.
  
  * Fix parsing of weak entity tags.
  

Changed:
  U   z3c.dav/trunk/src/z3c/dav/ifvalidator.py

-=-
Modified: z3c.dav/trunk/src/z3c/dav/ifvalidator.py
===================================================================
--- z3c.dav/trunk/src/z3c/dav/ifvalidator.py	2007-06-21 17:41:15 UTC (rev 76906)
+++ z3c.dav/trunk/src/z3c/dav/ifvalidator.py	2007-06-21 17:49:18 UTC (rev 76907)
@@ -225,6 +225,10 @@
       >>> validator.valid(resource, request, None)
       True
 
+      >>> request._environ['IF'] = '([W/"xx"])'
+      >>> validator.valid(resource, request, None)
+      True
+
     The request object gets annotated with the results of the matching any
     state tokens, so that I only need to parse this data once. But since only
     entity tags have been passed so far the request object will not be
@@ -249,14 +253,18 @@
       >>> validator.valid(resource, request, None)
       True
 
+      >>> request._environ['IF'] = '(NOT [W/"xx"])'
+      >>> validator.valid(resource, request, None)
+      False
+
       >>> getStateResults(request)
       {}
 
     State-tokens
     ------------
 
-    If the request is False then we have no need for the state tokens but
-    they just default then to an empty dictionary.
+    We collect the parsed state tokens from the `IF` as we find them and
+    store them as an annotation on the request object.
 
       >>> request = TestRequest(environ = {'IF': '(<locktoken>)'})
       >>> validator.valid(resource, request, None)
@@ -347,7 +355,7 @@
 
     The request needs more setup in order for the traversal to work. We
     need and a publication object and since our TestRequets object extends
-    the BrowserRequest pretend that the method is `FROG` so that the
+    the BrowserRequest pretend that the method is `PUT` so that the
     traversal doesn't try and find a default view for the resource. This
     would require more setup.
 
@@ -375,9 +383,6 @@
       >>> request._environ['IF'] = '<http://localhost/locked> (<locked>)'
       >>> validator.valid(demo, request, None)
       True
-      >>> request._environ['IF'] = '<http://localhost/demo> (<demo>)'
-      >>> validator.valid(demo, request, None)
-      True
 
     If a specified resource does not exist then the only way for the IF header
     to match is for the state tokens to be `(Not <locktoken>)`.
@@ -447,8 +452,9 @@
       >>> missingdemo2  #doctest:+ELLIPSIS
       <zope.app.http.put.NullResource object at ...>
 
-    Now this request evaluates to true and we have no state tokens since, we
-    have no path to compare the results against.
+    Now this request evaluates to true and we take the path from the `IF`
+    header and store the state tokens in the request annotation against
+    this path.
 
       >>> validator.valid(demo2, request, None)
       True
@@ -459,8 +465,7 @@
       >>> matchesIfHeader(missingdemo2, request)
       True
 
-    But if the root2 folder is locked then we consider our selves to be in
-    the scope of the lock.
+    The demo object is not locked so it automatically matches the if header.
 
       >>> root2._tokens = ['locked']
       >>> validator.valid(demo2, request, None)
@@ -468,13 +473,7 @@
       >>> getStateResults(request)
       {'/missing': {'locked': False}}
       >>> matchesIfHeader(demo2, request)
-      False
-      >>> validator.valid(missingdemo2, request, None)
       True
-      >>> getStateResults(request)
-      {'/missing': {'locked': False}}
-      >>> matchesIfHeader(missingdemo2, request)
-      True
 
     Even though the correct lock token is supplied in the `IF` header for the
     root2 resource, it is on for an alternative resource and the root object
@@ -507,9 +506,6 @@
       >>> demo3 = Demo('missing')
       >>> root2.add(demo3)
       >>> matchesIfHeader(demo3, request)
-      False
-      >>> demo3._tokens = ['locked']
-      >>> matchesIfHeader(demo3, request)
       True
 
     Invalid data
@@ -595,7 +591,7 @@
       False
 
     Specifying a `DAV:no-lock` state token always causes the validation
-    to succeed.
+    to succeed, but the locktoken is never in the header so we don't match it.
 
       >>> request._environ['IF'] = '</demo> (NOT <DAV:no-lock>)'
       >>> validator.valid(demo, request, None)
@@ -615,7 +611,9 @@
 
     The parsed state token for the demo object does not match that in the
     `IF` header. Note that we needed to specify the <DAV:no-lock> in order
-    for the request to be valid.
+    for the request to be valid. In this case we the request will be
+    executed but there are event handlers that might call the matchesIfHeader
+    which does return False since we don't have any locktoken.
 
       >>> request._environ['IF'] = '</demo> (<falsetest>) (NOT <DAV:no-lock>)'
       >>> validator.valid(demo, request, None)
@@ -623,9 +621,9 @@
       >>> matchesIfHeader(demo, request)
       False
 
-    The state token for the root object matches that in the `IF` header. But
-    since the demo object is a child of the root token then it is also
-    assumed to match the parsed data.
+    The state token for the root object matches that in the `IF` header. And
+    this as special meaning for any children of the root folder, if there
+    state token is the same then we get a match.
 
       >>> root._tokens = ['roottest']
       >>> request._environ['IF'] = '</> (<roottest>)'
@@ -633,17 +631,10 @@
       True
       >>> matchesIfHeader(root, request)
       True
-      >>> matchesIfHeader(demo, request)
-      True
 
-    Since the state of the root token does not match the information in the
-    `IF` header, and the demo object fails to match the data.
+    Demo is locked with the state token 'test' and so doesnot validate against
+    the `IF` header.
 
-      >>> request._environ['IF'] = '</> (<falseroottest>) (NOT <DAV:no-lock>)'
-      >>> validator.valid(demo, request, None)
-      True
-      >>> matchesIfHeader(root, request)
-      False
       >>> matchesIfHeader(demo, request)
       False
 
@@ -658,16 +649,33 @@
       >>> matchesIfHeader(demo, request)
       True
 
-    Even tough the demo objects state failed to match the `IF` header, its
-    parent did.
+    If the demo objects is for example indirectly locked against a root, and
+    its state token appears in the `IF` header for a parent object then
+    the demo object matches the `IF` header.
 
-      >>> request._environ['IF'] = '</> (<roottest>) </demo> (<demofalsetest>)'
+      >>> request._environ['IF'] = '</> (<roottest>)'
+      >>> validator.valid(root, request, None)
+      True
+      >>> matchesIfHeader(root, request)
+      True
+      >>> matchesIfHeader(demo, request)
+      False
+
+    But if the demo object is not locked then it passes.
+
+      >>> demo._tokens = ['roottest']
       >>> validator.valid(demo, request, None)
       True
       >>> matchesIfHeader(root, request)
       True
       >>> matchesIfHeader(demo, request)
+      True
+
+      >>> root._tokens = ['notroottest']
+      >>> matchesIfHeader(root, request)
       False
+      >>> matchesIfHeader(demo, request)
+      True
 
     Cleanup
     =======
@@ -733,8 +741,8 @@
 
                 entity_tag = listitem.group("entity_tag")
                 if entity_tag:
-                    if entity_tag[2:] == "W/":
-                        entity_tag = entity[2:]
+                    if entity_tag[:2] == "W/":
+                        entity_tag = entity_tag[2:]
                     entity_tag = entity_tag[1:-1]
 
                 conditions.append(
@@ -859,33 +867,41 @@
 
 
 def matchesIfHeader(context, request):
-    # Test the state of the context to see if matches the list of states
-    # supplied in the `IF` header.
+    # Test the state of the context to see if matches the list of state
+    # tokens supplied in the `IF` header.
     reqannot = zope.annotation.interfaces.IAnnotations(request)
     stateresults = reqannot.get(STATE_ANNOTS, {})
 
-    islocked = False
-    while context:
-        # We need to specify a None view here. This means that IStateTokens
-        # adapters need to be reqistered for all views. But in some situations
-        # it might be nice to restrict a state token adapter to specific views,
-        # for example is the view's context is a null resource.
-        states = zope.component.queryMultiAdapter(
-            (context, request, None), IStateTokens, default = [])
-        states = states and states.tokens
-        if states:
+    # We need to specify a None view here. This means that IStateTokens
+    # adapters need to be reqistered for all views. But in some situations
+    # it might be nice to restrict a state token adapter to specific views,
+    # for example is the view's context is a null resource.
+    states = zope.component.queryMultiAdapter(
+        (context, request, None), IStateTokens, default = [])
+    states = states and states.tokens
+    if states:
+        parsedstates = stateresults.get(
+            zope.traversing.api.getPath(context), {})
+        while not parsedstates and \
+               getattr(context, "__parent__", None) is not None:
+            # An object might be indirectly locked so we can test its parents
+            # to see if any of there state tokens are listed in the `IF`
+            # header.
+            context = context.__parent__
             parsedstates = stateresults.get(
                 zope.traversing.api.getPath(context), {})
-            for locktoken in states:
-                if locktoken in parsedstates:
-                    return True
-            if parsedstates:
-                # None of the statetokens in the passed.
-                return False
-            islocked = True
-        context = getattr(context, "__parent__", None)
+        for locktoken in states:
+            # From the spec:
+            # Note that for the purpose of submitting the lock token the
+            # actual form doesn't matter; what's relevant is that the
+            # lock token appears in the If header, and that the If header
+            # itself evaluates to true.
+            if locktoken in parsedstates:
+                return True
+        return False
 
-    return not islocked
+    # No state tokens so this automatically matches.
+    return True
 
 
 class StateTokens(object):



More information about the Checkins mailing list