Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Andrew Blackmore 84 posts 127 karma points
    Feb 17, 2010 @ 18:02
    Andrew Blackmore
    0

    Pulling first few <p> tags out of node

    Hi,

     

    This question started as a post in the extending umbraco forum and now that I know how to conceptually tackle the problem and have choosen my plan of attack, I'm a bit confused as to how to approach it.

    Original post:

    http://our.umbraco.org/forum/developers/extending-umbraco/6869-Displaying-Excerpts-and-a-multiblog-page-with-Blog4Umbraco

    I am setting up blogs and on the homepage of each blog I need it to display the first 4 posts (no problem) but take the first post and display 4 paragraphs and every subsequent post only display 1. I want to acheive this by having the XSLT scan each node and display based on <p> tags.

     

    This is part of the XSLT I have, and I know its incorrect - but I also know that I need to do something to the first post and something slightly different after but this is a bit over my very introductory knowledge of XSLT.

     

    <h2 id="post-{$post/@id}">
                <a href="{umbraco.library:NiceUrl($post/@id)}" title="Permalink to {$post/@nodeName}">
                  <xsl:value-of select="$post/@nodeName"/>
                </a>
              </h2>
              <div class="entry-content">
                <xsl:if test="position()=first()">
                  <xsl:value-of select="$post/data [@alias = 'bodyText']" disable-output-escaping="yes"/>
                </xsl:if>
    <xsl:value-of select="$post/data [@alias = 'bodyText']" disable-output-escaping="yes"/>

                <span class="readMore">
                  <a href="{umbraco.library:NiceUrl($post/@id)}" title="Permalink to {$post/@nodeName}">Read More</a>

         Any hints as to how to approach either selecting just the first post or to having the XSLT look inside the nodes for limited numbers of HTML tags would be hugely appreciated as it would help me out here and in other places on the site.

     

    Thanks

  • Andrew Blackmore 84 posts 127 karma points
    Feb 19, 2010 @ 17:30
    Andrew Blackmore
    0

    Something like this does not appear to work but I'm not sure why...

    <xsl:value-of select="$post/data [@alias = 'bodyText']/p" disable-output-escaping="yes"/>
  • Seth Niemuth 275 posts 397 karma points
    Feb 20, 2010 @ 10:26
    Seth Niemuth
    1

    The reason that doesn't work is that: $post/data [@alias = 'bodyText'] refers to the XML file with the nodes in it and 'p' is not defined in the as anything in the the file ( it will be CDATA information which is a trigger to let it know it is not part of the XML file). Here is how a typical node in your umbracoSettings.config file looks (it is in your data folder):

     

    <node id="1059" version="6cbe09d4-8118-4738-b11d-47cb7cef66ab" parentID="1057" level="2" writerID="0" creatorID="0" nodeType="1055" template="1056" sortOrder="2" createDate="2010-01-18T10:08:49" updateDate="2010-01-19T14:45:32" nodeName="About Us ok" urlName="about-us-ok" writerName="Administrator" creatorName="Administrator" nodeTypeAlias="Text Page" path="-1,1057,1059">
          <data alias="bodyText"><![CDATA[
    <p>About Us</p>
    <p>other info.</p>
    ]]></data>
          <data alias="umbracoRedirect" />
          <data alias="umbracoNaviHide" />
          <data alias="sectionTitle" />
          <data alias="sectionDescription"><![CDATA[]]></data>
        </node>

     

     

    As you can see the <p> is inside the CDATA and so will not be in your Xpath.

    I think a better route for you would be some string methods. substring-before will work when you just need the one paragraph:

    http://www.zvon.org/xxl/XSLTreference/Output/function_substring-before.html

    You could do something similar to this (gets the substring of everything before the first </p>:

    <xsl:value-of select="substring-before ($post/data [@alias = 'bodyText'],'</p>')" />

  • Andrew Blackmore 84 posts 127 karma points
    Feb 23, 2010 @ 20:24
    Andrew Blackmore
    0

    Hi Seth,

    Great idea! I did not realize that about the CDATA, but using a slightly altered version of your code I was able to get it to work for the first part of the problem.

     

     <xsl:value-of select="substring-before($post/data [@alias = 'bodyText'],'&lt;/p')" disable-output-escaping="yes"/>

    With this, it prevented to the full closing p tag from escaping anything and causing a nasty error.

     

    This solves the first and hopefully largest of the two issues. The second is this. I can get this to work perfectly and display the first paragraph of every entry. I, however, am completely baffled by how to solve the other issue:

     

    The first entry needs to pull 3 or 4 paragraphs and I have no idea 1.) how to treat the first post differently and 2.) how to apply this logic to the fourth paragraph without having the editors add some sort of class or id tag attribute to the p tag.

  • Seth Niemuth 275 posts 397 karma points
    Feb 24, 2010 @ 18:58
    Seth Niemuth
    1

    in your for loop you can use the position() to figure out where you are at:

    <xsl:choose>

    <xsl:when test="position()= 1" >

    extra stuff for your first one.

    </xsl:when>

    </xsl:choose>

    As to what to do inside there. Maybe you can figure out some way to only get the first 3 paragraphs with a combination of the string functions: http://www.w3schools.com/Xpath/xpath_functions.asp

    What most people do I think is just have a character limit which is what you can do with substring and so you are just going to show the first 250 characters.

    <xsl:value-of select="substring($post/data [@alias = 'bodyText'],0,250)" disable-output-escaping="yes"/>

    If it isn't possible just with those XSLT functions you may need to use some C# or javascript code in your XSLT in order to get those specific first 3 paragraphs:

    http://umbraco.org/documentation/books/extending-xslt-with-c-sharp-or-javascript/why-extend-xslt

  • Andrew Blackmore 84 posts 127 karma points
    Feb 24, 2010 @ 19:57
    Andrew Blackmore
    0

    Thanks a lot Seth. This looks like a great place to start and maybe I will be forced to go the character route. I'll let you know what I end up with.

  • Andrew Blackmore 84 posts 127 karma points
    Feb 24, 2010 @ 20:10
    Andrew Blackmore
    0

    Seth, the loop worked perfectly so now I'm looking further into XPath. I'll post the final loop when I'm done since it might be helpful.

  • Andrew Blackmore 84 posts 127 karma points
    Feb 24, 2010 @ 21:51
    Andrew Blackmore
    0

    Hey Seth,

    The loop worked perfectly and I have figured out some pseudo code (which I'm attempting to, but struggling with turning into real code) but here's my logic...

    Current Loop:

              <xsl:choose>
                <xsl:when test="position()=1">
                <p>
                <xsl:value-of select="substring-before($post/data [@alias = 'bodyText'],3,250)" disable-output-escaping="yes"/>
              </p>
                </xsl:when>
                <xsl:otherwise>
                  <p>
                    <xsl:value-of select="umbraco.library:RemoveFirstParagraphTag(substring-before($post/data [@alias = 'bodyText'],'&lt;/p'))" disable-output-escaping="yes"/>
                  </p>
                </xsl:otherwise>
              </xsl:choose>

    My logic is:

    string-length($post/data [@alias = 'bodyText']) = a

    b=string-length(substring-after($post/data [@alias ='bodyText'],</p>))

    if (b != 0) then{

         a = a-b

         b=string-length(substring-after(substring($post/data [@alias ='bodyText'],a,string-length($post/data [@alias = 'bodyText'])),</p>))

              if(b != 0) then{

               a = a+b

              b=string-length(substring-after(substring($post/data [@alias ='bodyText'],a,string-length($post/data [@alias = 'bodyText'])),</p>))

              }}then

    final output = substring($post/data [@alias ='bodyText'],0,a)

     

    Hopefully the logic makes sense, unfortunately since XSLT is very new to me I don't know how the syntax would work for this logic. That logic/pseudo code would be placed within the current loop so it is called whenever the position()=1. I'm looking into it now but ANY clues, hints or help would be greatly appreciated!

     

    Thanks

  • Andrew Blackmore 84 posts 127 karma points
    Feb 26, 2010 @ 16:22
    Andrew Blackmore
    0

    Hi Everyone,

     

    I have had partial success! I am able to get it to do everything I want but there is a major, semi-fatal flaw which I can't seem to find a solution to. If there is only one paragraph, it won't pull anything.

     

    This is my current code

    <xsl:choose>
                <xsl:when test="position()=1">
                    <xsl:variable name="a" select="number(string-length($post/data[@alias='bodyText']))" />
                    <xsl:variable name="b" select="number(string-length(substring-after($post/data [@alias ='bodyText'],'&lt;/p&gt;')))" />
                    <xsl:variable name="c" select="string-length(substring-after(substring($post/data [@alias ='bodyText'],(number($a)-number($b)),$a),'&lt;/p&gt;'))" />
                    <xsl:variable name="d" select="string-length(substring-after(substring($post/data [@alias ='bodyText'],(number($b)+number($c)),number($a)),'&lt;/p&gt;'))" />           
                    <xsl:variable name="firstPost" select="substring($post/data [@alias = 'bodyText'],0,(number($b)+number($c)+number($d)))" />
                    <xsl:variable name="firstPostLength" select="number(string-length($firstPost))" />
                <p>        
            <xsl:value-of select="$firstPost" disable-output-escaping="yes"/>
              </p>
                </xsl:when>
                <xsl:otherwise>
                  <p>
                    <xsl:value-of select="umbraco.library:RemoveFirstParagraphTag(substring-before($post/data [@alias = 'bodyText'],'&lt;/p'))" disable-output-escaping="yes"/>
                  </p>
                </xsl:otherwise>
              </xsl:choose>

    I've tryied adding an if statement in using the firstPostLength variable as shown below

            <xsl:choose>
                <xsl:when test="position()=1">
                    <xsl:variable name="a" select="number(string-length($post/data[@alias='bodyText']))" />
                    <xsl:variable name="b" select="number(string-length(substring-after($post/data [@alias ='bodyText'],'&lt;/p&gt;')))" />
                    <xsl:variable name="c" select="string-length(substring-after(substring($post/data [@alias ='bodyText'],(number($a)-number($b)),$a),'&lt;/p&gt;'))" />
                    <xsl:variable name="d" select="string-length(substring-after(substring($post/data [@alias ='bodyText'],(number($b)+number($c)),number($a)),'&lt;/p&gt;'))" />           
                    <xsl:variable name="firstPost" select="substring($post/data [@alias = 'bodyText'],0,(number($b)+number($c)+number($d)))" />
                    <xsl:variable name="firstPostLength" select="number(string-length($firstPost))" />
                <p>      
            <xsl:if test="number($firstPost) = 0">
                <xsl:value-of select="umbraco.library:RemoveFirstParagraphTag(substring-before($post/data [@alias = 'bodyText'],'&lt;/p'))" disable-output-escaping="yes"/>
            </xsl:if>    
            <xsl:value-of select="$firstPost" disable-output-escaping="yes"/>
              </p>
                </xsl:when>
                <xsl:otherwise>
                  <p>
                    <xsl:value-of select="umbraco.library:RemoveFirstParagraphTag(substring-before($post/data [@alias = 'bodyText'],'&lt;/p'))" disable-output-escaping="yes"/>
                  </p>
                </xsl:otherwise>
              </xsl:choose>

    and that then displays nothing. I don't know why.

    Any ideas? It's almost there.

  • Andrew Blackmore 84 posts 127 karma points
    Feb 26, 2010 @ 18:04
    Andrew Blackmore
    0

    I spoke too soon. That solution is full of issues. It only worked because my content was too short! Wishful thinking for the morning.

  • dandrayne 1138 posts 2262 karma points
    Feb 26, 2010 @ 18:07
    dandrayne
    0

    I know it's not a solution to your problem, but when doing this I just create a textboxmultiple field called "summary" that the authors can useto create short summaries for list pages etc.

    It's simple and bulletproof, and won't cause madness from parsing html with regexes and other such things.

    But, a solution to this would be interesting.

    Dan

  • Andrew Blackmore 84 posts 127 karma points
    Feb 26, 2010 @ 20:00
    Andrew Blackmore
    0

    Hi Dan,

     

    I thought about doing something like that. The issue cropped up that the top article (newest) needs to show more than articles following it. Ugh. I may end up doing this though as a temporary solution.

    Andrew

  • Andrew Blackmore 84 posts 127 karma points
    Mar 01, 2010 @ 19:03
    Andrew Blackmore
    0

    Hello Everyone,

    I have come up with a solution. I don't believe it is ideal, but it works for my purposes until I find a better one. It has been tested a decent amount (I had my editors try to break it.) The only issue I have found is if they inadvertantly insert an empty p tag at the top it will mess things up until they go back and delete it. This will pull the top 4 paragraphs for the first blog post before it enters the loop to pull the first paragraph of the next few posts.

              <xsl:choose>
                <xsl:when test="position()=1">
                  <xsl:variable name="a" select="number(string-length(fmautolink:Link($post/data[@alias='bodyText'])))" />
                  <xsl:variable name="paragraph" select="'&lt;/p&gt;'" />
                  <xsl:variable name="b" select="number(string-length(substring-before(fmautolink:Link($post/data [@alias ='bodyText']),'&lt;/p&gt;')))" />
                  <xsl:variable name="bString" select="substring-before(fmautolink:Link($post/data [@alias ='bodyText']),'&lt;/p&gt;')" />
                  <xsl:if test="$b &gt; 0">
                    <xsl:value-of select="concat($bString,$paragraph)" disable-output-escaping="yes"/>
                    <xsl:variable name="c" select="string-length(substring-before(substring(fmautolink:Link($post/data [@alias ='bodyText']),(number($b)+11),$a),'&lt;/p&gt;'))" />
                    <xsl:variable name="cString" select="substring-before(substring(fmautolink:Link($post/data [@alias ='bodyText']),(number($b)+11),$a),'&lt;/p&gt;')" />

                    <xsl:if test="$c &gt; 0">
                      <p>
                        <xsl:value-of select="$cString" disable-output-escaping="yes" />
                      </p>
                      <xsl:variable name="d" select="string-length(substring-before(substring(fmautolink:Link($post/data [@alias ='bodyText']),(number($b)+number($c)+21),number($a)),'&lt;/p&gt;'))" />
                      <xsl:variable name="dString" select="substring-before(substring(fmautolink:Link($post/data [@alias ='bodyText']),(number($b)+number($c)+21),number($a)),'&lt;/p&gt;')" />
                        <xsl:if test="$d &gt; 0">
                          <p>
                            <xsl:value-of select="$dString" disable-output-escaping="yes" />
                          </p>
                        <xsl:variable name="e" select="string-length(substring-before(substring(fmautolink:Link($post/data [@alias ='bodyText']),(number($b)+number($c)+number($d)+30),number($a)),'&lt;/p&gt;'))" />
                        <xsl:variable name="eString" select="substring-before(substring(fmautolink:Link($post/data [@alias ='bodyText']),(number($b)+number($c)+number($d)+30),number($a)),'&lt;/p&gt;')" />
                        <xsl:if test="$e &gt; 0">
                          <p>
                            <xsl:value-of select="$eString" disable-output-escaping="yes" />
                          </p>
                        </xsl:if></xsl:if>
                    </xsl:if>
                   
                  </xsl:if>
                </xsl:when>
                <xsl:otherwise>
                  <p>
                    <xsl:value-of select="umbraco.library:RemoveFirstParagraphTag(substring-before(fmautolink:Link($post/data [@alias = 'bodyText']),'&lt;/p'))" disable-output-escaping="yes"/>
                  </p>
                </xsl:otherwise>
              </xsl:choose>

     

    Hope this helps someone else and if i ever find a better solution I'll post it

  • Greyhound 102 posts 124 karma points
    Jun 30, 2010 @ 17:24
    Greyhound
    0

    Hi,

    A bit late - but having just moved to Umbraco (after the learning curve I'm thanking my lucky stars I did cos its excellent) I have hit a similar wall.

    Whilst slightly different from the posters original problem this solution extends the XSLT with C# in order to process paragraphs for a blog teaser.
    The substring method is certainly the easiest but means that the teaser can get cut off. Anayway - here is the code:

    You will need to modify your XSLT file as per the instructions here:
    http://umbraco.org/documentation/books/extending-xslt-with-c-sharp-or-javascript/extending-using-c-sharp

    Put this section into your XSLT file: - **Please note that you will need to add the relevent prefix and msxml namespace to you xslt declaration but all of that is included in the link above.

    <msxml:script implements-prefix="BlogParagraphs" language="C#">
     <msxml:assembly name="System.Web"/>
     <msxml:using namespace="System.Web"/>
     <![CDATA[
       public string getParagraphs(string BlogTextToSplit)
       {
       string returnedString="";
       string[] delim = { "<p>" };
       string[] splitText = BlogTextToSplit.Split(delim, StringSplitOptions.None);
       int paragraphCount = splitText.Length;
       if(paragraphCount>0)
        {
         for(int i=1; i <= paragraphCount; i++)
          {
           if(i >= 4) break;
           returnedString = returnedString + "<p>" + splitText[i].ToString();
          }
         }
        return returnedString;
       }
      ]]>
     </msxml:script>

    Then call this function with the blogBody text and it will return (in this case, a dodgy hard coded first 3 paragraphs)

    <xsl:variable name="BlogTextToSplit" select="($post/data [@alias = 'bodyText'])" />
    <xsl:variable name="BlogParaReturn" select="BlogParagraphs:getParagraphs($BlogTextToSplit)"/>
    <xsl:value-of select="$BlogParaReturn" disable-output-escaping="yes"/>

       

     

Please Sign in or register to post replies

Write your reply to:

Draft