Use List root folder named resource for List ACL instead of List default view URL
Code review : https://codereview.appspot.com/70090044/
diff --git a/src/com/google/enterprise/adaptor/sharepoint/SharePointAdaptor.java b/src/com/google/enterprise/adaptor/sharepoint/SharePointAdaptor.java
index c71092e..3ee29fd 100644
--- a/src/com/google/enterprise/adaptor/sharepoint/SharePointAdaptor.java
+++ b/src/com/google/enterprise/adaptor/sharepoint/SharePointAdaptor.java
@@ -1476,6 +1476,9 @@
         String webScopeId
             = w.getMetadata().getScopeID().toLowerCase(Locale.ENGLISH);
 
+        DocId rootFolderDocId
+            = encodeDocId(l.getMetadata().getRootFolder());
+
         Acl.Builder acl;
         if (scopeId.equals(webScopeId)) {
           acl = new Acl.Builder().setInheritFrom(new DocId(webUrl));
@@ -1485,11 +1488,23 @@
           acl = generateAcl(permissions, LIST_ITEM_MASK)
               .setInheritFrom(siteDocId, SITE_COLLECTION_ADMIN_FRAGMENT);
         }
-        response.setAcl(acl
+        response.setAcl(new Acl.Builder().setInheritFrom(rootFolderDocId)
             .setInheritanceType(Acl.InheritanceType.PARENT_OVERRIDES)
             .build());
+        final Map<DocId, Acl> map = Collections.singletonMap(rootFolderDocId,
+            acl.setInheritanceType(Acl.InheritanceType.PARENT_OVERRIDES)
+            .build());
+        executor.execute(new Runnable() {
+          @Override public void run() {
+            try {
+              context.getDocIdPusher().pushNamedResources(map);
+            } catch (InterruptedException ie) {
+              log.log(Level.WARNING, "Error pushing named resource", ie);
+	    }
+          }
+        });
       }
-      
+
       response.addMetadata(METADATA_OBJECT_TYPE,
           ObjectType.LIST.value());
       response.addMetadata(METADATA_PARENT_WEB_TITLE,
@@ -1897,12 +1912,12 @@
         // doesn't have a leading '/'.
         DocId folderDocId = encodeDocId("/" + rawFileDirRef.split(";#")[1]);
         DocId rootFolderDocId = encodeDocId(l.rootFolder);
-        DocId listDocId = encodeDocId(l.defaultViewUrl);
-        // If the parent is the List, we must use the list's docId instead of
-        // folderDocId, since the root folder is a List and not actually a
-        // Folder.
-        boolean parentIsList = folderDocId.equals(rootFolderDocId);
-        DocId parentDocId = parentIsList ? listDocId : folderDocId;
+        // If the parent is a list, folderDocId will be same as 
+        // rootFolderDocId. If inheritance chain is not 
+        // broken, item will inherit its permission from list root folder.
+        // If parent is a folder, item will inherit its permissions from parent
+        // folder.
+        boolean parentIsList = folderDocId.equals(rootFolderDocId);      
         String parentScopeId;
         if (parentIsList) {
           com.microsoft.schemas.sharepoint.soap.List list
@@ -1938,7 +1953,7 @@
               .split(";#", 2)[1].toLowerCase(Locale.ENGLISH);
         }
         if (scopeId.equals(parentScopeId)) {
-          acl = new Acl.Builder().setInheritFrom(parentDocId);
+          acl = new Acl.Builder().setInheritFrom(folderDocId);
         } else {
           // We have to search for the correct scope within the scopes element.
           // The scope provided in the metadata is for the parent list, not for
diff --git a/test/com/google/enterprise/adaptor/sharepoint/SharePointAdaptorTest.java b/test/com/google/enterprise/adaptor/sharepoint/SharePointAdaptorTest.java
index 586134b..95b1be8 100644
--- a/test/com/google/enterprise/adaptor/sharepoint/SharePointAdaptorTest.java
+++ b/test/com/google/enterprise/adaptor/sharepoint/SharePointAdaptorTest.java
@@ -24,6 +24,7 @@
 import static org.junit.Assert.fail;
 
 import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
 import com.google.common.util.concurrent.Callables;
 import com.google.enterprise.adaptor.Acl;
 import com.google.enterprise.adaptor.Config;
@@ -794,6 +795,7 @@
 
     adaptor = new SharePointAdaptor(initableSoapFactory,
         new UnsupportedHttpClient(), executorFactory);
+    AccumulatingDocIdPusher pusher = new AccumulatingDocIdPusher();
     adaptor.init(new MockAdaptorContext(config, pusher));
     ByteArrayOutputStream baos = new ByteArrayOutputStream();
     GetContentsRequest request = new GetContentsRequest(
@@ -819,14 +821,23 @@
         + "</ul></body></html>";
     assertEquals(golden, responseString);
     assertEquals(new Acl.Builder()
-        .setEverythingCaseInsensitive()
-        .setInheritFrom(new DocId("http://localhost:1/sites/SiteCollection"),
-          "admin")
+        .setInheritFrom(new DocId(
+            "http://localhost:1/sites/SiteCollection/Lists/Custom List"))
         .setInheritanceType(Acl.InheritanceType.PARENT_OVERRIDES)
-        .setPermitGroups(Arrays.asList(SITES_SITECOLLECTION_MEMBERS,
-            SITES_SITECOLLECTION_OWNERS, SITES_SITECOLLECTION_VISITORS))
         .build(),
         response.getAcl());
+    // Verify named resource for List Root Folder
+    assertEquals(ImmutableList.of(Collections.singletonMap(
+        new DocId("http://localhost:1/sites/SiteCollection/Lists/Custom List"),
+        new Acl.Builder()
+          .setEverythingCaseInsensitive()
+          .setInheritFrom(new DocId("http://localhost:1/sites/SiteCollection"),
+              "admin")
+          .setInheritanceType(Acl.InheritanceType.PARENT_OVERRIDES)
+          .setPermitGroups(Arrays.asList(SITES_SITECOLLECTION_MEMBERS,
+              SITES_SITECOLLECTION_OWNERS, SITES_SITECOLLECTION_VISITORS))
+          .build())),
+        pusher.getNamedResources());
     assertEquals(URI.create("http://localhost:1/sites/SiteCollection/Lists/"
           + "Custom%20List/AllItems.aspx"), response.getDisplayUrl());
     assertEquals(new Date(1336166672000L), response.getLastModified());
@@ -1149,6 +1160,47 @@
   }
 
   @Test
+  public void testGetDocContentListItemWithListAsParent() throws Exception {
+    SiteDataSoap siteData = MockSiteData.blank()
+        .register(SITES_SITECOLLECTION_S_CONTENT_EXCHANGE)
+        .register(SITES_SITECOLLECTION_LISTS_CUSTOMLIST_L_CONTENT_EXCHANGE)
+        .register(SITES_SITECOLLECTION_LISTS_CUSTOMLIST_2_LI_CONTENT_EXCHANGE
+            .replaceInContent("ows_Attachments='1'", "ows_Attachments='0'")
+            .replaceInContent("Inside Folder", "Under List")
+            .replaceInContent("/Test Folder", "")
+            .replaceInContent("/Test%20Folder", "")
+            .replaceInContent(
+              "ows_ScopeId='2;#{2E29615C-59E7-493B-B08A-3642949CC069}'",
+              "ows_ScopeId='2;#{f9cb02b3-7f29-4cac-804f-ba6e14f1eb39}'"))
+        .register(new URLSegmentsExchange(
+            "http://localhost:1/sites/SiteCollection/Lists/Custom List/2_.000",
+          true, null, null, "{6F33949A-B3FF-4B0C-BA99-93CB518AC2C0}", "2"));
+    adaptor = new SharePointAdaptor(initableSoapFactory,
+        new UnsupportedHttpClient(), executorFactory);
+    adaptor.init(new MockAdaptorContext(config, pusher));
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    GetContentsRequest request = new GetContentsRequest(
+        new DocId("http://localhost:1/sites/SiteCollection/Lists/Custom List"
+          + "/2_.000"));
+    GetContentsResponse response = new GetContentsResponse(baos);
+    adaptor.new SiteAdaptor("http://localhost:1/sites/SiteCollection",
+          "http://localhost:1/sites/SiteCollection", siteData,
+          new UnsupportedUserGroupSoap(), new UnsupportedPeopleSoap(),
+          Callables.returning(SITES_SITECOLLECTION_MEMBER_MAPPING),
+          new UnsupportedCallable<MemberIdMapping>())
+        .getDocContent(request, response);
+    // just verify ACLs here
+    assertEquals(new Acl.Builder()
+        .setInheritFrom(new DocId("http://localhost:1/sites/SiteCollection/"
+            + "Lists/Custom List"))
+        .setInheritanceType(Acl.InheritanceType.PARENT_OVERRIDES).build(),
+        response.getAcl());
+    assertEquals(URI.create("http://localhost:1/sites/SiteCollection/Lists/"
+          + "Custom%20List/DispForm.aspx?ID=2"),
+        response.getDisplayUrl());
+  }
+
+  @Test
   public void testGetDocContentListItemAnonymousAccess() throws Exception {
     SiteDataSoap siteData = MockSiteData.blank()
         .register(SITES_SITECOLLECTION_LISTS_CUSTOMLIST_1_URLSEG_EXCHANGE)
@@ -1314,48 +1366,6 @@
         response.getNamedResources());
   }
 
-  public void testGetDocContentListItemScopeSameAsParent() throws Exception {
-    SiteDataSoap siteData = MockSiteData.blank()
-        .register(new URLSegmentsExchange(
-            "http://localhost:1/sites/SiteCollection/Lists/Custom List"
-              + "/Test Folder/2_.000",
-            true, null, null, "{6F33949A-B3FF-4B0C-BA99-93CB518AC2C0}", "2"))
-        .register(new ContentExchange(ObjectType.LIST_ITEM,
-              "{6F33949A-B3FF-4B0C-BA99-93CB518AC2C0}", null, "2", true, false,
-              null, loadTestString("tapasnay-Lists-Announcements-1-li.xml")))
-        .register(new ContentExchange(ObjectType.LIST,
-              "{6F33949A-B3FF-4B0C-BA99-93CB518AC2C0}", null, null, false,
-              false, null,
-              loadTestString("tapasnay-Lists-Announcements-l.xml")));
-    final MemberIdMapping memberIdMapping
-        = new MemberIdMappingBuilder()
-        .put(1, new UserPrincipal("SOMEHOST\\administrator", DEFAULT_NAMESPACE))
-        .build();
-
-    adaptor = new SharePointAdaptor(initableSoapFactory,
-        new UnsupportedHttpClient(), executorFactory);
-    adaptor.init(new MockAdaptorContext(config, pusher));
-    ByteArrayOutputStream baos = new ByteArrayOutputStream();
-    GetContentsRequest request = new GetContentsRequest(
-        new DocId("http://localhost:1/sites/SiteCollection/Lists/Custom List/"
-          + "Test Folder/2_.000"));
-    GetContentsResponse response = new GetContentsResponse(baos);
-    adaptor.new SiteAdaptor("http://localhost:1/sites/SiteCollection",
-        "http://localhost:1/sites/SiteCollection",
-        siteData, new UnsupportedUserGroupSoap(), new UnsupportedPeopleSoap(),
-        Callables.returning(memberIdMapping),
-        new UnsupportedCallable<MemberIdMapping>())
-        .getDocContent(request, response);
-    // It looks odd that nobody can access the document since there are no
-    // groups and users, but the policy permits GDC-PSL\administrator. Thus, the
-    // policy's PARENT_OVERRIDE behavior is important.
-    assertEquals(new Acl.Builder()
-        .setInheritFrom(new DocId(
-            "http://localhost:1/tapasnay/Lists/Announcements"))
-        .setInheritanceType(Acl.InheritanceType.PARENT_OVERRIDES).build(),
-        response.getAcl());
-  }
-
   @Test
   public void testGetDocContentFolder() throws Exception {
     SiteDataSoap siteData = MockSiteData.blank()