How to programmatically manipulate XhtmlString?

Vote:
 

Hi,

I have a situation where I need to update a bit of text in an XhtmlString programatically.  I want to replace an old value with a new value.  The value to be replaced is also the first "fragment" in the XhtmlString.

Currently the XhtmlString is composed of multiple fragments- some are simple HTML text, some are images, and some are additional blocks added into the XhtmlString.  I am trying to update just the first fragment and set it to a new bit of HTML.

Here's what I'm trying:

var clone = myBlock.CreateWritableClone() as MyBlock;
if (clone != null && clone.MainBody != null && clone.MainBody.Fragments.Any())
{
    // MainBody is an XhtmlString
    clone.MainBody.Fragments.RemoveAt(0);

    // value to replace- the first "fragment" of text
    var replaceValue = clone.MainBody.Fragments.First().InternalFormat;

    // replacement value html
    var newValue = @"<p>some new value</p>";

    var newBody = clone.MainBody.ToInternalString().Replace(replaceValue, newValue);
    clone.MainBody = new XhtmlString(newBody);

    // save
    _contentRepository.Save((IContent)clone, SaveAction.Publish, AccessLevel.NoAccess);
}

When I run this, however, I seem to lose the blocks that have been added to the rich text.

What is the proper way to go about replacing 1 fragment of an XhtmlString with another?

#229753
Edited, Oct 22, 2020 20:15
Vote:
 

I found the solution.  What you need to do is:

  1. Create a new XhtmlString
  2. Loop through the old XhtmlString's fragments, and append each fragment to the new XhtmlString.  Skip the one you are trying to manipulate and inject a different fragment instead.
  3. Save the IContent housing the XhtmlString

So in my case, the first fragment was static HTML and had a typo in it.  We needed to find this and fix it, so the pseudo-algorithm looked like this:

var clone = myBlock.CreateWritableClone () as MyBlock;
if (clone != null && clone.MainBody != null && clone.MainBody.Fragments.Any ()) {

    // replace first fragment with new bit of HTML:
    var targetVal = "<h1>Hello World</h1>";
    var newXhtmlStringValue = new XhtmlString ();
    newXhtmlStringValue.Fragments.Add (new StaticFragment (targetVal));;

    // skip first fragment and add the rest to the new xhtml string
    clone.MainBody.Fragments.RemoveAt (0); // purge first item from old body
    foreach (var fragment in clone.MainBody.Fragments) {
        newXhtmlStringValue.Fragments.Add (fragment);
    }

    // set the main body over to the new xhtml string
    clone.MainBody = newXhtmlStringValue;

    // save
    _contentRepository.Save ((IContent) clone, SaveAction.Publish, AccessLevel.NoAccess);
}
#229801
Oct 23, 2020 13:00
Vote:
 

My initial suggestion would be something like this:

var clone = myBlock.CreateWritableClone () as MyBlock;
if (clone != null && clone.MainBody != null) 
{
    var oldText = clone.MainBody.ToInternalString();
    var newText = oldtext.Replace("old", "new");
    clone.MainBody = new XhtmlString(newText);
    _contentRepository.Save ((IContent) clone, SaveAction.Publish, AccessLevel.NoAccess);
}

But your approach looks safer. Maybe I will mess up blocks, personalization or some other stuff I did not think of?

#229834
Oct 24, 2020 17:51
Vote:
 

Correct Tomas, converting the entire XhtmlString to an internal string, and then parsing it back will break any dynamic fragments inside (personalized, blocks, etc).  This could possibly be a bug on the XhtmlString constructor.  I couldn't find any other way around this except to copy the fragments out one by one and inject a new static fragment in the target location.

#229899
Oct 26, 2020 12:12
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.