Unique Random Images - Solution Options
VirtualRichard
Posted: Tuesday, June 03, 2008 5:56:22 PM

Rank: Fanatic

Joined: 9/17/2007
Posts: 202
Location: London, UK.
This is in response to an earlier post detailing my troubles. This functionality follows on from another author's random image XSLT but I decided I wanted the images to be not only random but unique and also to be able to get more than 1 image out. I had no idea the can of worms I was about to open up :) Here is my solution.

First, create a new XSLT file called RandomImages. Leave the box checked to create the associated macro. Here is the XSLT:

Code:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [<!ENTITY nbsp " ">]>
<xsl:stylesheet
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:msxml="urn:schemas-microsoft-com:xslt"
  xmlns:umbraco.library="urn:umbraco.library"
  xmlns:msxsl="urn:schemas-microsoft-com:xslt"
  xmlns:aqua="urn:schemas-my-org:aqua"
  xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings"
  xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath"
  exclude-result-prefixes="msxml Exslt.ExsltMath Exslt.ExsltStrings aqua umbraco.library">

  <xsl:output method="xml" omit-xml-declaration="yes"/>

  <xsl:param name="currentPage"/>

  <xsl:variable name="StartNode" select="/macro/StartNode/node/@id" />
  <xsl:variable name="NumberOfImages" select="/macro/NumberOfImages" />
  <xsl:variable name="parent" select="umbraco.library:GetMedia($StartNode, 'false')" />
  <xsl:variable name="randomNumbers" select="aqua:random($NumberOfImages, count($parent/node))"/>

  <xsl:template match="/">

    <xsl:for-each select="$randomNumbers/numbers/number">

      <xsl:variable name="randomPosition" select="."/>

      <xsl:for-each select="$parent/node">

        <xsl:variable name="parentPosition" select="position()"/>

        <xsl:if test="$parentPosition = $randomPosition">

          <xsl:if test="./data [@alias = 'umbracoExtension'] = 'gif' or ./data [@alias = 'umbracoExtension'] = 'jpg' or ./data [@alias = 'umbracoExtension'] = 'jpeg' or ./data [@alias = 'umbracoExtension'] = 'png'">

            <div class="Random{$currentPage/@nodeName}Image">

              <xsl:element name="img">
                <xsl:attribute name="src"><xsl:value-of select="./data [@alias = 'umbracoFile']"/></xsl:attribute>
                <xsl:attribute name="alt"><xsl:value-of select="@nodeName"/></xsl:attribute>
                <xsl:attribute name="width"><xsl:value-of select="./data [@alias = 'umbracoWidth']"/></xsl:attribute>
                <xsl:attribute name="height"><xsl:value-of select="./data [@alias = 'umbracoHeight']"/></xsl:attribute>
              </xsl:element>

            </div>

          </xsl:if>

        </xsl:if>

      </xsl:for-each>

    </xsl:for-each>

  </xsl:template>

  <msxml:script language="CSharp" implements-prefix="aqua">
  <msxml:using namespace="System.Collections.Generic" />
  <msxml:using namespace="System.IO" />
  public XPathNodeIterator random(int numMin, int numMax)
  {
    int i;
    Random random = new Random();

    List&lt;int&gt; numbersList = new List&lt;int&gt;();

    for (i = 1; i &lt; numMax + 1; i++)
    {
      numbersList.Add(i);
    }
    int numRange = numMax - numMin;

    for (i = 0; i &lt; numRange; i++)
    {
      int index = random.Next(0, numbersList.Count);
      numbersList.RemoveAt(index);
    }
    StringBuilder stringBuilder = new StringBuilder("&lt;numbers&gt;");

    foreach (int item in numbersList)
    {
      stringBuilder.Append("&lt;number&gt;");
      stringBuilder.Append(item.ToString());
      stringBuilder.Append("&lt;/number&gt;");
    }
    stringBuilder.Append("&lt;/numbers&gt;");

    StringReader stringReader = new StringReader(stringBuilder.ToString());
    XPathDocument xPathDocument = new XPathDocument(stringReader );
    XPathNavigator xPathNavigator = xPathDocument.CreateNavigator();

    XPathExpression xPathExpression = xPathNavigator.Compile("/");

    XPathNodeIterator xPathNodeIterator = xPathNavigator.Select(xPathExpression);

    return xPathNodeIterator;
  }
  </msxml:script>

</xsl:stylesheet>

NB: You'll need to check the checkbox to disable error checking to save the file.

Got to the new macro and add two parameters:

Code:
Alias: StartNode
Name: Start node
Type: mediaCurrent

Alias: NumberOfImages
Name: Number of images
Type: number

In the media section, create a folder to hold your images.

In a template you can then insert the macro. Select the appropriate media folder and tell the macro how many images you want to display.

In this example, images will display vertically. They are wrapped in a div. You can always change this. Hey, you could add another macro parameter for multiple presentations and use that to select a different template even.

I found implementing the above very frustrating (my lack of knowledge shone) and almost gave up. However, I'm really glad I stayed with it. Not only do I now know really well how to use script in XSLT, I found out how to put assemblies in and also how to manipulate XML and pass it back to my XSLT - all very worthwhile knowing.

There's very probably a much better way of doing this (shhh!) but it works for me and I'm happy. If I need to change it in the future, I can go direct to this one file and make my changes.

I've pasted the file in AS IS. It's working on my site and I hope you find this useful.

Richard

2 * 3 * 3 * 37 : The prime factorisation of The Beast.
astuanax
Posted: Thursday, June 05, 2008 4:21:19 PM

Rank: Devotee

Joined: 7/20/2006
Posts: 86
HI,

To switch from a string into a node set you can use the function

node-set()

If your string is xml, you can create nodes with this, so you don't need to create all this C# stuff to return xml nodes.
I haven't looked at the random stuff you are doing but I guess that is possible with the umbraco.library:GetRandom().

Len.


VirtualRichard
Posted: Friday, June 06, 2008 11:38:52 AM

Rank: Fanatic

Joined: 9/17/2007
Posts: 202
Location: London, UK.
If only it could be so simple. Look up node-set, it only brings back xsl text. I tried it already and it doesn't work.

Also, sure getrandom would be good but by definition a random call can bring back the same value twice, especially when there are only , say, 8 items being randomised. That's what the list is for. Pop out the random choice until what's left is your list of items. Because the same item can't be taken away twice you don't get any duplicates. This is impossible just calling random (this is the issue with the get random image xslt posted elsewhere in these forums).

Richard

2 * 3 * 3 * 37 : The prime factorisation of The Beast.
imayat12
Posted: Friday, June 06, 2008 12:23:24 PM

Rank: Addict

Joined: 7/19/2006
Posts: 586
Location: Preston, UK
Guys,

I have xslt to do random images see code below:

Code:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:Stylesheet [ <!ENTITY nbsp "&#x00A0;"> ]>
<xsl:stylesheet
    version="1.0"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxml="urn:schemas-microsoft-com:xslt"
    xmlns:umbraco.library="urn:umbraco.library"
    xmlns:myFuncs="urn:my-scripts"
    exclude-result-prefixes="msxml myFuncs umbraco.library">


<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>

<xsl:param name="currentPage"/>


<xsl:variable name="imageRoot" select="/macro/startMediaNode"/>


<msxsl:script implements-prefix="myFuncs" language="JavaScript">

<![CDATA[

var myArray = new Array();

function random(){
    
    var index = Math.floor(Math.random() * myArray.length);    
    return myArray [index];
}

function addtoArray(item){
    myArray.push(item);

}

]]>

</msxsl:script>


<xsl:template match="/">


<!-- start writing XSLT -->
<!--<xsl:copy-of select="umbraco.library:GetMedia($imageRoot/node/@id, 'true')/node/data[@alias='umbracoFile']/text()"/>-->

<xsl:call-template name="buildArray">
    <xsl:with-param name="arrayNodes" select="umbraco.library:GetMedia($imageRoot/node/@id, 'true')/node/data[@alias='umbracoFile']"/>
</xsl:call-template>

<!--<xsl:value-of select="myFuncs:random()"/>-->

<img alt="Random image" width="425" height="283" src="{myFuncs:random()}"/>



</xsl:template>

<xsl:template name="buildArray">
<xsl:param name="arrayNodes"/>
<!--<xsl:copy-of select="$arrayNodes"/>-->
    <xsl:for-each select="$arrayNodes">
        <xsl:value-of select="myFuncs:addtoArray(string(./text()))"/>
    </xsl:for-each>

</xsl:template>

</xsl:stylesheet>


you could have the array from being string to your own structure if you need more properties than just the image url.

Regards

Ismail

Level 2 certified. If it aint broke dont fix.
ascendinternet
Posted: Sunday, June 22, 2008 6:32:32 PM
Rank: Devotee

Joined: 11/16/2007
Posts: 51
Richard, I have added your script to a site I'm developing and have a question - why have you added the node name as part of the surrounding DIV name? That means it cannot be styled using CSS?

Gordon Saxby | Ascend Internet Limited | Web Site Development, Hosting & Domain Names
VirtualRichard
Posted: Monday, June 23, 2008 11:37:30 AM

Rank: Fanatic

Joined: 9/17/2007
Posts: 202
Location: London, UK.
ascendinternet wrote:
Richard, I have added your script to a site I'm developing and have a question - why have you added the node name as part of the surrounding DIV name? That means it cannot be styled using CSS?


Sorry for the delay in replying, I've been sunning myself in Portugal (hooray!) but I'm back at work now (boo!).

Actually, you can style by CSS. My templates are named according to sections, so what I get is a class name that is specific to a section of my website, so if you are in 'Products', the CSS becomes 'RandomProductsImage' and if in, say, specific product page you might have 'RandomProductImage'.

This allows me to style my DIVs by section as the templates are very different in look and feel.

Richard

2 * 3 * 3 * 37 : The prime factorisation of The Beast.
Doogie Talons
Posted: Thursday, July 03, 2008 6:16:31 PM
Rank: Enthusiast

Joined: 1/28/2008
Posts: 34
Hi would it be possible to alter this to perhaps show ordinary Nodes, say for a random news feed of 10 stories
Users browsing this topic
Guest


You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.