content: /*
   * This library is part of OpenCms -
   * the Open Source Content Management System
   *
   * Copyright (c) Alkacon Software GmbH & Co. KG (https://www.alkacon.com)
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   * Lesser General Public License for more details.
   *
   * For further information about Alkacon Software GmbH & Co. KG, please see the
   * company website: https://www.alkacon.com
   *
   * For further information about OpenCms, please see the
   * project website: https://www.opencms.org
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   */
  
  package org.opencms.file;
  
  import org.opencms.db.CmsPublishList;
  import org.opencms.db.CmsPublishedResource;
  import org.opencms.db.CmsResourceState;
  import org.opencms.file.history.CmsHistoryFile;
  import org.opencms.file.history.I_CmsHistoryResource;
  import org.opencms.file.types.CmsResourceTypeFolder;
  import org.opencms.file.types.CmsResourceTypeImage;
  import org.opencms.file.types.CmsResourceTypePlain;
  import org.opencms.file.types.CmsResourceTypeXmlPage;
  import org.opencms.i18n.CmsEncoder;
  import org.opencms.lock.CmsLockFilter;
  import org.opencms.lock.CmsLockType;
  import org.opencms.main.CmsException;
  import org.opencms.main.CmsMultiException;
  import org.opencms.main.OpenCms;
  import org.opencms.publish.CmsPublishJobFinished;
  import org.opencms.report.CmsShellReport;
  import org.opencms.test.OpenCmsTestCase;
  import org.opencms.test.OpenCmsTestProperties;
  import org.opencms.test.OpenCmsTestResourceFilter;
  import org.opencms.xml.page.CmsXmlPage;
  import org.opencms.xml.page.CmsXmlPageFactory;
  
  import java.util.Collections;
  import java.util.List;
  import java.util.Locale;
  
  import org.apache.logging.log4j.core.appender.OpenCmsTestLogAppender;
  
  import junit.extensions.TestSetup;
  import junit.framework.Test;
  import junit.framework.TestSuite;
  
  /**
   * Unit tests for OpenCms publishing.<p>
   */
  public class TestPublishing extends OpenCmsTestCase {
  
      /**
       * Default JUnit constructor.<p>
       *
       * @param arg0 JUnit parameters
       */
      public TestPublishing(String arg0) {
  
          super(arg0);
      }
  
      /**
       * Test suite for this test class.<p>
       *
       * @return the test suite
       */
      public static Test suite() {
  
          OpenCmsTestProperties.initialize(org.opencms.test.AllTests.TEST_PROPERTIES_PATH);
  
          TestSuite suite = new TestSuite();
          suite.setName(TestPublishing.class.getName());
  
          suite.addTest(new TestPublishing("testPublishQueueIssue1"));
          suite.addTest(new TestPublishing("testPublishQueueIssue2"));
          suite.addTest(new TestPublishing("testPublishQueueIssue3"));
          suite.addTest(new TestPublishing("testPublishResourceGalore"));
          suite.addTest(new TestPublishing("testPublishResourceWithRelatedGalore"));
          suite.addTest(new TestPublishing("testPublishProjectGalore"));
          suite.addTest(new TestPublishing("testPublishStructureProperty"));
          suite.addTest(new TestPublishing("testPublishResourceProperty"));
          suite.addTest(new TestPublishing("testPublishSiblings"));
          suite.addTest(new TestPublishing("testPublishCopySibling"));
          suite.addTest(new TestPublishing("testPublishNewFiles"));
          suite.addTest(new TestPublishing("testPublishNewFilesInNewFolder"));
          suite.addTest(new TestPublishing("testPublishChangedFiles"));
          suite.addTest(new TestPublishing("testPublishLockedFiles"));
          suite.addTest(new TestPublishing("testPublishFolderWithLockedFiles"));
          suite.addTest(new TestPublishing("testPublishDeletedFiles"));
          suite.addTest(new TestPublishing("testPublishProjectLastmodified"));
          suite.addTest(new TestPublishing("testPublishProjectLastmodifiedFolder"));
          suite.addTest(new TestPublishing("testPublishTemporaryProject"));
          suite.addTest(new TestPublishing("testPublishMovedFiles"));
          suite.addTest(new TestPublishing("testPublishRelatedFiles"));
          suite.addTest(new TestPublishing("testPublishRelatedFilesInFolder"));
          suite.addTest(new TestPublishing("testPublishRelatedFilesInNewFolder"));
          suite.addTest(new TestPublishing("testPublishContentDate"));
          suite.addTest(new TestPublishing("testPublishDeletedSiblings"));
          suite.addTest(new TestPublishing("testPublishDeletedSiblings2"));
          suite.addTest(new TestPublishing("testPublishReplacedFile"));
  
          TestSetup wrapper = new TestSetup(suite) {
  
              @Override
              protected void setUp() {
  
                  setupOpenCms("simpletest", "/");
              }
  
              @Override
              protected void tearDown() {
  
                  removeOpenCms();
              }
          };
  
          return wrapper;
      }
  
      /**
       * Test publishing changed files.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testPublishChangedFiles() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing publish changed files");
  
          String resource1 = "/folder2/image1_new.gif";
          String resource2 = "/folder2/image1_sibling1.gif";
          String resource3 = "/folder2/image1_sibling2.gif";
          String resource4 = "/folder2/image1_sibling3.gif";
  
          CmsProject onlineProject = cms.readProject("Online");
          CmsProject offlineProject = cms.readProject("Offline");
  
          CmsProperty prop1;
          CmsProperty prop2;
          CmsProperty prop3;
          CmsProperty prop4;
  
          // make changes to the resources
          // do not need to make any changes to resource3 and resource4 as they are
          // siblings of resource2!
  
          cms.lockResource(resource1);
          cms.lockResource(resource2);
  
          cms.writePropertyObject(resource1, new CmsProperty("Title", resource1, null));
          cms.writePropertyObject(resource2, new CmsProperty("Title", resource2, null));
          cms.writePropertyObject(resource3, new CmsProperty("Title", resource3, null));
          cms.writePropertyObject(resource4, new CmsProperty("Title", resource4, null));
  
          // unlock all resources
          cms.unlockResource(resource1);
          cms.unlockResource(resource2);
  
          storeResources(cms, resource1);
          storeResources(cms, resource2);
          storeResources(cms, resource3);
          storeResources(cms, resource4);
  
          // publish a modified resource without siblings
          //
          OpenCms.getPublishManager().publishResource(cms, resource1);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // the online file must the offline changes
          cms.getRequestContext().setCurrentProject(onlineProject);
          prop1 = cms.readPropertyObject(resource1, "Title", false);
  
          if (!prop1.getValue().equals((resource1))) {
              fail("Property not published for " + resource1);
          }
          assertFilter(cms, resource1, OpenCmsTestResourceFilter.FILTER_PUBLISHRESOURCE);
  
          // check if the file in the offline project is unchancged now
          cms.getRequestContext().setCurrentProject(offlineProject);
          assertState(cms, resource1, CmsResource.STATE_UNCHANGED);
  
          // publish a modified resource with siblings, but keeping the siblings unpublished
          //
          OpenCms.getPublishManager().publishResource(cms, resource2);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // the online file must the offline changes
          cms.getRequestContext().setCurrentProject(onlineProject);
          prop2 = cms.readPropertyObject(resource2, "Title", false);
          prop3 = cms.readPropertyObject(resource3, "Title", false);
          prop4 = cms.readPropertyObject(resource4, "Title", false);
  
          if (!prop2.getValue().equals((resource2))) {
              fail("Property not published for " + resource2);
          }
          if (prop3.getValue().equals((resource3))) {
              fail("Property  published for " + resource3);
          }
          if (prop4.getValue().equals((resource4))) {
              fail("Property  published for " + resource4);
          }
  
          assertFilter(cms, resource2, OpenCmsTestResourceFilter.FILTER_PUBLISHRESOURCE);
  
          // check if the file in the offline project is unchancged now
          cms.getRequestContext().setCurrentProject(offlineProject);
          assertState(cms, resource2, CmsResource.STATE_UNCHANGED);
          assertState(cms, resource3, CmsResource.STATE_CHANGED);
          assertState(cms, resource4, CmsResource.STATE_CHANGED);
  
          // publish a modified resource with siblings, publish the siblings as well
          //
          OpenCms.getPublishManager().publishResource(
              cms,
              resource3,
              true,
              new CmsShellReport(cms.getRequestContext().getLocale()));
          OpenCms.getPublishManager().waitWhileRunning();
  
          // the online file must the offline changes
          cms.getRequestContext().setCurrentProject(onlineProject);
          prop3 = cms.readPropertyObject(resource3, "Title", false);
          prop4 = cms.readPropertyObject(resource4, "Title", false);
  
          if (!prop3.getValue().equals((resource3))) {
              fail("Property  not published for " + resource3);
          }
          if (!prop4.getValue().equals((resource4))) {
              fail("Property  not published for " + resource4);
          }
  
          assertFilter(cms, resource3, OpenCmsTestResourceFilter.FILTER_PUBLISHRESOURCE);
          assertFilter(cms, resource4, OpenCmsTestResourceFilter.FILTER_PUBLISHRESOURCE);
  
          // check if the file in the offline project is unchancged now
          cms.getRequestContext().setCurrentProject(offlineProject);
          assertState(cms, resource3, CmsResource.STATE_UNCHANGED);
          assertState(cms, resource4, CmsResource.STATE_UNCHANGED);
      }
  
      /**
       * Tests the behaviour during publishing depending on the content date.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testPublishContentDate() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing the behaviour during publishing depending on the content date");
  
          String resName = "/newtext.txt";
  
          // test creating content
          long time = System.currentTimeMillis();
          synchronized (this) {
              wait(50);
          }
          @SuppressWarnings("deprecation")
          CmsResource offlineResource = cms.createResource(resName, CmsResourceTypePlain.getStaticTypeId(), null, null);
          CmsFile offlineFile = cms.readFile(offlineResource);
          offlineFile.setContents("abc".getBytes());
          offlineFile = cms.writeFile(offlineFile);
          synchronized (this) {
              wait(50);
          }
          assertTrue(offlineResource.getDateContent() < System.currentTimeMillis());
          assertDateContentAfter(cms, resName, time);
  
          // test publishing new content
          OpenCms.getPublishManager().publishResource(cms, resName);
          OpenCms.getPublishManager().waitWhileRunning();
  
          CmsProject onlineProject = cms.readProject("Online");
          cms.getRequestContext().setCurrentProject(onlineProject);
          assertDateContentAfter(cms, resName, offlineResource.getDateContent());
          synchronized (this) {
              wait(50);
          }
          assertTrue(cms.readResource(offlineResource.getStructureId()).getDateContent() < System.currentTimeMillis());
  
          // test modifying the content
          CmsProject offlineProject = cms.readProject("Offline");
          cms.getRequestContext().setCurrentProject(offlineProject);
  
          cms.lockResource(resName);
          offlineFile = cms.readFile(offlineResource);
          time = System.currentTimeMillis();
          synchronized (this) {
              wait(50);
          }
          offlineFile.setContents("xyz".getBytes());
          offlineFile = cms.writeFile(offlineFile);
          offlineFile = cms.readFile(resName);
          assertDateContentAfter(cms, resName, time);
  
          // check the online project before publishing
          cms.getRequestContext().setCurrentProject(onlineProject);
          assertDateContentAfter(cms, resName, offlineResource.getDateContent());
  
          cms.getRequestContext().setCurrentProject(offlineProject);
  
          // now publish and check again
          OpenCms.getPublishManager().publishResource(cms, resName);
          OpenCms.getPublishManager().waitWhileRunning();
  
          cms.getRequestContext().setCurrentProject(onlineProject);
          assertDateContentAfter(cms, resName, offlineResource.getDateContent());
  
          // test modifying just a property/attribute
          cms.getRequestContext().setCurrentProject(offlineProject);
          cms.lockResource(resName);
          time = cms.readResource(resName).getDateContent();
          synchronized (this) {
              wait(50);
          }
          cms.writePropertyObject(resName, new CmsProperty(CmsPropertyDefinition.PROPERTY_TITLE, "abc", "def"));
          cms.setDateExpired(resName, System.currentTimeMillis() + 100000, false);
          assertDateContent(cms, resName, time);
  
          // check the online project before publishing
          cms.getRequestContext().setCurrentProject(onlineProject);
          assertDateContentAfter(cms, resName, time);
  
          // now publish and check again
          cms.getRequestContext().setCurrentProject(offlineProject);
          time = cms.readResource(resName).getDateContent();
          synchronized (this) {
              wait(50);
          }
          OpenCms.getPublishManager().publishResource(cms, resName);
          OpenCms.getPublishManager().waitWhileRunning();
  
          cms.getRequestContext().setCurrentProject(onlineProject);
          assertDateContentAfter(cms, resName, time);
  
          // test creating a sibling
          String sibName = "/newtext_sib.txt";
  
          cms.getRequestContext().setCurrentProject(offlineProject);
          time = cms.readResource(resName).getDateContent();
          synchronized (this) {
              wait(50);
          }
          CmsResource siblingOffline = cms.createSibling(resName, sibName, null);
          assertDateContent(cms, sibName, time);
          assertDateContent(cms, resName, time);
  
          synchronized (this) {
              wait(50);
          }
          // now publish and check
          OpenCms.getPublishManager().publishResource(cms, sibName);
          OpenCms.getPublishManager().waitWhileRunning();
  
          cms.getRequestContext().setCurrentProject(onlineProject);
          assertDateContentAfter(cms, sibName, time);
          assertDateContentAfter(cms, resName, time);
  
          // test modifying the content of a sibling
          cms.getRequestContext().setCurrentProject(offlineProject);
  
          cms.lockResource(resName);
          offlineFile = cms.readFile(resName);
          time = System.currentTimeMillis();
          synchronized (this) {
              wait(50);
          }
          offlineFile.setContents("test".getBytes());
          offlineFile = cms.writeFile(offlineFile);
          offlineFile = cms.readFile(resName);
          synchronized (this) {
              wait(50);
          }
  
          assertTrue(offlineFile.getDateContent() < System.currentTimeMillis());
          assertDateContentAfter(cms, resName, time);
          assertDateContentAfter(cms, sibName, time);
  
          // check the online project before publishing
          cms.getRequestContext().setCurrentProject(onlineProject);
          assertDateContentAfter(cms, resName, offlineResource.getDateContent());
          siblingOffline = cms.readResource(sibName);
          assertDateContentAfter(cms, sibName, siblingOffline.getDateContent());
  
          // now publish and check again
          cms.getRequestContext().setCurrentProject(offlineProject);
          synchronized (this) {
              wait(50);
          }
          OpenCms.getPublishManager().publishResource(cms, resName);
          OpenCms.getPublishManager().waitWhileRunning();
  
          cms.getRequestContext().setCurrentProject(onlineProject);
          assertDateContentAfter(cms, resName, offlineResource.getDateContent());
          assertDateContentAfter(cms, sibName, offlineResource.getDateContent());
          assertDateContent(cms, sibName, cms.readResource(resName).getDateContent());
          long onlineTime = cms.readResource(resName).getDateContent();
  
          // test moving
          String movedName = "/newtext_mov.txt";
  
          cms.getRequestContext().setCurrentProject(offlineProject);
          cms.lockResource(resName);
          time = cms.readResource(resName).getDateContent();
          synchronized (this) {
              wait(50);
          }
          // this will NOT write the content
          cms.moveResource(resName, movedName);
          offlineFile = cms.readFile(movedName);
  
          assertDateContent(cms, movedName, time);
          assertDateContent(cms, sibName, time);
  
          // check the online project before publishing
          cms.getRequestContext().setCurrentProject(onlineProject);
          assertDateContent(cms, resName, onlineTime);
          assertDateContent(cms, sibName, onlineTime);
  
          // now publish and check again
          cms.getRequestContext().setCurrentProject(offlineProject);
          synchronized (this) {
              wait(50);
          }
          OpenCms.getPublishManager().publishResource(cms, movedName);
          OpenCms.getPublishManager().waitWhileRunning();
  
          cms.getRequestContext().setCurrentProject(onlineProject);
          assertDateContent(cms, movedName, time);
          assertDateContent(cms, sibName, time);
      }
  
      /**
       * Tests the publishing of a fresh created sibling.<p>
       *
       * take a modified resource:
       * - create a sibling of it
       * - publish only the sibling
       * - compare online/offline versions of the sibling
       *
       * @throws Throwable if something goes wrong
       */
      public void testPublishCopySibling() throws Throwable {
  
          CmsObject cms = getCmsObject();
          String source = "/folder1/page1.html";
          echo("testing the publishing of a fresh created sibling");
  
          long time = System.currentTimeMillis();
          cms.lockResource(source);
          cms.setDateLastModified(source, time, false);
          synchronized (this) {
              wait(100);
          }
  
          String destination = "/folder1/sibling2.html";
          cms.copyResource(source, destination, CmsResource.COPY_AS_SIBLING);
  
          OpenCms.getPublishManager().publishResource(cms, destination);
          OpenCms.getPublishManager().waitWhileRunning();
  
          long newTime = cms.readResource(destination).getDateLastModified();
  
          cms.getRequestContext().setCurrentProject(cms.readProject(CmsProject.ONLINE_PROJECT_ID));
          assertEquals(
              "File is unchanged and online date last modified is not the same as offline",
              newTime,
              cms.readResource(destination).getDateLastModified());
          assertState(cms, destination, CmsResource.STATE_UNCHANGED);
      }
  
      /**
       * Test publishing deleted files.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testPublishDeletedFiles() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing publish deleted files");
  
          String resource1 = "/folder2/image1_new.gif";
          String resource2 = "/folder2/image1_sibling1.gif";
          String resource3 = "/folder2/image1_sibling2.gif";
          String resource4 = "/folder2/image1_sibling3.gif";
  
          CmsProject onlineProject = cms.readProject("Online");
          CmsProject offlineProject = cms.readProject("Offline");
  
          // publish a deleted resource without siblings
          //
  
          // delete the resources
          cms.lockResource(resource1);
          cms.deleteResource(resource1, CmsResource.DELETE_PRESERVE_SIBLINGS);
          cms.unlockResource(resource1);
  
          OpenCms.getPublishManager().publishResource(cms, resource1);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // the online file must be deleted
          cms.getRequestContext().setCurrentProject(onlineProject);
          try {
              cms.readResource(resource1);
              fail("Resource " + resource1 + " was not deleted online");
          } catch (CmsVfsResourceNotFoundException e) {
              // ok
          }
  
          cms.getRequestContext().setCurrentProject(offlineProject);
  
          // publish a deleted resource with siblings,
          // delete the siblings also, but publish only the resource itself
  
          // delete the resources
          cms.lockResource(resource2);
          cms.deleteResource(resource2, CmsResource.DELETE_REMOVE_SIBLINGS);
          cms.unlockResource(resource2);
  
          // this test makes only sense when siblings are published
          OpenCms.getPublishManager().publishResource(
              cms,
              resource2,
              false,
              new CmsShellReport(cms.getRequestContext().getLocale()));
          OpenCms.getPublishManager().waitWhileRunning();
  
          // the files must be unlocked
          List<String> lockedResources = cms.getLockedResources("/folder2", CmsLockFilter.FILTER_ALL);
          assertTrue(lockedResources.isEmpty());
  
          // the online file must be deleted
          cms.getRequestContext().setCurrentProject(onlineProject);
          try {
              cms.readResource(resource2);
              fail("Resource " + resource2 + " was not deleted online");
          } catch (CmsVfsResourceNotFoundException e) {
              // ok
          }
          // the other siblings must still be there
          try {
              cms.readResource(resource3);
          } catch (CmsException e) {
              fail("Resource " + resource3 + " error:" + e);
          }
          try {
              cms.readResource(resource4);
          } catch (CmsException e) {
              fail("Resource " + resource4 + " error:" + e);
          }
  
          cms.getRequestContext().setCurrentProject(offlineProject);
          // in the offline project, the siblings must be still marked as deleted
          assertState(cms, resource3, CmsResource.STATE_DELETED);
          assertState(cms, resource4, CmsResource.STATE_DELETED);
  
          // publish a deleted resource with siblings, delete the siblings
          //
          OpenCms.getPublishManager().publishResource(
              cms,
              resource3,
              true,
              new CmsShellReport(cms.getRequestContext().getLocale()));
          OpenCms.getPublishManager().waitWhileRunning();
  
          // the online files must be deleted
          cms.getRequestContext().setCurrentProject(onlineProject);
          try {
              cms.readResource(resource3);
              fail("Resource " + resource3 + " was not deleted online");
          } catch (CmsVfsResourceNotFoundException e) {
              // ok
          }
          try {
              cms.readResource(resource4);
              fail("Resource " + resource4 + " was not deleted online");
          } catch (CmsVfsResourceNotFoundException e) {
              // ok
          }
  
          cms.getRequestContext().setCurrentProject(offlineProject);
  
      }
  
      /**
       * Tests the publishing of deleted siblings.<p>
       *
       * take 2 siblings (s1 and s2):
       *  - delete one
       *  - publish the deleted one
       *  - the remaining sibling should be readable in online project
       *  - the deleted sibling should be eligible for restoration
       *
       * @throws Throwable if something goes wrong
       */
      public void testPublishDeletedSiblings() throws Throwable {
  
          CmsObject cms = getCmsObject();
  
          String source = "/testdeletesib1.html";
          String sibling = "/testdeletesib2.html";
  
          echo("testing publishing of deleted siblings");
  
          // create the siblings
          @SuppressWarnings("deprecation")
          CmsResource src = cms.createResource(source, CmsResourceTypePlain.getStaticTypeId());
          CmsFile file = cms.readFile(src);
          file.setContents("test text".getBytes());
          cms.writeFile(file);
  
          cms.createSibling(source, sibling, null);
  
          // publish both
          OpenCms.getPublishManager().publishResource(
              cms,
              source,
              true,
              new CmsShellReport(cms.getRequestContext().getLocale()));
          OpenCms.getPublishManager().waitWhileRunning();
  
          CmsProject online = cms.readProject(CmsProject.ONLINE_PROJECT_ID);
          CmsProject offline = cms.getRequestContext().getCurrentProject();
  
          // store the online versions
          cms.getRequestContext().setCurrentProject(online);
          storeResources(cms, source);
          storeResources(cms, sibling);
          cms.getRequestContext().setCurrentProject(offline);
  
          // delete one sibling
          cms.lockResource(sibling);
          cms.deleteResource(sibling, CmsResource.DELETE_PRESERVE_SIBLINGS);
          cms.unlockResource(sibling);
  
          // check the online versions
          cms.getRequestContext().setCurrentProject(online);
          assertFilter(cms, source, OpenCmsTestResourceFilter.FILTER_EQUAL);
          assertFilter(cms, sibling, OpenCmsTestResourceFilter.FILTER_EQUAL);
          cms.getRequestContext().setCurrentProject(offline);
  
          // publish just the deleted sibling
          OpenCms.getPublishManager().publishResource(
              cms,
              sibling,
              false,
              new CmsShellReport(cms.getRequestContext().getLocale()));
          OpenCms.getPublishManager().waitWhileRunning();
  
          cms.getRequestContext().setCurrentProject(online);
          assertFalse(cms.existsResource(sibling, CmsResourceFilter.ALL));
          assertTrue(cms.existsResource(source, CmsResourceFilter.ALL));
          assertEquals(new String("test text".getBytes()), new String(cms.readFile(source).getContents()));
          cms.getRequestContext().setCurrentProject(offline);
  
          List<I_CmsHistoryResource> list = cms.readDeletedResources("/", false);
          CmsHistoryFile histFile = (CmsHistoryFile)list.get(0);
          assertEquals(cms.getRequestContext().addSiteRoot(sibling), histFile.getRootPath());
          assertEquals(2, histFile.getVersion());
          file = cms.readFile(histFile);
          assertEquals(new String("test text".getBytes()), new String(file.getContents()));
  
          // now delete the 2nd sibling
          cms.lockResource(source);
          cms.deleteResource(source, CmsResource.DELETE_PRESERVE_SIBLINGS);
          cms.unlockResource(source);
  
          // publish the deleted sibling
          OpenCms.getPublishManager().publishResource(cms, source);
          OpenCms.getPublishManager().waitWhileRunning();
  
          cms.getRequestContext().setCurrentProject(online);
          assertFalse(cms.existsResource(sibling, CmsResourceFilter.ALL));
          assertFalse(cms.existsResource(source, CmsResourceFilter.ALL));
          cms.getRequestContext().setCurrentProject(offline);
  
          list = cms.readDeletedResources("/", false);
          assertEquals(2, list.size());
          histFile = (CmsHistoryFile)list.get(0);
          assertEquals(cms.getRequestContext().addSiteRoot(source), histFile.getRootPath());
          assertEquals(2, histFile.getVersion());
          file = cms.readFile(histFile);
          assertEquals(new String("test text".getBytes()), new String(file.getContents()));
          CmsHistoryFile histFile2 = (CmsHistoryFile)list.get(1);
          assertEquals(cms.getRequestContext().addSiteRoot(sibling), histFile2.getRootPath());
          assertEquals(2, histFile2.getVersion());
          file = cms.readFile(histFile2);
          assertEquals(new String("test text".getBytes()), new String(file.getContents()));
      }
  
      /**
       * Tests the publishing of deleted siblings, 2nd issue.<p>
       *
       * take 2 siblings (s1 and s2):
       *  - delete all
       *  - publish all
       *  - non of them should be readable
       *  - both siblings should be eligible for restoration
       *
       * @throws Throwable if something goes wrong
       */
      @SuppressWarnings("deprecation")
      public void testPublishDeletedSiblings2() throws Throwable {
  
          CmsObject cms = getCmsObject();
  
          String folder = "/testdelete";
          String source = "/testdelete/testdeletesib2-1.html";
          String sibling = "/testdelete/testdeletesib2-2.html";
  
          echo("testing publishing of deleted siblings, 2nd issue");
  
          cms.createResource(folder, CmsResourceTypeFolder.RESOURCE_TYPE_ID);
          // create the siblings
          CmsResource src = cms.createResource(source, CmsResourceTypePlain.getStaticTypeId());
          CmsFile file = cms.readFile(src);
          file.setContents("test text".getBytes());
          cms.writeFile(file);
  
          cms.createSibling(source, sibling, null);
  
          // publish all
          OpenCms.getPublishManager().publishResource(cms, folder);
          OpenCms.getPublishManager().waitWhileRunning();
  
          CmsProject online = cms.readProject(CmsProject.ONLINE_PROJECT_ID);
          CmsProject offline = cms.getRequestContext().getCurrentProject();
  
          // store the online versions
          cms.getRequestContext().setCurrentProject(online);
          storeResources(cms, source);
          storeResources(cms, sibling);
          cms.getRequestContext().setCurrentProject(offline);
  
          // delete both siblings
          cms.lockResource(sibling);
          cms.deleteResource(sibling, CmsResource.DELETE_REMOVE_SIBLINGS);
          cms.unlockResource(sibling);
  
          // check the online versions
          cms.getRequestContext().setCurrentProject(online);
          assertFilter(cms, source, OpenCmsTestResourceFilter.FILTER_EQUAL);
          assertFilter(cms, sibling, OpenCmsTestResourceFilter.FILTER_EQUAL);
          cms.getRequestContext().setCurrentProject(offline);
  
          // publish both deleted siblings
          OpenCms.getPublishManager().publishResource(
              cms,
              sibling,
              true,
              new CmsShellReport(cms.getRequestContext().getLocale()));
          OpenCms.getPublishManager().waitWhileRunning();
  
          cms.getRequestContext().setCurrentProject(online);
          assertFalse(cms.existsResource(sibling, CmsResourceFilter.ALL));
          assertFalse(cms.existsResource(source, CmsResourceFilter.ALL));
          cms.getRequestContext().setCurrentProject(offline);
  
          List<I_CmsHistoryResource> list = cms.readDeletedResources(folder, false);
          assertEquals(2, list.size());
          CmsHistoryFile histFile = (CmsHistoryFile)list.get(0);
          assertEquals(cms.getRequestContext().addSiteRoot(source), histFile.getRootPath());
          assertEquals(2, histFile.getVersion());
          file = cms.readFile(histFile);
          assertEquals(new String("test text".getBytes()), new String(file.getContents()));
          CmsHistoryFile histFile2 = (CmsHistoryFile)list.get(1);
          assertEquals(cms.getRequestContext().addSiteRoot(sibling), histFile2.getRootPath());
          assertEquals(2, histFile2.getVersion());
          file = cms.readFile(histFile2);
          assertEquals(new String("test text".getBytes()), new String(file.getContents()));
      }
  
      /**
       * Test publishing a folder with locked files.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testPublishFolderWithLockedFiles() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing publish a folder with locked files");
  
          String folder = "/folder2/subfolder21/";
          String resource = "image1.gif";
  
          // touch the folder
          cms.lockResource(folder);
          cms.setDateLastModified(folder, System.currentTimeMillis(), false);
          cms.unlockResource(folder);
  
          // lock a resource in the folder
          cms.lockResource(folder + resource);
          cms.setDateLastModified(folder + resource, System.currentTimeMillis(), false);
  
          // publish the folder without subresources
          CmsPublishList publishList = OpenCms.getPublishManager().getPublishList(
              cms,
              Collections.singletonList(cms.readResource(folder)),
              false,
              false);
          OpenCms.getPublishManager().publishProject(
              cms,
              new CmsShellReport(cms.getRequestContext().getLocale()),
              publishList);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // ensure the folder has been published
          assertState(cms, folder, CmsResource.STATE_UNCHANGED);
          // ensure the folder is unlocked
          assertLock(cms, folder, CmsLockType.UNLOCKED);
          // ensure that the changed resource is still changed in the offline project
          assertState(cms, folder + resource, CmsResource.STATE_CHANGED);
          // ensure the resource is still locked
          assertLock(cms, folder + resource);
      }
  
      /**
       * Test publishing changed files.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testPublishLockedFiles() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing publish locked files");
  
          // switch user
          cms.loginUser("test1", "test1");
          cms.getRequestContext().setCurrentProject(cms.readProject("Offline"));
          cms.getRequestContext().setSiteRoot("/sites/default/");
  
          String source = "/folder2/subfolder21/image1.gif";
          String resource1 = "/folder2/image1_new.gif";
          String resource2 = "/folder2/image1_sibling1.gif";
  
          // make changes to the resources
          // do not need to make any changes to resource3 and resource4 as they are
          // siblings of resource2!
  
          cms.lockResource(source);
          cms.lockResource(resource1);
          cms.lockResource(resource2);
  
          cms.writePropertyObject(source, new CmsProperty("Title", source + " modified", null));
          cms.writePropertyObject(resource1, new CmsProperty("Title", resource1 + " modified", null));
          cms.writePropertyObject(resource2, new CmsProperty("Title", resource2 + " modified", null));
  
          // switch user, so the locked resources can not be published until they are explicit unlocked
          cms.loginUser("Admin", "admin");
          cms.getRequestContext().setCurrentProject(cms.readProject("Offline"));
          cms.getRequestContext().setSiteRoot("/sites/default/");
  
          storeResources(cms, source);
          storeResources(cms, resource1);
          storeResources(cms, resource2);
  
          // publish a modified resource without siblings
          OpenCms.getPublishManager().publishProject(cms);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // ensure that all changed resources are still changed in the offline project
          assertState(cms, source, CmsResource.STATE_CHANGED);
          assertState(cms, resource1, CmsResource.STATE_CHANGED);
          assertState(cms, resource2, CmsResource.STATE_CHANGED);
  
          // lock the resources, so the next test can run
          cms.changeLock(source);
          cms.changeLock(resource1);
          cms.changeLock(resource2);
  
          // ensure that all changed resources are NOT published
          cms.getRequestContext().setCurrentProject(cms.readProject("Online"));
          CmsProperty prop0 = cms.readPropertyObject(source, "Title", false);
          CmsProperty prop1 = cms.readPropertyObject(resource1, "Title", false);
          CmsProperty prop2 = cms.readPropertyObject(resource2, "Title", false);
  
          if (prop0.getValue().equals((source + " modified"))) {
              fail("Property published for " + source);
          }
          if (prop1.getValue().equals((resource1 + " modified"))) {
              fail("Property published for " + resource1);
          }
          if (prop2.getValue().equals((resource2 + " modified"))) {
              fail("Property published for " + resource2);
          }
      }
  
      /**
       * Test publishing moved files.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testPublishMovedFiles() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing publish moved files");
  
          String resource1 = "/folder1/subfolder12/subsubfolder121/image1.gif";
          String target1 = "/folder1/subfolder12/subsubfolder121/image1_new.gif";
  
          CmsProject onlineProject = cms.readProject("Online");
          CmsProject offlineProject = cms.readProject("Offline");
  
          // move the resource
          cms.lockResource(resource1);
          cms.moveResource(resource1, target1);
          cms.unlockResource(target1);
  
          storeResources(cms, target1);
  
          // publish it
          CmsPublishList publishList = OpenCms.getPublishManager().getPublishList(cms, cms.readResource(target1), false);
          OpenCms.getPublishManager().publishProject(
              cms,
              new CmsShellReport(cms.getRequestContext().getLocale()),
              publishList);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check publish resource state
          List<?> pubResources = cms.readPublishedResources(publishList.getPublishHistoryId());
          assertEquals(2, pubResources.size());
          CmsPublishedResource pubRes = (CmsPublishedResource)pubResources.get(0);
          assertEquals(cms.getRequestContext().addSiteRoot(resource1), pubRes.getRootPath());
          assertEquals(CmsPublishedResource.STATE_MOVED_SOURCE, pubRes.getMovedState());
          pubRes = (CmsPublishedResource)pubResources.get(1);
          assertEquals(cms.getRequestContext().addSiteRoot(target1), pubRes.getRootPath());
          assertEquals(CmsPublishedResource.STATE_MOVED_DESTINATION, pubRes.getMovedState());
  
          // the online original file must be deleted
          cms.getRequestContext().setCurrentProject(onlineProject);
          try {
              cms.readResource(resource1, CmsResourceFilter.ALL);
              fail("Resource " + resource1 + " was not deleted online");
          } catch (CmsVfsResourceNotFoundException e) {
              // ok
          } catch (CmsException e) {
              fail("Resource " + resource1 + " error:" + e);
          }
          try {
              cms.readResource(target1);
          } catch (CmsVfsResourceNotFoundException e) {
              fail("Resource " + target1 + " is not online");
          } catch (CmsException e) {
              fail("Resource " + target1 + " error:" + e);
          }
          assertFilter(cms, target1, OpenCmsTestResourceFilter.FILTER_PUBLISHRESOURCE);
  
          cms.getRequestContext().setCurrentProject(offlineProject);
          try {
              cms.readResource(resource1, CmsResourceFilter.ALL);
              fail("Resource " + resource1 + " was not deleted offline");
          } catch (CmsVfsResourceNotFoundException e) {
              // ok
          } catch (CmsException e) {
              fail("Resource " + resource1 + " error:" + e);
          }
          try {
              cms.readResource(target1);
          } catch (CmsVfsResourceNotFoundException e) {
              fail("Resource " + target1 + " is not offline");
          } catch (CmsException e) {
              fail("Resource " + target1 + " error:" + e);
          }
          assertState(cms, target1, CmsResource.STATE_UNCHANGED);
  
      }
  
      /**
       * Test publishing new files.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testPublishNewFiles() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing publish new files");
  
          String source = "/folder2/subfolder21/image1.gif";
          String destination1 = "/folder2/image1_new.gif";
          String destination2 = "/folder2/image1_sibling1.gif";
          String destination3 = "/folder2/image1_sibling2.gif";
          String destination4 = "/folder2/image1_sibling3.gif";
  
          CmsProject onlineProject = cms.readProject("Online");
          CmsProject offlineProject = cms.readProject("Offline");
  
          // make four copies of a file to be published later
          cms.copyResource(source, destination1, CmsResource.COPY_AS_NEW);
          cms.copyResource(source, destination2, CmsResource.COPY_AS_SIBLING);
          cms.copyResource(source, destination3, CmsResource.COPY_AS_SIBLING);
          cms.copyResource(source, destination4, CmsResource.COPY_AS_SIBLING);
  
          storeResources(cms, destination1);
          storeResources(cms, destination2);
          storeResources(cms, destination3);
          storeResources(cms, destination4);
  
          // unlock all new resources
          // do not neet do unlock destination3 as it is a sibling of destination2
          cms.unlockResource(destination1);
          cms.unlockResource(destination2);
  
          // publish a new resource
          //
          OpenCms.getPublishManager().publishResource(cms, destination1);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // the file must be now available in the online project
          cms.getRequestContext().setCurrentProject(onlineProject);
          try {
              cms.readResource(destination1);
          } catch (CmsException e) {
              fail("Resource " + destination1 + " not found in online project:" + e);
          }
          assertFilter(cms, destination1, OpenCmsTestResourceFilter.FILTER_PUBLISHRESOURCE);
  
          // check if the file in the offline project is unchanged now
          cms.getRequestContext().setCurrentProject(offlineProject);
          assertState(cms, destination1, CmsResource.STATE_UNCHANGED);
  
          // publish a sibling without publishing other siblings
          //
          OpenCms.getPublishManager().publishResource(cms, destination2);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // the file must be now available in the online project
          cms.getRequestContext().setCurrentProject(onlineProject);
          try {
              cms.readResource(destination2);
          } catch (CmsException e) {
              fail("Resource " + destination2 + " not found in online project:" + e);
          }
          // the other siblings must not be available in the online project yet
          try {
              cms.readResource(destination3);
              fail("Resource " + destination3 + " should not available online yet");
          } catch (CmsVfsResourceNotFoundException e) {
              // ok
          } catch (CmsException e) {
              fail("Resource " + destination3 + " error:" + e);
          }
          try {
              cms.readResource(destination4);
              fail("Resource " + destination4 + " should not available online yet");
          } catch (CmsVfsResourceNotFoundException e) {
              // ok
          } catch (CmsException e) {
              fail("Resource " + destination4 + " error:" + e);
          }
  
          assertFilter(cms, destination2, OpenCmsTestResourceFilter.FILTER_PUBLISHRESOURCE);
  
          // check if the file in the offline project is unchanged now
          cms.getRequestContext().setCurrentProject(offlineProject);
          assertState(cms, destination2, CmsResource.STATE_UNCHANGED);
          // the other siblings in the offline project must still be shown as new
          assertState(cms, destination3, CmsResource.STATE_NEW);
          assertState(cms, destination4, CmsResource.STATE_NEW);
  
          // publish a sibling and all other siblings of it
          //
          OpenCms.getPublishManager().publishResource(
              cms,
              destination3,
              true,
              new CmsShellReport(cms.getRequestContext().getLocale()));
          OpenCms.getPublishManager().waitWhileRunning();
          // the file and its siblings must be now available in the online project
          cms.getRequestContext().setCurrentProject(onlineProject);
          try {
              cms.readResource(destination3);
          } catch (CmsException e) {
              fail("Resource " + destination3 + " not found in online project:" + e);
          }
          try {
              cms.readResource(destination4);
          } catch (CmsException e) {
              fail("Resource " + destination4 + " not found in online project:" + e);
          }
          assertFilter(cms, destination3, OpenCmsTestResourceFilter.FILTER_PUBLISHRESOURCE);
          assertFilter(cms, destination4, OpenCmsTestResourceFilter.FILTER_PUBLISHRESOURCE);
  
          // check if the file in the offline project is unchanged now
          cms.getRequestContext().setCurrentProject(offlineProject);
          assertState(cms, destination3, CmsResource.STATE_UNCHANGED);
          assertState(cms, destination4, CmsResource.STATE_UNCHANGED);
      }
  
      /**
       * Test publishing files within a new unpublished folder.<p>
       *
       * @throws Throwable if something goes wrong
       */
      @SuppressWarnings("deprecation")
      public void testPublishNewFilesInNewFolder() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing publishing new files in new folder");
  
          String source = "/folder1/image1.gif";
          String newFolder = "/new_folder/";
          String newFile = newFolder + "new_file";
          String newSibling = newFolder + "new_sibling";
  
          cms.createResource(newFolder, CmsResourceTypeFolder.getStaticTypeId());
          cms.unlockResource(newFolder);
          storeResources(cms, newFolder);
  
          // change to test project
          CmsProject project = getTestProject(cms);
          cms.getRequestContext().setCurrentProject(project);
  
          cms.copyResource(source, newFile, CmsResource.COPY_AS_NEW);
          cms.unlockResource(newFile);
  
          cms.copyResource(source, newSibling, CmsResource.COPY_AS_SIBLING);
          cms.unlockResource(newSibling);
  
          // direct publish of the new file will not publish the new file
          echo("Publishing the resource directly");
  
          storeResources(cms, newFile);
  
          boolean error = true;
          try {
              // this will generate an error in the log, ensure the test still continues
              OpenCmsTestLogAppender.setBreakOnError(false);
              OpenCms.getPublishManager().publishResource(cms, newFile);
              OpenCms.getPublishManager().waitWhileRunning();
          } catch (CmsMultiException e) {
              CmsVfsException ex = (CmsVfsException)e.getExceptions().get(0);
              if (ex.getMessageContainer().getKey() == org.opencms.db.Messages.ERR_DIRECT_PUBLISH_PARENT_NEW_2) {
                  error = false;
              }
          }
          // reset log to stop test on error
          OpenCmsTestLogAppender.setBreakOnError(true);
  
          if (error) {
              fail("A resource in a new folder could be published without generating an error!");
          }
  
          assertFilter(cms, newFile, OpenCmsTestResourceFilter.FILTER_EQUAL);
  
          // direct publish of another sibling will not publish the new sibling
          echo("Publishing another sibling");
          cms.lockResource(source);
          cms.setDateLastModified(source, System.currentTimeMillis(), false);
          cms.unlockResource(source);
  
          storeResources(cms, newSibling);
          OpenCms.getPublishManager().publishResource(
              cms,
              source,
              true,
              new CmsShellReport(cms.getRequestContext().getLocale()));
          OpenCms.getPublishManager().waitWhileRunning();
          assertFilter(cms, newSibling, OpenCmsTestResourceFilter.FILTER_EQUAL);
  
          // publishing the test project will not publish the new file or the new sibling
          echo("Publishing the test project");
          OpenCms.getPublishManager().publishProject(cms);
          OpenCms.getPublishManager().waitWhileRunning();
  
          assertFilter(cms, newFile, OpenCmsTestResourceFilter.FILTER_EQUAL);
          assertFilter(cms, newSibling, OpenCmsTestResourceFilter.FILTER_EQUAL);
  
          // publishing the offline project will publish the folder but not the resources
          echo("Publishing the offline project");
  
          cms.getRequestContext().setCurrentProject(cms.readProject("Offline"));
          OpenCms.getPublishManager().publishProject(cms);
          OpenCms.getPublishManager().waitWhileRunning();
  
          assertFilter(cms, newFolder, OpenCmsTestResourceFilter.FILTER_PUBLISHRESOURCE);
          assertState(cms, newFolder, CmsResource.STATE_UNCHANGED);
  
          assertFilter(cms, newFile, OpenCmsTestResourceFilter.FILTER_EQUAL);
          assertFilter(cms, newSibling, OpenCmsTestResourceFilter.FILTER_EQUAL);
  
          // publishing the test proejct again will now publish the resources
          echo("Publishing the test project again");
          cms.getRequestContext().setCurrentProject(project);
          OpenCms.getPublishManager().publishProject(cms);
          OpenCms.getPublishManager().waitWhileRunning();
  
          assertFilter(cms, newFile, OpenCmsTestResourceFilter.FILTER_PUBLISHRESOURCE);
          assertState(cms, newFile, CmsResource.STATE_UNCHANGED);
  
          assertFilter(cms, newSibling, OpenCmsTestResourceFilter.FILTER_PUBLISHRESOURCE);
          assertState(cms, newSibling, CmsResource.STATE_UNCHANGED);
  
      }
  
      /**
       * Test publishing a project with an iteration.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testPublishProjectGalore() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing publishing a project with an iteration");
  
          // publish project with several resources
          String resourcename = "/";
          cms.lockResource(resourcename);
          cms.setDateLastModified(resourcename, System.currentTimeMillis(), true);
  
          for (int i = 0; i < 10; i++) { // size of publish queue
              OpenCms.getPublishManager().publishProject(cms);
          }
          OpenCms.getPublishManager().waitWhileRunning();
  
          List<?> pubHistory = OpenCms.getPublishManager().getPublishHistory();
          assertEquals(10, pubHistory.size());
          CmsPublishJobFinished pubJob = (CmsPublishJobFinished)pubHistory.get(0);
          assertEquals(13 + 51, pubJob.getSize()); // folders + files
          for (int i = 1; i < 10; i++) {
              pubJob = (CmsPublishJobFinished)pubHistory.get(i);
              assertEquals("pubJob: " + i, 0, pubJob.getSize());
          }
      }
  
      /**
       * Tests publishing resources within a distinct project.<p>
       *
       * @throws Throwable if something goes wrong
       */
      @SuppressWarnings("deprecation")
      public void testPublishProjectLastmodified() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing publishing a distinct project");
  
          String path = "/folder1";
          String res1 = path + "/page1.html";
          String res2 = path + "/page2.html";
  
          long timestamp = System.currentTimeMillis();
  
          cms.getRequestContext().setCurrentProject(cms.readProject("Online"));
          storeResources(cms, res1);
  
          // change first resource in the offline project
          cms.getRequestContext().setCurrentProject(cms.readProject("Offline"));
          cms.lockResource(res1);
          cms.setDateLastModified(res1, timestamp, false);
  
          cms.unlockProject(cms.getRequestContext().getCurrentProject().getUuid());
  
          // create a new project
          CmsProject project = getTestProject(cms);
          cms.getRequestContext().setCurrentProject(project);
          cms.copyResourceToProject(path);
  
          // and change another resource in this project
          cms.lockResource(res2);
          cms.setDateLastModified(res2, timestamp, false);
          cms.unlockProject(cms.getRequestContext().getCurrentProject().getUuid());
          storeResources(cms, res2);
  
          // when the project is published, only the second resource will be published
          OpenCms.getPublishManager().publishProject(cms);
          OpenCms.getPublishManager().waitWhileRunning();
  
          cms.getRequestContext().setCurrentProject(cms.readProject("Online"));
          assertFilter(cms, res1, OpenCmsTestResourceFilter.FILTER_EQUAL);
          assertFilter(cms, res2, OpenCmsTestResourceFilter.FILTER_PUBLISHRESOURCE);
          assertDateLastModified(cms, res2, timestamp);
  
          echo("Testing publishing in different projects");
  
          // change back to the test project, create a new resource
          String res3 = path + "/testPublishProjectLastmodified.txt";
          cms.getRequestContext().setCurrentProject(project);
          cms.createResource(res3, CmsResourceTypePlain.getStaticTypeId());
  
          // change to offline project, copy resource as sibling
          // this will also change the project in which the source was lastmodified (now "Offline")
          String res4 = "/testPublishProjectLastmodified.txt";
          cms.getRequestContext().setCurrentProject(cms.readProject("Offline"));
          cms.copyResource(res3, res4, CmsResource.COPY_AS_SIBLING);
  
          // check the current state of the resources
          assertState(cms, res3, CmsResource.STATE_NEW);
          assertState(cms, res4, CmsResource.STATE_NEW);
  
          // publish the test project
          cms.getRequestContext().setCurrentProject(project);
          cms.unlockProject(project.getUuid());
          OpenCms.getPublishManager().publishProject(cms);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // the resource inside the offline project must not be published (not in test project)
          assertState(cms, res3, CmsResource.STATE_NEW);
  
          // the sibling outside the offline project must not be published (not in test project)
          assertState(cms, res4, CmsResource.STATE_NEW);
  
          // publish the root folder of the test project within the test project
          OpenCms.getPublishManager().publishResource(
              cms,
              path,
              true,
              new CmsShellReport(cms.getRequestContext().getLocale()));
          OpenCms.getPublishManager().waitWhileRunning();
  
          // all resources inside the folder must be published, even if not in test project
          assertState(cms, res3, CmsResource.STATE_NEW);
  
          // as well as all siblings, even if not in test project
          assertState(cms, res4, CmsResource.STATE_NEW);
      }
  
      /**
       * Tests publishing a folder containing resources modified within a distinct project.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testPublishProjectLastmodifiedFolder() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing publishing a folder containing resources modified within a distinct project");
  
          String path = "/folder1";
          String res1 = path + "/index.html";
  
          // change first resource in the offline project
          cms.getRequestContext().setCurrentProject(cms.readProject("Offline"));
          cms.lockResource(res1);
          cms.setDateLastModified(res1, System.currentTimeMillis(), false);
  
          cms.unlockProject(cms.getRequestContext().getCurrentProject().getUuid());
          storeResources(cms, res1);
  
          // create a new project
          CmsProject project = getTestProject(cms);
          cms.getRequestContext().setCurrentProject(project);
          cms.copyResourceToProject(path);
  
          // check the publish list
          // the resource modified in the Offline project should not be there
          CmsPublishList pubList = OpenCms.getPublishManager().getPublishList(cms, cms.readResource(path), false);
          assertTrue(pubList.isPublishSubResources());
          assertTrue(pubList.getAllResources().isEmpty());
      }
  
      /**
       * Test an issue with the publish queue when trying to publish a folder that has already a subresource that is being published.<p>
       *
       * FIRST TRY:
       * - modify and lock a file, publish the file
       * - modify and lock a second file, publish the folder
       *
       * @throws Throwable if something goes wrong
       */
      public void testPublishQueueIssue1() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing publishing a folder with a subresource in the publish queue, first try");
  
          // stop the publish engine to ensure that the file will not be published before we try to publish the folder
          OpenCms.getPublishManager().stopPublishing();
  
          // touch and publish a file in a folder
          String folder = "/folder1/subfolder11/subsubfolder111/";
          String file = folder + "image.gif";
          cms.lockResource(file);
          cms.setDateLastModified(file, System.currentTimeMillis(), false);
          OpenCms.getPublishManager().publishResource(cms, file);
  
          // check the publish queue
          assertEquals(1, OpenCms.getPublishManager().getPublishQueue().size());
  
          // touch a second file and publish the file
          String file2 = folder + "text.txt";
          cms.lockResource(file2);
          cms.setDateLastModified(file2, System.currentTimeMillis(), false);
          OpenCms.getPublishManager().publishResource(cms, folder);
  
          // check the publish queue
          assertEquals(2, OpenCms.getPublishManager().getPublishQueue().size());
  
          // start the publish engine
          OpenCms.getPublishManager().startPublishing();
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check the publish queue
          assertEquals(0, OpenCms.getPublishManager().getPublishQueue().size());
  
          // check publish reports
          List<?> history = OpenCms.getPublishManager().getPublishHistory();
          CmsPublishJobFinished publishJob = (CmsPublishJobFinished)history.get(history.size() - 2);
          String reportContents = new String(OpenCms.getPublishManager().getReportContents(publishJob));
          // check if the report states that the destination was published successfully
          if (!reportContents.matches("(?s)" + "(.*)" + file + "(.*)" + "o.k." + "(.*)")) {
              System.err.println(reportContents);
              fail("publish report contains errors");
          }
          publishJob = (CmsPublishJobFinished)history.get(history.size() - 1);
          reportContents = new String(OpenCms.getPublishManager().getReportContents(publishJob));
          // check if the report states that the destination was published successfully
          if (!reportContents.matches("(?s)" + "(.*)" + file2 + "(.*)" + "o.k." + "(.*)")) {
              System.err.println(reportContents);
              fail("publish report contains errors");
          }
  
          // check the published resources
          assertState(cms, folder, CmsResourceState.STATE_UNCHANGED);
          assertLock(cms, folder, CmsLockType.UNLOCKED);
          assertState(cms, file, CmsResourceState.STATE_UNCHANGED);
          assertLock(cms, file, CmsLockType.UNLOCKED);
          assertState(cms, file2, CmsResourceState.STATE_UNCHANGED);
          assertLock(cms, file2, CmsLockType.UNLOCKED);
      }
  
      /**
       * Test an issue with the publish queue when trying to publish a folder that has already a subresource that is being published.<p>
       *
       * SECOND TRY:
       * - modify and lock a file, publish the file
       * - lock the folder, modify a second file, publish the folder
       *
       * @throws Throwable if something goes wrong
       */
      public void testPublishQueueIssue2() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing publishing a folder with a subresource in the publish queue, second try");
  
          // stop the publish engine to ensure that the file will not be published before we try to publish the folder
          OpenCms.getPublishManager().stopPublishing();
  
          // touch and publish a file in a folder
          String folder = "/folder1/subfolder11/subsubfolder111/";
          String file = folder + "image.gif";
          cms.lockResource(file);
          cms.setDateLastModified(file, System.currentTimeMillis(), false);
          OpenCms.getPublishManager().publishResource(cms, file);
  
          // check the publish queue
          assertEquals(1, OpenCms.getPublishManager().getPublishQueue().size());
  
          // touch a second file and publish the folder
          String file2 = folder + "text.txt";
          cms.lockResource(folder);
          cms.setDateLastModified(file2, System.currentTimeMillis(), false);
          OpenCms.getPublishManager().publishResource(cms, folder);
  
          // check the publish queue
          assertEquals(2, OpenCms.getPublishManager().getPublishQueue().size());
  
          // start the publish engine
          OpenCms.getPublishManager().startPublishing();
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check the publish queue
          assertEquals(0, OpenCms.getPublishManager().getPublishQueue().size());
  
          // check publish reports
          List<?> history = OpenCms.getPublishManager().getPublishHistory();
          CmsPublishJobFinished publishJob = (CmsPublishJobFinished)history.get(history.size() - 2);
          String reportContents = new String(OpenCms.getPublishManager().getReportContents(publishJob));
          // check if the report states that the destination was published successfully
          if (!reportContents.matches("(?s)" + "(.*)" + file + "(.*)" + "o.k." + "(.*)")) {
              System.err.println(reportContents);
              fail("publish report contains errors");
          }
          publishJob = (CmsPublishJobFinished)history.get(history.size() - 1);
          reportContents = new String(OpenCms.getPublishManager().getReportContents(publishJob));
          // check if the report states that the destination was published successfully
          if (!reportContents.matches("(?s)" + "(.*)" + file2 + "(.*)" + "o.k." + "(.*)")) {
              System.err.println(reportContents);
              fail("publish report contains errors");
          }
  
          cms.unlockResource(folder);
          // check the published files
          assertState(cms, file, CmsResourceState.STATE_UNCHANGED);
          assertLock(cms, file, CmsLockType.UNLOCKED);
          assertState(cms, file2, CmsResourceState.STATE_UNCHANGED);
          assertLock(cms, file2, CmsLockType.UNLOCKED);
          assertState(cms, folder, CmsResourceState.STATE_UNCHANGED);
          assertLock(cms, folder, CmsLockType.UNLOCKED);
      }
  
      /**
       * Test an issue with the publish queue when trying to publish a folder that has already a subresource that is being published.<p>
       *
       * THIRD TRY:
       * - modify and lock a file, publish the file
       * - modify and lock the folder, publish the folder
       *
       * @throws Throwable if something goes wrong
       */
      public void testPublishQueueIssue3() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing publishing a folder with a subresource in the publish queue, third try");
  
          // stop the publish engine to ensure that the file will not be published before we try to publish the folder
          OpenCms.getPublishManager().stopPublishing();
  
          // touch and publish a file in a folder
          String folder = "/folder1/subfolder11/subsubfolder111/";
          String file = folder + "image.gif";
          cms.lockResource(file);
          cms.setDateLastModified(file, System.currentTimeMillis(), false);
          OpenCms.getPublishManager().publishResource(cms, file);
  
          // check the publish queue
          assertEquals(1, OpenCms.getPublishManager().getPublishQueue().size());
  
          // touch a second file and publish the folder
          cms.lockResource(folder);
          String file2 = folder + "text.txt";
          cms.setDateLastModified(file2, System.currentTimeMillis(), false);
          cms.setDateLastModified(folder, System.currentTimeMillis(), false);
          OpenCms.getPublishManager().publishResource(cms, folder);
  
          // check the publish queue
          assertEquals(2, OpenCms.getPublishManager().getPublishQueue().size());
  
          // start the publish engine
          OpenCms.getPublishManager().startPublishing();
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check the publish queue
          assertEquals(0, OpenCms.getPublishManager().getPublishQueue().size());
  
          // check publish reports
          List<?> history = OpenCms.getPublishManager().getPublishHistory();
          CmsPublishJobFinished publishJob = (CmsPublishJobFinished)history.get(history.size() - 2);
          String reportContents = new String(OpenCms.getPublishManager().getReportContents(publishJob));
          // check if the report states that the destination was published successfully
          if (!reportContents.matches("(?s)" + "(.*)" + file + "(.*)" + "o.k." + "(.*)")) {
              System.err.println(reportContents);
              fail("publish report contains errors");
          }
          publishJob = (CmsPublishJobFinished)history.get(history.size() - 1);
          reportContents = new String(OpenCms.getPublishManager().getReportContents(publishJob));
          // check if the report states that the destination was published successfully
          if (!reportContents.matches("(?s)" + "(.*)" + file2 + "(.*)" + "o.k." + "(.*)")) {
              System.err.println(reportContents);
              fail("publish report contains errors");
          }
          if (!reportContents.matches("(?s)" + "(.*)" + folder + "(.*)" + "o.k." + "(.*)")) {
              System.err.println(reportContents);
              fail("publish report contains errors");
          }
  
          // check the published resources
          assertState(cms, folder, CmsResourceState.STATE_UNCHANGED);
          assertLock(cms, folder, CmsLockType.UNLOCKED);
          assertState(cms, file, CmsResourceState.STATE_UNCHANGED);
          assertLock(cms, file, CmsLockType.UNLOCKED);
          assertState(cms, file2, CmsResourceState.STATE_UNCHANGED);
          assertLock(cms, file2, CmsLockType.UNLOCKED);
      }
  
      /**
       * Test publishing of related files.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testPublishRelatedFiles() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing publishing of related files");
  
          String resName = "index.html";
  
          // touch the file to publish
          cms.lockResource(resName);
          cms.setDateLastModified(resName, System.currentTimeMillis(), false);
          CmsResource resource = cms.readResource(resName, CmsResourceFilter.DEFAULT);
  
          // get the publish list
          CmsPublishList pubList = OpenCms.getPublishManager().getPublishList(cms, resource, false);
          // just check the publish list
          assertTrue(pubList.getDeletedFolderList().isEmpty());
          assertTrue(pubList.getFolderList().isEmpty());
          assertEquals(1, pubList.getFileList().size());
          assertTrue(pubList.getFileList().contains(resource));
  
          // get the list of related resources, which should be still empty, since the related resource has unchanged state
          CmsPublishList relatedList = OpenCms.getPublishManager().getRelatedResourcesToPublish(cms, pubList);
          // check the publish list has not been touched
          assertTrue(pubList.getDeletedFolderList().isEmpty());
          assertTrue(pubList.getFolderList().isEmpty());
          assertEquals(1, pubList.getFileList().size());
          assertTrue(pubList.getFileList().contains(resource));
          // check the related publish list
          assertTrue(relatedList.getAllResources().isEmpty());
  
          String relResName = "folder1/image2.gif";
  
          // touch the related resource
          cms.lockResource(relResName);
          cms.setDateLastModified(relResName, System.currentTimeMillis(), false);
          CmsResource relatedRes = cms.readResource(relResName, CmsResourceFilter.DEFAULT);
  
          // get the list of related resources again
          relatedList = OpenCms.getPublishManager().getRelatedResourcesToPublish(cms, pubList);
          // check the publish list has not been touched
          assertTrue(pubList.getDeletedFolderList().isEmpty());
          assertTrue(pubList.getFolderList().isEmpty());
          assertEquals(1, pubList.getFileList().size());
          assertTrue(pubList.getFileList().contains(resource));
          // check the related publish list
          assertTrue(relatedList.getDeletedFolderList().isEmpty());
          assertTrue(relatedList.getFolderList().isEmpty());
          assertEquals(1, relatedList.getFileList().size());
          assertTrue(relatedList.getFileList().contains(relatedRes));
  
          CmsPublishList mergedList = OpenCms.getPublishManager().mergePublishLists(cms, pubList, relatedList);
          // check the publish list has not been touched
          assertTrue(pubList.getDeletedFolderList().isEmpty());
          assertTrue(pubList.getFolderList().isEmpty());
          assertEquals(1, pubList.getFileList().size());
          assertTrue(pubList.getFileList().contains(resource));
          // check the related publish list has not been touched
          assertTrue(relatedList.getDeletedFolderList().isEmpty());
          assertTrue(relatedList.getFolderList().isEmpty());
          assertEquals(1, relatedList.getFileList().size());
          assertTrue(relatedList.getFileList().contains(relatedRes));
          // check the merged publish list
          assertTrue(mergedList.getDeletedFolderList().isEmpty());
          assertTrue(mergedList.getFolderList().isEmpty());
          assertEquals(2, mergedList.getFileList().size());
          assertTrue(mergedList.getFileList().contains(relatedRes));
          assertTrue(mergedList.getFileList().contains(resource));
      }
  
      /**
       * Test publishing of related files taken a whole folder structure.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testPublishRelatedFilesInFolder() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing publishing of related files taken a whole folder structure");
  
          String resName = "/folder1/";
  
          // touch the file to publish
          cms.lockResource(resName);
          cms.setDateLastModified(resName, System.currentTimeMillis(), true);
          CmsResource resource = cms.readResource(resName, CmsResourceFilter.DEFAULT);
  
          // get the publish list
          CmsPublishList pubList = OpenCms.getPublishManager().getPublishList(cms, resource, false);
          // just check the publish list
          assertTrue(pubList.getDeletedFolderList().isEmpty());
          assertFalse(pubList.getFolderList().isEmpty());
          assertFalse(pubList.getFileList().isEmpty());
  
          // get the list of related resources, which should be still empty, since the related resource has unchanged state
          CmsPublishList relatedList = OpenCms.getPublishManager().getRelatedResourcesToPublish(cms, pubList);
          // check the publish list has not been touched
          assertTrue(pubList.getDeletedFolderList().isEmpty());
          assertFalse(pubList.getFolderList().isEmpty());
          assertFalse(pubList.getFileList().isEmpty());
          // check the related publish list
          assertTrue(relatedList.getAllResources().isEmpty());
  
          CmsPublishList mergedList = OpenCms.getPublishManager().mergePublishLists(cms, pubList, relatedList);
          // check the publish list has not been touched
          assertTrue(pubList.getDeletedFolderList().isEmpty());
          assertFalse(pubList.getFolderList().isEmpty());
          assertFalse(pubList.getFileList().isEmpty());
          // check the related publish list
          assertTrue(relatedList.getAllResources().isEmpty());
          // check the merged publish list
          assertEquals(pubList.getDeletedFolderList(), mergedList.getDeletedFolderList());
          assertEquals(pubList.getFolderList(), mergedList.getFolderList());
          assertEquals(pubList.getFileList(), mergedList.getFileList());
      }
  
      /**
       * Test publishing of related files in an unpublished folder.<p>
       *
       * @throws Throwable if something goes wrong
       */
      @SuppressWarnings("deprecation")
      public void testPublishRelatedFilesInNewFolder() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing publishing of related files in an unpublished folder");
  
          String folderName = "/testFolder/";
          String fileName = "/testFolder/testFile.gif";
          String srcName = "sourceTest.html";
  
          // create folder and file
          cms.createResource(folderName, CmsResourceTypeFolder.RESOURCE_TYPE_ID);
          cms.createResource(fileName, CmsResourceTypeImage.getStaticTypeId());
  
          // create source file
          String content = CmsXmlPageFactory.createDocument(Locale.ENGLISH, CmsEncoder.ENCODING_UTF_8);
          CmsResource source = cms.createResource(
              srcName,
              CmsResourceTypeXmlPage.getStaticTypeId(),
              content.getBytes(CmsEncoder.ENCODING_UTF_8),
              null);
  
          CmsFile file = cms.readFile(srcName);
          CmsXmlPage page = CmsXmlPageFactory.unmarshal(cms, file);
          String element = "test";
          page.addValue(element, Locale.ENGLISH);
  
          // set the strong link
          content = "<img src='" + fileName + "'>";
          page.setStringValue(cms, element, Locale.ENGLISH, content);
  
          file.setContents(page.marshal());
          cms.writeFile(file);
  
          // get the publish list
          CmsPublishList pubList = OpenCms.getPublishManager().getPublishList(cms, source, false);
          // just check the publish list
          assertTrue(pubList.getDeletedFolderList().isEmpty());
          assertTrue(pubList.getFolderList().isEmpty());
          assertEquals(1, pubList.getFileList().size());
  
          // get the list of related resources
          CmsPublishList relatedList = OpenCms.getPublishManager().getRelatedResourcesToPublish(cms, pubList);
          // check the publish list has not been touched
          assertTrue(pubList.getDeletedFolderList().isEmpty());
          assertTrue(pubList.getFolderList().isEmpty());
          assertEquals(1, pubList.getFileList().size());
          // check the related publish list
          assertTrue(relatedList.getDeletedFolderList().isEmpty());
          assertEquals(1, relatedList.getFolderList().size());
          assertEquals(1, relatedList.getFileList().size());
  
          CmsPublishList mergedList = OpenCms.getPublishManager().mergePublishLists(cms, pubList, relatedList);
          // check the publish list has not been touched
          assertTrue(pubList.getDeletedFolderList().isEmpty());
          assertTrue(pubList.getFolderList().isEmpty());
          assertEquals(1, pubList.getFileList().size());
          // check the related publish list
          assertTrue(relatedList.getDeletedFolderList().isEmpty());
          assertEquals(1, relatedList.getFolderList().size());
          assertEquals(1, relatedList.getFileList().size());
          // check the merged publish list
          assertTrue(mergedList.getDeletedFolderList().isEmpty());
          assertEquals(1, mergedList.getFolderList().size());
          assertEquals(2, mergedList.getFileList().size());
      }
  
      /**
       * Test publishing a replaced file.<p>
       *
       * @throws Throwable if something goes wrong
       */
      @SuppressWarnings("deprecation")
      public void testPublishReplacedFile() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing publishing a replaced file");
  
          String resourcename = "testReplace.txt";
          byte[] orgCnt = "original content".getBytes();
          byte[] newCnt = "new content".getBytes();
  
          // create new file
          cms.createResource(resourcename, CmsResourceTypePlain.getStaticTypeId(), orgCnt, null);
          // assert offline
          assertContent(cms, resourcename, orgCnt);
          // publish
          OpenCms.getPublishManager().publishResource(cms, resourcename);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // assert online
          cms.getRequestContext().setCurrentProject(cms.readProject(CmsProject.ONLINE_PROJECT_ID));
          assertContent(cms, resourcename, orgCnt);
          cms = getCmsObject();
  
          // replace
          cms.lockResource(resourcename);
          cms.replaceResource(resourcename, CmsResourceTypePlain.getStaticTypeId(), newCnt, null);
          // assert offline
          assertContent(cms, resourcename, newCnt);
          // publish again
          OpenCms.getPublishManager().publishResource(cms, resourcename);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // assert online
          cms.getRequestContext().setCurrentProject(cms.readProject(CmsProject.ONLINE_PROJECT_ID));
          assertContent(cms, resourcename, newCnt);
      }
  
      /**
       * Test publishing a single resource with an iteration.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testPublishResourceGalore() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing publishing a single resource with an iteration");
  
          // publish directly with a single resource
          String resourcename = "index.html";
  
          cms.lockResource(resourcename);
          cms.setDateLastModified(resourcename, System.currentTimeMillis(), true);
  
          for (int i = 0; i < 10; i++) {
              OpenCms.getPublishManager().publishResource(cms, resourcename);
          }
          OpenCms.getPublishManager().waitWhileRunning();
  
          List<?> pubHistory = OpenCms.getPublishManager().getPublishHistory();
          assertEquals(10, pubHistory.size());
          CmsPublishJobFinished pubJob = (CmsPublishJobFinished)pubHistory.get(0);
          assertEquals(1, pubJob.getSize());
          for (int i = 1; i < 10; i++) {
              pubJob = (CmsPublishJobFinished)pubHistory.get(i);
              assertEquals("pubJob: " + i, 0, pubJob.getSize());
          }
      }
  
      /**
       * Tests the publishing of resource property.<p>
       *
       * take 2 siblings (s1 and s2):
       * - change a shared property of s1
       * - s2 will be marked as changed also
       * - direct publishing s1, should also change s2
       *
       * @throws Throwable if something goes wrong
       */
      public void testPublishResourceProperty() throws Throwable {
  
          CmsObject cms = getCmsObject();
          String source = "/folder1/page1.html";
          String sibling = "/folder1/sibling1.html";
          echo("testing the publishing of structure property");
  
          storeResources(cms, source);
          storeResources(cms, sibling);
  
          cms.lockResource(sibling);
          cms.writePropertyObject(sibling, new CmsProperty(CmsPropertyDefinition.PROPERTY_TITLE, null, "test4"));
          cms.unlockResource(sibling);
  
          assertFilter(cms, sibling, OpenCmsTestResourceFilter.FILTER_WRITEPROPERTY);
          assertFilter(cms, source, OpenCmsTestResourceFilter.FILTER_WRITEPROPERTY);
  
          storeResources(cms, source);
          storeResources(cms, sibling);
  
          List<?> pubList = OpenCms.getPublishManager().getPublishList(cms).getFileList();
          assertEquals(pubList.size(), 2);
          assertEquals(((CmsResource)pubList.get(0)).getRootPath(), cms.getRequestContext().addSiteRoot(source));
          assertEquals(((CmsResource)pubList.get(1)).getRootPath(), cms.getRequestContext().addSiteRoot(sibling));
  
          assertFilter(cms, cms.readResource(source), OpenCmsTestResourceFilter.FILTER_EQUAL);
          assertFilter(cms, cms.readResource(sibling), OpenCmsTestResourceFilter.FILTER_EQUAL);
  
          OpenCms.getPublishManager().publishResource(cms, source);
          OpenCms.getPublishManager().waitWhileRunning();
  
          assertFilter(cms, source, OpenCmsTestResourceFilter.FILTER_PUBLISHRESOURCE);
          assertFilter(cms, sibling, OpenCmsTestResourceFilter.FILTER_PUBLISHRESOURCE);
  
          storeResources(cms, source);
  
          assertTrue(OpenCms.getPublishManager().getPublishList(cms).getFileList().isEmpty());
      }
  
      /**
       * Test publishing a resource with related resources with an iteration.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testPublishResourceWithRelatedGalore() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing publishing a resource with related resources with an iteration");
  
          // publish directly with a single resource and related resource
          String resourcename = "index.html";
  
          // touch everything, so the involved resources can be published
          cms.lockResource("/");
          cms.setDateLastModified("/", System.currentTimeMillis(), true);
  
          // get the list of involved files
          CmsPublishList pubList = OpenCms.getPublishManager().getPublishList(cms, cms.readResource(resourcename), false);
          CmsPublishList relatedList = OpenCms.getPublishManager().getRelatedResourcesToPublish(cms, pubList);
          pubList = OpenCms.getPublishManager().mergePublishLists(cms, pubList, relatedList);
  
          for (int i = 0; i < 10; i++) {
              // create a new publish list
              CmsPublishList publishList = OpenCms.getPublishManager().getPublishList(
                  cms,
                  cms.readResource(resourcename, CmsResourceFilter.ALL),
                  false);
              // get the related resources
              CmsPublishList relResources = OpenCms.getPublishManager().getRelatedResourcesToPublish(cms, publishList);
              // merge the two publish lists
              publishList = OpenCms.getPublishManager().mergePublishLists(cms, publishList, relResources);
              // publish
              OpenCms.getPublishManager().publishProject(
                  cms,
                  new CmsShellReport(cms.getRequestContext().getLocale()),
                  publishList);
          }
          OpenCms.getPublishManager().waitWhileRunning();
  
          List<?> pubHistory = OpenCms.getPublishManager().getPublishHistory();
          assertEquals(10, pubHistory.size());
          CmsPublishJobFinished pubJob = (CmsPublishJobFinished)pubHistory.get(0);
          assertEquals(pubList.size(), pubJob.getSize());
          for (int i = 1; i < 10; i++) {
              pubJob = (CmsPublishJobFinished)pubHistory.get(i);
              assertEquals("pubJob: " + i, 0, pubJob.getSize());
          }
      }
  
      /**
       * Tests the publishing of structure/shared properties.<p>
       *
       * take 2 siblings (s1 and s2):
       * - change a individual property of s1
       * - change a shared property of s2
       * - direct publishing s2, should not change s1
       *
       * @throws Throwable if something goes wrong
       */
      public void testPublishSiblings() throws Throwable {
  
          CmsObject cms = getCmsObject();
          String source = "/folder1/page1.html";
          String sibling = "/folder1/sibling1.html";
          echo("testing the publishing of structure/shared properties");
  
          storeResources(cms, source);
          storeResources(cms, sibling);
  
          cms.lockResource(sibling); // individual property
          cms.writePropertyObject(sibling, new CmsProperty(CmsPropertyDefinition.PROPERTY_TITLE, "test2", null));
          cms.unlockResource(sibling);
  
          assertFilter(cms, sibling, OpenCmsTestResourceFilter.FILTER_WRITEPROPERTY);
  
          cms.lockResource(source); // shared property
          cms.writePropertyObject(source, new CmsProperty(CmsPropertyDefinition.PROPERTY_NAVTEXT, null, "test3"));
          cms.unlockResource(source);
  
          assertFilter(cms, source, OpenCmsTestResourceFilter.FILTER_WRITEPROPERTY);
  
          storeResources(cms, source);
          storeResources(cms, sibling);
  
          List<?> pubList = OpenCms.getPublishManager().getPublishList(cms).getFileList();
          assertEquals(pubList.size(), 2);
          assertEquals(((CmsResource)pubList.get(0)).getRootPath(), cms.getRequestContext().addSiteRoot(source));
          assertEquals(((CmsResource)pubList.get(1)).getRootPath(), cms.getRequestContext().addSiteRoot(sibling));
  
          assertFilter(cms, cms.readResource(source), OpenCmsTestResourceFilter.FILTER_EQUAL);
          assertFilter(cms, cms.readResource(sibling), OpenCmsTestResourceFilter.FILTER_EQUAL);
  
          OpenCms.getPublishManager().publishResource(cms, source);
          OpenCms.getPublishManager().waitWhileRunning();
  
          assertFilter(cms, source, OpenCmsTestResourceFilter.FILTER_PUBLISHRESOURCE);
          assertFilter(cms, sibling, OpenCmsTestResourceFilter.FILTER_EQUAL);
  
          storeResources(cms, source);
  
          pubList = OpenCms.getPublishManager().getPublishList(cms).getFileList();
          assertEquals(pubList.size(), 1);
          assertEquals(((CmsResource)pubList.get(0)).getRootPath(), cms.getRequestContext().addSiteRoot(sibling));
          assertFilter(cms, cms.readResource(sibling), OpenCmsTestResourceFilter.FILTER_EQUAL);
  
          OpenCms.getPublishManager().publishResource(cms, sibling);
          OpenCms.getPublishManager().waitWhileRunning();
  
          assertFilter(cms, sibling, OpenCmsTestResourceFilter.FILTER_PUBLISHRESOURCE);
          assertFilter(cms, source, OpenCmsTestResourceFilter.FILTER_EQUAL);
      }
  
      /**
       * Tests the publishing of structure property.<p>
       *
       * take 2 siblings (s1 and s2):
       * - change a individual property of s1
       * - s2 remains unchanged
       * - direct publishing s1, should not change s2
       *
       * @throws Throwable if something goes wrong
       */
      public void testPublishStructureProperty() throws Throwable {
  
          CmsObject cms = getCmsObject();
          String source = "/folder1/page1.html";
          String sibling = "/folder1/sibling1.html";
          echo("testing the publishing of structure property");
  
          cms.copyResource(source, sibling, CmsResource.COPY_AS_SIBLING);
  
          // be sure everything is published
          cms.unlockProject(cms.getRequestContext().getCurrentProject().getUuid());
          OpenCms.getPublishManager().publishProject(cms);
          OpenCms.getPublishManager().waitWhileRunning();
  
          storeResources(cms, source);
          storeResources(cms, sibling);
  
          cms.lockResource(sibling);
          cms.writePropertyObject(sibling, new CmsProperty(CmsPropertyDefinition.PROPERTY_TITLE, "test1", null));
          cms.unlockResource(sibling);
  
          assertFilter(cms, sibling, OpenCmsTestResourceFilter.FILTER_WRITEPROPERTY);
          assertFilter(cms, source, OpenCmsTestResourceFilter.FILTER_SIBLING_PROPERTY);
  
          storeResources(cms, source);
          storeResources(cms, sibling);
  
          List<?> pubList = OpenCms.getPublishManager().getPublishList(cms).getFileList();
          assertEquals(pubList.size(), 1);
          assertEquals(((CmsResource)pubList.get(0)).getRootPath(), cms.getRequestContext().addSiteRoot(sibling));
          assertFilter(cms, cms.readResource(sibling), OpenCmsTestResourceFilter.FILTER_EQUAL);
  
          OpenCms.getPublishManager().publishProject(cms);
          OpenCms.getPublishManager().waitWhileRunning();
  
          assertFilter(cms, sibling, OpenCmsTestResourceFilter.FILTER_PUBLISHRESOURCE);
          assertFilter(cms, source, OpenCmsTestResourceFilter.FILTER_EQUAL);
      }
  
      /**
       * Test publishing a temporary project.<p>
       *
       * @throws Throwable if something goes wrong
       */
      @SuppressWarnings("deprecation")
      public void testPublishTemporaryProject() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing publish temporary project");
  
          String source = "/folder1/testfile.txt";
          CmsProject onlineProject = cms.readProject("Online");
          CmsProject offlineProject = cms.readProject("Offline");
  
          //create a new temp project
          CmsProject tempProject = cms.createProject(
              "deleteme",
              "Temp project to be deleted after publish",
              "Users",
              "Projectmanagers",
              CmsProject.PROJECT_TYPE_TEMPORARY);
          cms.copyResourceToProject("/");
          cms.getRequestContext().setCurrentProject(tempProject);
  
          // now create a new resource
          cms.createResource(source, CmsResourceTypePlain.getStaticTypeId());
          cms.unlockResource(source);
          storeResources(cms, source);
  
          cms.readResource(source);
  
          //publish the project
          OpenCms.getPublishManager().publishProject(cms);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // now try to read the resource
          try {
              cms.readResource(source);
          } catch (CmsException e) {
              fail(
                  "Resource "
                      + source
                      + " not found in project "
                      + cms.getRequestContext().getCurrentProject().toString()
                      + ":"
                      + e);
          }
  
          // now try to read the resource in the online project
          cms.getRequestContext().setCurrentProject(onlineProject);
          try {
              cms.readResource(source);
          } catch (CmsException e) {
              fail(
                  "Resource "
                      + source
                      + " not found in project "
                      + cms.getRequestContext().getCurrentProject().toString()
                      + ":"
                      + e);
          }
  
          // check if the state of the resource is ok
          assertFilter(cms, source, OpenCmsTestResourceFilter.FILTER_PUBLISHRESOURCE);
  
          // check if the file in the offline project is unchancged now
          cms.getRequestContext().setCurrentProject(offlineProject);
          assertState(cms, source, CmsResource.STATE_UNCHANGED);
  
          // check if the project is deleted
          try {
              cms.readProject("deleteme");
              fail("Temporary project still existing");
          } catch (CmsException e) {
              // to nothing, this exception must be thrown
          }
  
      }
  
      /**
       * Returns a project for testing purposes.<p>
       *
       * @param cms the cms object
       *
       * @return a project for testing purposes
       */
      private CmsProject getTestProject(CmsObject cms) {
  
          CmsProject project = null;
          try {
              project = cms.readProject("Test");
          } catch (Exception e) {
              try {
                  project = cms.createProject("Test", "", "Users", "Administrators");
              } catch (Exception ee) {
                  // noop
              }
          }
          return project;
      }
  }
  