Opens the site-map specified.<p>

@param sitePath the site path to the site-map folder
@param siteChange in case the site was changed
489862
public void openSiteMap(String sitePath, boolean siteChange) {

        String uri = CmsCoreProvider.get().link(
            CmsCoreProvider.get().getUri()) + "?" + CmsCoreData.PARAM_PATH + "=" + sitePath;
        if (!siteChange) {
            uri += "&" + CmsCoreData.PARAM_RETURNCODE + "=" + getData().getReturnCode();
        }
        Window.Location.replace(uri);
    }
744361
public void edit(
        CmsClientSitemapEntry entry,
        List<CmsPropertyModification> propertyChanges,
        final CmsReloadMode reloadStatus) {

        CmsSitemapChange change = getChangeForEdit(entry, propertyChanges);
        final String updateTarget = ((reloadStatus == CmsReloadMode.reloadParent))
        ? getParentEntry(entry).getSitePath()
        : entry.getSitePath();
        Command callback = new Command() {

            public void execute() {

                if ((reloadStatus == CmsReloadMode.reloadParent) || (reloadStatus == CmsReloadMode.reloadEntry)) {
                    updateEntry(updateTarget);
                }
            }
        };
        if (change != null) {
            commitChange(change, callback);
        }
    }
848711
@SuppressWarnings("deprecation")
    private void adjustFolderType(CmsResource siteRootResource) throws CmsLoaderException, CmsException {

        if (OpenCms.getResourceManager().getResourceType(
            CmsResourceTypeFolder.RESOURCE_TYPE_NAME) == OpenCms.getResourceManager().getResourceType(
                siteRootResource)) {

            siteRootResource.setType(
                OpenCms.getResourceManager().getResourceType(
                    CmsResourceTypeFolderSubSitemap.TYPE_SUBSITEMAP).getTypeId());
            m_cms.writeResource(siteRootResource);
        }
    }
604936
protected void onSiteChange(String site) {

        try {
            m_treeData.removeAllItems();
            CmsObject rootCms = OpenCms.initCmsObject(A_CmsUI.getCmsObject());
            rootCms.getRequestContext().setSiteRoot("");
            CmsResource siteRootResource = rootCms.readResource(site);

            m_treeData.initRoot(rootCms, siteRootResource);
            m_fileTree.expandItem(siteRootResource.getStructureId());
            m_siteRoot = site;
            updateRoot(rootCms, siteRootResource);
        } catch (CmsException e) {
            LOG.error(e.getLocalizedMessage(), e);
        }
    }
773527
private void handleDropSitemapEntry(
        final CmsSitemapTreeItem sitemapEntry,
        final I_CmsDropTarget target,
        CmsClientSitemapEntry parent) {

        if (isChangedPosition(sitemapEntry, target, true)) {
            // moving a tree entry around
            final CmsClientSitemapEntry entry = sitemapEntry.getSitemapEntry();
            m_controller.ensureUniqueName(parent, entry.getName(), new I_CmsSimpleCallback<String>() {

                public void execute(String uniqueName) {

                    if (!uniqueName.equals(entry.getName()) && isChangedPosition(sitemapEntry, target, false)) {
                        m_controller.editAndChangeName(
                            entry,
                            uniqueName,
                            Collections.<CmsPropertyModification> emptyList(),
                            entry.isNew(),
                            CmsReloadMode.none);
                        m_controller.move(entry, m_insertPath + uniqueName + "/", m_insertIndex);
                    } else {
                        m_controller.move(entry, m_insertPath + entry.getName() + "/", m_insertIndex);
                    }
                }
            });
        } else {
            sitemapEntry.resetEntry();
        }
    }
135767
public void updateSitePath(String sitepath, I_CmsSitemapController controller) {

        if (!isLeafType() && !sitepath.endsWith("/")) {
            sitepath = sitepath + "/";
        }
        if (!m_sitePath.equals(sitepath)) {
            // update the vfs path as well
            int start = m_vfsPath.lastIndexOf(m_sitePath);
            int stop = start + m_sitePath.length();
            m_vfsPath = CmsStringUtil.joinPaths(m_vfsPath.substring(0, start), sitepath, m_vfsPath.substring(stop));
            if (isLeafType() && m_vfsPath.endsWith("/")) {
                m_vfsPath = m_vfsPath.substring(0, m_vfsPath.length() - 1);
            }
            String oldPath = m_sitePath;
            m_sitePath = sitepath;
            String name = CmsResource.getName(sitepath);
            if (name.endsWith("/")) {
                name = name.substring(0, name.length() - 1);
            }
            m_name = name;
            for (CmsClientSitemapEntry child : m_subEntries) {
                child.updateSitePath(CmsStringUtil.joinPaths(sitepath, child.getName()), controller);
            }
            if (controller != null) {
                controller.registerPathChange(this, oldPath);
            }
        }
    }
1091769
public void updateSitePath(String sitePath) {

        String newSubTitle = getDisplayedUrl(sitePath);
        removeInvalidChildren();
        getListItemWidget().setSubtitleLabel(newSubTitle);
        String name = getName(sitePath);
        setId(name);
        getListItemWidget().setAdditionalInfoValue(1, name);
        if (getLoadState() == LoadState.LOADED) {
            for (int i = 0; i < getChildCount(); i++) {
                CmsSitemapTreeItem item = (CmsSitemapTreeItem)getChild(i);
                if ((item != null)
                    && (CmsSitemapView.getInstance().getController().getEntryById(item.getEntryId()) != null)) {
                    String path = CmsStringUtil.joinPaths(sitePath, CmsResource.getName(item.getSitePath()));
                    item.updateSitePath(path);
                }
            }
        }
        getListItemWidget().updateTruncation();
    }
1332100
public void addChangeData(CmsSitemapChange data) {

        if (data == null) {
            return;
        }
        if (!m_entryId.equals(data.m_entryId)) {
            throw new UnsupportedOperationException("Can't add data for a different entry id.");
        }
        m_sitePath = data.m_sitePath;
        if (data.hasChangedName()) {
            m_name = data.m_name;
        }
        if (data.hasChangedPosition()) {
            m_position = data.m_position;
        }
        if (data.hasChangedProperties()) {
            m_propertyModifications.addAll(data.m_propertyModifications);
        }
        if (data.hasNewParent()) {
            m_parentId = data.m_parentId;
        }

        if (data.getClipBoardData() != null) {
            m_clipBoardData = data.getClipBoardData();
        }
        if ((data.m_changeType == ChangeType.delete)
            || ((data.m_changeType == ChangeType.remove) && (m_changeType != ChangeType.delete))
            || (m_changeType == ChangeType.clipboardOnly)) {
            m_changeType = data.m_changeType;
        }
    }
1256631
private void modifyEntry(CmsSitemapChange change) throws CmsException {

        CmsObject cms = getCmsObject();
        CmsResource entryPage = null;
        CmsResource entryFolder = null;

        CmsResource ownRes = null;
        CmsResource defaultFileRes = null;
        try {
            // lock all resources necessary first to avoid doing changes only half way through

            if (hasOwnChanges(change)) {
                ownRes = cms.readResource(change.getEntryId(), CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
                ensureLock(ownRes);
            }

            if (hasDefaultFileChanges(change)) {
                defaultFileRes = cms.readResource(change.getDefaultFileId(), CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
                ensureLock(defaultFileRes);
            }

            if ((ownRes != null) && ownRes.isFolder()) {
                entryFolder = ownRes;
            }

            if ((ownRes != null) && ownRes.isFile()) {
                entryPage = ownRes;
            }

            if (defaultFileRes != null) {
                entryPage = defaultFileRes;
            }

            if (change.isLeafType()) {
                entryFolder = entryPage;
            }

            updateProperties(cms, ownRes, defaultFileRes, change.getPropertyChanges());
            if (change.hasChangedPosition()) {
                updateNavPos(ownRes, change);
            }

            if (entryFolder != null) {
                if (change.hasNewParent() || change.hasChangedName()) {
                    String destinationPath;
                    if (change.hasNewParent()) {
                        CmsResource futureParent = cms.readResource(
                            change.getParentId(),
                            CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
                        destinationPath = CmsStringUtil.joinPaths(cms.getSitePath(futureParent), change.getName());
                    } else {
                        destinationPath = CmsStringUtil.joinPaths(
                            CmsResource.getParentFolder(cms.getSitePath(entryFolder)),
                            change.getName());
                    }
                    if (change.isLeafType() && destinationPath.endsWith("/")) {
                        destinationPath = destinationPath.substring(0, destinationPath.length() - 1);
                    }
                    // only if the site-path has really changed
                    if (!cms.getSitePath(entryFolder).equals(destinationPath)) {
                        cms.moveResource(cms.getSitePath(entryFolder), destinationPath);
                    }
                    entryFolder = cms.readResource(
                        entryFolder.getStructureId(),
                        CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
                }
            }
        } finally {
            if (entryPage != null) {
                tryUnlock(entryPage);
            }
            if (entryFolder != null) {
                tryUnlock(entryFolder);
            }
        }
    }
103008
public void undelete(CmsUUID entryId, String sitePath) {

        CmsSitemapChange change = new CmsSitemapChange(entryId, sitePath, ChangeType.undelete);
        CmsSitemapClipboardData data = CmsSitemapView.getInstance().getController().getData().getClipboardData().copy();
        data.getDeletions().remove(entryId);
        change.setClipBoardData(data);
        commitChange(change, null);
    }
1364913
public void openPropertyDialog(CmsUUID sitemapEntryId, CmsUUID rootId) {

        getRpcProxy(I_CmsSitemapClientRpc.class).openPropertyDialog("" + sitemapEntryId, "" + rootId);
    }
1220332
private void updateSiteInfo(final String siteRoot) {

        String top = "";
        String bottom = "";

        if (m_new) {
            top = CmsVaadinUtils.getMessageText(Messages.GUI_MODULES_RESINFO_NEW_MODULE_0);
        } else {
            top = m_module.getName();
        }

        if (siteRoot == null) {
            bottom = CmsVaadinUtils.getMessageText(Messages.GUI_MODULES_MODULE_SITE_NOT_SET_0);
        } else {
            CmsSiteManagerImpl siteManager = OpenCms.getSiteManager();
            CmsSite site = siteManager.getSiteForSiteRoot(siteRoot);
            if (site != null) {
                bottom = CmsVaadinUtils.getMessageText(
                    Messages.GUI_MODULES_MODULE_SITE_1,
                    site.getTitle() + " (" + siteRoot + ")");
            } else if (siteRoot.equals("") || siteRoot.equals("/")) {
                bottom = CmsVaadinUtils.getMessageText(Messages.GUI_MODULES_MODULE_SITE_ROOT_FOLDER_0);
            } else {
                bottom = CmsVaadinUtils.getMessageText(Messages.GUI_MODULES_MODULE_SITE_1, siteRoot);
            }
        }
        m_info.get().getTopLine().setValue(top);
        m_info.get().getBottomLine().setValue(bottom);

        for (Component c : Arrays.asList(m_moduleResources, m_excludedResources)) {
            CmsVaadinUtils.visitDescendants(c, new Predicate<Component>() {

                public boolean apply(Component comp) {

                    if (comp instanceof CmsModuleResourceSelectField) {
                        ((CmsModuleResourceSelectField)comp).updateSite(siteRoot);
                    }
                    return true;
                }
            });
        }

    }
1122354
protected CmsLazyTreeItem createItem(final CmsSitemapEntryBean sitemapEntry) {

        CmsDataValue dataValue = new CmsDataValue(
            600,
            3,
            sitemapEntry.getSmallIconClasses(),
            sitemapEntry.getDisplayName());
        dataValue.setUnselectable();
        if (sitemapEntry.isHiddenEntry()) {
            dataValue.setColor("#aaaaaa");
        }
        dataValue.setSearchMatch(sitemapEntry.isSearchMatch());

        CmsLazyTreeItem result = new CmsLazyTreeItem(dataValue, true);
        result.setData(sitemapEntry);
        if (getTabHandler().hasSelectResource()) {
            dataValue.addButton(
                createSelectResourceButton(
                    m_handler.getSelectPath(sitemapEntry),
                    sitemapEntry.getStructureId(),
                    sitemapEntry.getDisplayName(),
                    sitemapEntry.getType()));
        }
        result.setLeafStyle(!sitemapEntry.isFolder());
        result.setSmallView(true);
        if (sitemapEntry.hasChildren()) {
            for (CmsSitemapEntryBean child : sitemapEntry.getChildren()) {
                result.addChild(createItem(child));
            }
            result.setOpen(true, false);
            result.onFinishLoading();
        }
        if ((sitemapEntry.getChildren() != null) && sitemapEntry.getChildren().isEmpty()) {
            result.setLeafStyle(true);
        }
        m_items.add(result);
        dataValue.addClickHandler(new ClickHandler() {

            public void onClick(ClickEvent e) {

                if (getTabHandler().hasSelectResource()) {
                    getTabHandler().selectResource(
                        m_handler.getSelectPath(sitemapEntry),
                        sitemapEntry.getStructureId(),
                        sitemapEntry.getDisplayName(),
                        sitemapEntry.getType());
                }
            }
        });
        return result;
    }
389686
void submit() {

        String siteRoot = (String)m_siteComboBox.getValue();
        if (!m_context.getCms().getRequestContext().getSiteRoot().equals(siteRoot)) {
            A_CmsUI.get().changeSite(siteRoot);
            if (CmsStringUtil.isEmptyOrWhitespaceOnly(siteRoot) || OpenCms.getSiteManager().isSharedFolder(siteRoot)) {
                // switch to explorer view when selecting shared or root site

                Page.getCurrent().open(CmsCoreService.getFileExplorerLink(A_CmsUI.getCmsObject(), siteRoot), "_top");
                return;
            }
        } else {
            siteRoot = null;
        }
        m_context.finish(null, siteRoot);
    }
49901
@SuppressWarnings("deprecation")
    private CmsClientSitemapEntry createNewEntry(String entryPoint, CmsSitemapChange change) throws CmsException {

        CmsObject cms = getCmsObject();
        CmsClientSitemapEntry newEntry = null;
        if (change.getParentId() != null) {
            CmsResource parentFolder = cms.readResource(
                change.getParentId(),
                CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
            String entryPath = "";
            CmsResource entryFolder = null;
            CmsResource newRes = null;
            byte[] content = null;
            List<CmsProperty> properties = Collections.emptyList();
            CmsResource copyPage = null;
            if (change.getNewCopyResourceId() != null) {
                copyPage = cms.readResource(change.getNewCopyResourceId(), CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
                content = cms.readFile(copyPage).getContents();
                properties = cms.readPropertyObjects(copyPage, false);
            }
            List<CmsProperty> filteredProperties = new ArrayList<CmsProperty>();
            for (CmsProperty property : properties) {
                boolean filter = false;
                if (FILTER_PROPERTIES.contains(property.getName())) {
                    filter = true;

                } else {
                    // filter localized versions also
                    for (String filterProp : FILTER_PROPERTIES) {
                        if (property.getName().startsWith(filterProp + "_")) {
                            filter = true;
                            break;
                        }
                    }
                }
                if (!filter) {
                    filteredProperties.add(property);
                }
            }
            properties = filteredProperties;
            if (isRedirectType(change.getNewResourceTypeId())) {
                entryPath = CmsStringUtil.joinPaths(cms.getSitePath(parentFolder), change.getName());
                newRes = cms.createResource(
                    entryPath,
                    change.getNewResourceTypeId(),
                    null,
                    Collections.singletonList(
                        new CmsProperty(CmsPropertyDefinition.PROPERTY_TITLE, change.getName(), null)));
                cms.writePropertyObjects(newRes, generateInheritProperties(change, newRes));
                applyNavigationChanges(change, newRes);
            } else {
                String entryFolderPath = CmsStringUtil.joinPaths(cms.getSitePath(parentFolder), change.getName() + "/");
                boolean idWasNull = change.getEntryId() == null;
                // we don't really need to create a folder object here anymore.
                if (idWasNull) {
                    // need this for calculateNavPosition, even though the id will get overwritten
                    change.setEntryId(new CmsUUID());
                }

                boolean isFunctionDetail = false;
                boolean isNavigationLevel = false;
                if (change.getCreateParameter() != null) {
                    if (CmsUUID.isValidUUID(change.getCreateParameter())) {
                        isFunctionDetail = true;
                    } else if (CmsJspNavBuilder.NAVIGATION_LEVEL_FOLDER.equals(change.getCreateParameter())) {
                        isNavigationLevel = true;
                    }
                }
                String folderType = CmsResourceTypeFolder.getStaticTypeName();
                String createSitemapFolderType = change.getCreateSitemapFolderType();
                if (createSitemapFolderType != null) {
                    folderType = createSitemapFolderType;
                }

                int folderTypeId = OpenCms.getResourceManager().getResourceType(folderType).getTypeId();
                entryFolder = new CmsResource(
                    change.getEntryId(),
                    new CmsUUID(),
                    entryFolderPath,
                    folderTypeId,
                    true,
                    0,
                    cms.getRequestContext().getCurrentProject().getUuid(),
                    CmsResource.STATE_NEW,
                    System.currentTimeMillis(),
                    cms.getRequestContext().getCurrentUser().getId(),
                    System.currentTimeMillis(),
                    cms.getRequestContext().getCurrentUser().getId(),
                    CmsResource.DATE_RELEASED_DEFAULT,
                    CmsResource.DATE_EXPIRED_DEFAULT,
                    1,
                    0,
                    System.currentTimeMillis(),
                    0);
                List<CmsProperty> folderProperties = generateInheritProperties(change, entryFolder);
                if (isNavigationLevel) {
                    folderProperties.add(
                        new CmsProperty(
                            CmsPropertyDefinition.PROPERTY_DEFAULT_FILE,
                            CmsJspNavBuilder.NAVIGATION_LEVEL_FOLDER,
                            null));
                }
                entryFolder = cms.createResource(entryFolderPath, folderTypeId, null, folderProperties);
                if (createSitemapFolderType != null) {
                    createSitemapContentFolder(cms, entryFolder);
                }
                if (idWasNull) {
                    change.setEntryId(entryFolder.getStructureId());
                }
                applyNavigationChanges(change, entryFolder);
                entryPath = CmsStringUtil.joinPaths(entryFolderPath, "index.html");
                boolean isContainerPage = change.getNewResourceTypeId() == CmsResourceTypeXmlContainerPage.getContainerPageTypeIdSafely();
                if (isContainerPage && (copyPage != null)) {

                    // do *NOT* get this from the cache, because we perform some destructive operation on the XML content
                    CmsXmlContainerPage page = CmsXmlContainerPageFactory.unmarshal(
                        cms,
                        cms.readFile(copyPage),
                        true,
                        true);
                    ensureSingleLocale(page, entryFolder);
                    if (isFunctionDetail) {
                        String functionDetailContainer = getFunctionDetailContainerName(parentFolder);
                        CmsUUID functionStructureId = new CmsUUID(change.getCreateParameter());
                        CmsResource functionRes = cms.readResource(
                            functionStructureId,
                            CmsResourceFilter.IGNORE_EXPIRATION);
                        CmsResource functionFormatter;
                        if (OpenCms.getResourceManager().matchResourceType(
                            CmsResourceTypeFunctionConfig.TYPE_NAME,
                            functionRes.getTypeId())) {
                            functionFormatter = cms.readResource(CmsResourceTypeFunctionConfig.FORMATTER_PATH);
                        } else {
                            functionFormatter = cms.readResource(
                                CmsResourceTypeFunctionConfig.FORMATTER_PATH,
                                CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
                        }
                        addFunctionDetailElement(
                            cms,
                            page,
                            functionDetailContainer,
                            functionStructureId,
                            functionFormatter.getStructureId());
                    }
                    createNewContainerElements(cms, page, entryPath);
                    content = page.marshal();
                }
                newRes = cms.createResource(
                    entryPath,
                    copyPage != null ? copyPage.getTypeId() : change.getNewResourceTypeId(),
                    content,
                    properties);
                cms.writePropertyObjects(newRes, generateOwnProperties(change));
            }

            if (entryFolder != null) {
                tryUnlock(entryFolder);
                String sitePath = cms.getSitePath(entryFolder);
                newEntry = toClientEntry(
                    getNavBuilder().getNavigationForResource(sitePath, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED),
                    false);
                newEntry.setSubEntries(getChildren(sitePath, 1, null), null);
                newEntry.setChildrenLoadedInitially(true);
            }
            if (newRes != null) {
                tryUnlock(newRes);
            }
            if (newEntry == null) {
                newEntry = toClientEntry(
                    getNavBuilder().getNavigationForResource(
                        cms.getSitePath(newRes),
                        CmsResourceFilter.ONLY_VISIBLE_NO_DELETED),
                    false);
            }
            // mark position as not set
            newEntry.setPosition(-1);
            newEntry.setNew(true);
            change.getClipBoardData().getModifications().remove(null);
            change.getClipBoardData().getModifications().put(newEntry.getId(), newEntry);
        }
        return newEntry;
    }
1093483
public void setSiteParam(String siteRoot, String key, String value) {

        CmsSite site = OpenCms.getSiteManager().getSiteForRootPath(siteRoot);
        if (site == null) {
            throw new IllegalArgumentException("No site found for path: " + siteRoot);
        } else {
            site.getParameters().put(key, value);
            OpenCms.writeConfiguration(CmsSitesConfiguration.class);
        }
    }
95334
def incremental(self, allow_deletion=False,
                    change_list_uri=None, from_datetime=None):
        
        self.logger.debug("Starting incremental sync")
        # 0. Sanity checks
        if (len(self.mapper) < 1):
            raise ClientFatalError(
                "No source to destination mapping specified")
        if (self.mapper.unsafe()):
            raise ClientFatalError(
                "Source to destination mappings unsafe: %s" % str(
                    self.mapper))
        from_timestamp = None
        if (from_datetime is not None):
            try:
                from_timestamp = str_to_datetime(from_datetime)
            except ValueError:
                raise ClientFatalError(
                    "Bad datetime in --from (%s)" %
                    from_datetime)
        # 1. Work out where to start from
        if (from_timestamp is None):
            from_timestamp = ClientState().get_state(self.sitemap)
            if (from_timestamp is None):
                raise ClientFatalError(
                    "Cannot do incremental sync. No stored timestamp for this site, and no explicit --from.")
        # 2. Get URI of change list, from sitemap or explicit
        if (change_list_uri):
            # Translate as necessary using maps
            change_list = self.sitemap_uri(change_list_uri)
        else:
            # Try default name
            change_list = self.sitemap_uri(self.change_list_name)
        # 3. Read change list from source
        try:
            self.logger.info("Reading change list %s" % (change_list))
            src_change_list = ChangeList()
            src_change_list.read(uri=change_list)
            self.logger.debug("Finished reading change list")
        except Exception as e:
            raise ClientFatalError(
                "Can't read source change list from %s (%s)" %
                (change_list, str(e)))
        self.logger.info(
            "Read source change list, %d changes listed" %
            (len(src_change_list)))
        # if (len(src_change_list)==0):
        #    raise ClientFatalError("Aborting as there are no resources to sync")
        if (len(self.hashes) > 0):
            self.prune_hashes(src_change_list.hashes(), 'change')
        # Check all changes have timestamp and record last
        self.last_timestamp = 0
        for resource in src_change_list:
            if (resource.timestamp is None):
                raise ClientFatalError(
                    "Aborting - missing timestamp for change in %s" %
                    (uri))
            if (resource.timestamp > self.last_timestamp):
                self.last_timestamp = resource.timestamp
        # 4. Check that the change list has authority over URIs listed
        # FIXME - What does authority mean for change list? Here use both the
        # change list URI and, if we used it, the sitemap URI
        if (not self.noauth):
            uauth_cs = UrlAuthority(change_list, self.strictauth)
            if (not change_list_uri):
                uauth_sm = UrlAuthority(self.sitemap)
                for resource in src_change_list:
                    if (not uauth_cs.has_authority_over(resource.uri) and
                            (change_list_uri or not uauth_sm.has_authority_over(resource.uri))):
                        raise ClientFatalError(
                            "Aborting as change list (%s) mentions resource at a location it does not have authority over (%s), override with --noauth" %
                            (change_list, resource.uri))
        # 5. Prune entries before starting timestamp and dupe changes for a
        # resource
        num_skipped = src_change_list.prune_before(from_timestamp)
        if (num_skipped > 0):
            self.logger.info(
                "Skipped %d changes before %s" %
                (num_skipped, datetime_to_str(from_timestamp)))
        num_dupes = src_change_list.prune_dupes()
        if (num_dupes > 0):
            self.logger.info("Removed %d prior changes" % (num_dupes))
        # Review and log status before
        # FIXME - should at this stage prune the change list to pick out
        # only the last change for each resource
        to_update = 0
        to_create = 0
        to_delete = 0
        for resource in src_change_list:
            if (resource.change == 'updated'):
                to_update += 1
            elif (resource.change == 'created'):
                to_create += 1
            elif (resource.change == 'deleted'):
                to_delete += 1
            else:
                raise ClientError("Unknown change type %s" % (resource.change))
        # Log status based on what we know from the Change List. Exit if
        # either there are no changes or if there are only deletions and
        # we don't allow deletion
        in_sync = ((to_update + to_delete + to_create) == 0)
        self.log_status(in_sync=in_sync, incremental=True, created=to_create,
                        updated=to_update, deleted=to_delete)
        if (in_sync or ((to_update + to_create) == 0 and not allow_deletion)):
            self.logger.debug("Completed incremental")
            return
        # 6. Apply changes at same time or after from_timestamp
        delete_msg = (
            ", and delete %d resources" %
            to_delete) if (allow_deletion) else ''
        self.logger.warning(
            "Will apply %d changes%s" %
            (len(src_change_list), delete_msg))
        num_updated = 0
        num_deleted = 0
        num_created = 0
        for resource in src_change_list:
            uri = resource.uri
            filename = self.mapper.src_to_dst(uri)
            if (resource.change == 'updated'):
                self.logger.info("updated: %s -> %s" % (uri, filename))
                self.update_resource(resource, filename, 'updated')
                num_updated += 1
            elif (resource.change == 'created'):
                self.logger.info("created: %s -> %s" % (uri, filename))
                self.update_resource(resource, filename, 'created')
                num_created += 1
            elif (resource.change == 'deleted'):
                num_deleted += self.delete_resource(
                    resource, filename, allow_deletion)
            else:
                raise ClientError("Unknown change type %s" % (resource.change))
        # 7. Report status and planned actions
        self.log_status(incremental=True, created=num_created, updated=num_updated,
                        deleted=num_deleted, to_delete=to_delete)
        # 8. Record last timestamp we have seen
        if (self.last_timestamp > 0):
            ClientState().set_state(self.sitemap, self.last_timestamp)
            self.logger.info(
                "Written last timestamp %s for incremental sync" %
                (datetime_to_str(
                    self.last_timestamp)))
        # 9. Done
        self.logger.debug("Completed incremental sync")