I am new to XML (a couple of days now...)and everything is going good, however, I can't seem to get the xml processed with css style
here is my perl...
#!/usr/bin/perl -w
use strict;
use warnings;
use diagnostics;
use Text::CSV_XS;
my $csv = Text::CSV_XS->new ({ binary => 1, auto_diag => 1 });
my $ifile="elementarray.csv";
my $ofile="elementarray.xml";
open my $fh, "<", $ifile or die $ifile.": $!";
open my $out, "> ".$ofile or die "Cannot write ".$ofile.": $!";
print $out <<EOT;
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="elementarray.xsl"?>
<?xml-stylesheet href="elementarray.css" title="Default style"?>
<EMAILCOMMENTS>
EOT
# First record contains list of fieldnames
#my $fields = $csv->getline($fh);
#print #$fields;
while (my $row = $csv->getline($fh)) {
last unless $row && #$row;
# Encode "<" characters as "<" and "&" as "&" in all fields
foreach (#$row) {
s/&/&/g;
s/</</g;
}
# Create hash of fields using hash slice
my %row;
#row{"Subject","Body","From: (Name)","From: (Address)","To: (Name)","To:(Address)","Date","Time"} = #$row;
print $out <<EOT;
<EMAIL>
<HEADER>
<ORIGNAME>$row{"From: (Name)"}</ORIGNAME>
<ORIGADDRESS>$row{"From: (Address)"}</ORIGADDRESS>
<DESTNAME>$row{"To: (Name)"}</DESTNAME>
<DESTADDRESS>$row{"To: (Address)"}</DESTADDRESS>
<SUBJECT>$row{"Subject"}</SUBJECT>
</HEADER>
<BODY>$row{"Body"}</BODY>
<DATE id="date">$row{"Date"}</DATE>
<TIME id="time">$row{"Time"}</TIME>
</EMAIL>
EOT
}
print $out "</EMAILCOMMENTS>\n";
$csv->eof or $csv->error_diag;
close $fh or die $ifile.": $!";
close $out or die $ofile.": $!";
Here is my xsl...
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes" method="html"/>
<xsl:template match="/"><!-- a 'pattern', / matches the root node -->
<html>
<head>
<title>E-mail</title>
</head>
<body>
<xsl:apply-templates/><!-- an instruction -->
</body>
</html>
</xsl:template>
<xsl:template match="HEADER">
<span STYLE="color:red; font-weight:bold; font-style:italic">
<xsl:value-of select="SUBJECT"/>
</span>
From: <xsl:call-template name="formatEmail">
<xsl:with-param name="address" select="ORIGADDRESS"/>
</xsl:call-template>
To: <xsl:call-template name="formatEmail">
<xsl:with-param name="address" select="DESTADDRESS"/>
</xsl:call-template>
</xsl:template>
<xsl:template match="EMAIL">
<xsl:apply-templates select="HEADER"/>
<pre>
<xsl:value-of select="BODY"/>
</pre>
<div>
<span>
<xsl:attribute name="id"><xsl:value-of select="date"/></xsl:attribute>
<!-- --><xsl:value-of select="DATE"/><!-- --></span>
<span>
<xsl:attribute name="id"><xsl:value-of select="time"/></xsl:attribute>
<!-- --><xsl:value-of select="TIME"/><!-- --></span>
</div>
<br />
</xsl:template>
<xsl:template name="formatEmail">
<xsl:param name="address"/>
<a>
<xsl:attribute name="href"><xsl:value-of select="concat('mailto:',$address)"/></xsl:attribute>
<xsl:value-of select="$address"/>
</a>
</xsl:template>
</xsl:stylesheet>
here is my xml (1 record)...
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="elementarray.xsl"?>
<?xml-stylesheet href="elementarray.css" title="Default style"?>
<EMAILCOMMENTS>
<EMAIL>
<HEADER>
<ORIGNAME>William Holt</ORIGNAME>
<ORIGADDRESS><x#gmail.com></ORIGADDRESS>
<DESTNAME>X</DESTNAME>
<DESTADDRESS>bill#elementarray.com</DESTADDRESS>
<SUBJECT>welcome to the neighborhood</SUBJECT>
</HEADER>
<BODY>just thought i'd say hello</BODY>
<DATE id="date">07/18/2013</DATE>
<TIME id="time">11:53</TIME>
</EMAIL>
</EMAILCOMMENTS>
and here is css ( style for just to see if it is working, and it isn't :( )...
DATE{background-color:red;}
TIME{background-color:green;}
#date{background-color:yellow;}
#time{background-color:blue;}
BODY{background-color:grey;}
Sorry for the length of the post but I think you need all these files...
Although you can indeed style XML with CSS, using the xml-stylesheet processing instruction you have included in your XML, you can't transform it with XSLT at the same time. That is to say you should only really have one stylesheet processing instruction in your XML. Either you simply style your XML with CSS (which is not really a common thing to do), or your transform it to HTML with XSLT and style the HTML output with CSS.
In the latter case, remove the <?xml-stylesheet href="elementarray.css"?> instruction from your XML document, and instead output a reference to the CSS document in your first template.
<xsl:template match="/">
<html>
<head>
<title>E-mail</title>
<link href="elementarray.css" type="text/css" rel="stylesheet" />
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
Now, in your CSS, I see you already have selectors for the date and time ids, so you need to ensure the relevant elements in your HTML have these ids.
At the moment, you are doing this in your XSLT
<span>
<xsl:attribute name="id"><xsl:value-of select="date"/></xsl:attribute>
<!-- --><xsl:value-of select="DATE"/><!-- --></span>
<span>
But this is setting the id attribute to have the value of the date element in your XML, but such an element does not exist! I think you meaning to use the literal string here. Now, you could do this...
<xsl:attribute name="id"><xsl:value-of select="'date'"/></xsl:attribute>
But this is verbose. You can just do this:
<xsl:attribute name="id">date</xsl:attribute>
But better still, just write it out as an attribute in the normal way:
<span id="date">
<xsl:value-of select="DATE"/>
</span>
And similarly for 'time'. This should ensure the two span tags get styled using your CSS in the HTML you output.
Related
Here is my XML:
<?xml version="1.0" encoding="UTF-8"?>
<Collection>
<Content>
<ID>2779</ID>
<Type>Content</Type>
<Title>Article One</Title>
<QuickLink>/template.aspx?id=2779</QuickLink>
<Teaser />
<Html>
<root>
<NewsArticle>
<artTitle>The Comprehensive Breast Center: Quality Care on the Fast Track</artTitle>
<artThumb>
<img alt="Thumb Article One" src="/uploadedImages/Test/News/artOne.png?n=5954" />
</artThumb>
<artFull />
<releaseDate />
<contactName />
<contactPhone />
<contactEmail />
<artTeaser>The National Cancer Institute estimates that a woman in the United States has a 1 in 8 chance of developing invasive breast cancer</artTeaser>
<artText>
<p>The Comprehensive Breast Center: Quality Care on
the Fast Track</p>
<p>
How do I display the IMG tag from my XML above to an HTML document using XSLT
Something like this should do the trick:
<xsl:template match="/">
<xsl:for-each select="Collection/Content">
<xsl:copy-of select="Html/root/NewsArticle/artThumb/node()"/>
</xsl:for-each>
</xsl:template>
I should note that this assumes you're getting this from an Ektron collection -- assumption made based on your tagging of this question. This will display the image from each content block in the collection. If you want just the image from the first content block of the collection, you could remove the for-each and instead use something like this:
<xsl:template match="/">
<xsl:copy-of select="Collection/Content/Html/root/NewsArticle/artThumb/node()"/>
</xsl:template>
Also, it works either way, but i removed the slash from the front of the select on the for-each. Seemed redundant since the code is in a template that already matches on "/".
UPDATE
Some of that can be done in the workarea -- it allows you to set the css class, though I'm not sure if you can set the title attribute of an image. Here's how you could do that via XSLT. In this case, you can't copy the node whole-sale:
<xsl:template match="/">
<xsl:for-each select="Collection/Content">
<xsl:variable name="imageSrc" select="Html/root/NewsArticle/artThumb/img/#src" />
<xsl:variable name="imageId">
<xsl:text>NewsArticle_</xsl:text>
<xsl:value-of select="ID" />
<xsl:text>_image</xsl:text>
</xsl:variable>
<xsl:variable name="contentTitle" select="Html/root/NewsArticle/artTitle" />
<img id="{ $imageId }" class="myCssClass" title="{ $contentTitle }" alt="{ $contentTitle }" src="{ $imageSrc }" />
</xsl:for-each>
</xsl:template>
(Updated again - appears i misread your comment. Thanks, #MathiasMüller!)
When assigning ids to elements like this, I prefer to use a little more than just the content id. In this case, by using "NewsArticle_{Content ID}image", I allow for a container div to use an id "NewsArticle{Content Id}" if it is needed in the future without colliding with the image ids.
How do i assign a title and an alt from artTitle and also a class and id?
Building upon the answer given by #BrianOliver, this is how you output an img element whose "title" attribute reflects the content of artTitle from your input XML - the same for ID.
I assume that by "an alt from artTitle" you mean that the text content of img/#alt should also come from the artTitle element.
<xsl:template match="/">
<xsl:for-each select="Collection/Content">
<xsl:variable name="imageSrc" select="Html/root/NewsArticle/artThumb/img/#src" />
<!--xsl:variable name="imageAlt" select="Html/root/NewsArticle/artThumb/img/#alt" /-->
<xsl:variable name="imageId" select="ID"/>
<xsl:variable name="imageTitle" select="Html/root/NewsArticle/artTitle"/>
<img id="{$imageId}" class="myCssClass" title="{$imageTitle}" alt="{ $imageTitle}" src="{$imageSrc}"/>
</xsl:for-each>
</xsl:template>
However, I am not sure where the class attribute should come from.
I am transforming some simple XML document into XForms and I am trying to add some style to the final result. I am using the XSLTForms implementation and I am pointing to a local CSS file (Twitter's bootstrap). So the XML file looks like that:
<structure>
<part class="Container" id="container">
<part class="Button" id="b1"/>
</part>
</structure>
<style>
<property part-name="b1" name="label">Submit</property>
</style>
My XSLT that transforms these parts to XForms document:
<xsl:key name="buttonLabels" match="property[#name='label']" use="#part-name"/>
<xsl:template match="part[#class='Button']"><!-- [key('buttonActions', #id)]-->
<xsl:element name="xf:trigger">
<xsl:attribute name="id">
<xsl:value-of select="#id | #size | #style"/>
</xsl:attribute>
<xsl:element name="xf:label">
<xsl:value-of select="key('buttonLabels', #id)"/>
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:template match="part[#class='Container']">
<xsl:element name="div">
<xsl:attribute name="class">container</xsl:attribute>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
Now, this produces: (which is fine for what I need currently)
<div class="container">
<xf:trigger id="b1">
<xf:label>Submit</xf:label>
</xf:trigger>
</div>
But I'd like to add some style rules to this <xf:trigger>. The thing is that when it gets transformed into html elements, the structure is
|_span
|_span
|_button
|_span (for the label)
So I am not sure how to make the XSLT transformation, so that it inserts let's say class="btn-danger" attribute into the <button> tag.
In other words, I need to get something like that in the final Html: (currently I get it, but without the class="btn-danger" in the button tag)
<span id="b1">
<span>
<button type="button" class="btn-danger">
<span id="xsltforms-mainform-label-2_6_2_4_3_" class="xforms-label">Submit</span>
</button>
</span>
</span>
Besides, I tried with adding an <xsl:attribute name="class">btn-danger</xsl:attribute> in the template for the xf:trigger, but that inserts the attribute at the first span element.
Any ideas? Thanks in advance!
UPDATE:
The XSLT responsible for transforming the xforms:trigger element to html elements can be found on this link - http://bpaste.net/show/c42CtcIcjbsI6GFGWK2q/
But I don't want to edit the XSLTForms implementation, but rather find a workaround, so that I can specify the style of my buttons from my XML file.
For example:
<structure>
<part class="Container" id="container">
<part class="Button" id="b1"/>
</part>
</structure>
<style>
<property part-name="b1" name="label">Submit</property>
<property part-name="b1" name="style">btn-danger</property>
</style>
So here, I am matching the part-name and saying that I want the button with id="b1" to have css style rules for btn-danger for example. But if I have another button and there is no style rule for it, it should have default appearance. Hope that's clear.
You cannot add a class attribute directly to the button element generated by XSLTForms.
But instead of it, you can define a CSS rule that applies to it this way:
#myclass button {
color: red;
}
and use the class in the trigger:
<xf:trigger class="myclass >
The same for every XForms control. Just take a look to the browser inspector to see the generated controls.
I am running umbraco v 4.0.3. My web server is Windows Server 2003.
I noticed that one of my pages was being displayed with content from a parent node in addition to the of the control it hosted. Since the page only serves as a host for an ascx control, it has no content of its own.
The content area of the node is empty, so I am at a loss of where this content is coming from.
I have done the following things to attempt to resolve the problem, but none of them resulted in the page in question changing at all. The page persists.
I've published the node again.
I've clicked the unpublish button, and then published the node.
I've cleared my browsers cache.
I've used multiple browsers.
I've used the incognito mode on Chrome.
I've changed the template the node is based on, which should have prevented the macro from running, but it didn't.
I've added content to the node. The new content didn't show up.
I've changed the node's name. When I reload the page under it's old name, it's still there.
I've touched web.config to restart the application. There was no change.
I've stopped and started the website.
I've right clicked on the top node of the site, and choose "republish"
I've manually deleted the umbraco.conf file under the data folder.
I've even deleted the node.
Nothing I do changes what appears when I navigate to the page. At this point, I should get a 404, but the page still loads and still runs the macro.
At this point, I am at a loss of what to do, but I'm guessing it has something to do with IIS or Dot.Net and caching.
Does anybody have an idea how to fix this?
EDIT
I have found that if I removed an XSLT macro reference from the template upon which the deleted page was built, the duplication of the parent page goes away when the deleted page loads in the browser. Since this was my primary problem, I've recreated the page and have gone on. I am still perplexed as to why the page I deleted was able to be loaded from multiple browsers.
The macro I removed is used in the majority of the 1,500+ pages of our web site, and on none of them does it reproduce the parent's document on the child, nor does it seem possible to me that an XSLT macro can reach outside of the XML cache. I've reviewed the code, and didn't see any references to an additional data store. I confess that XSLT is quite new to me.
The code for the macro whose reference I removed is listed below.
<?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:custom="urn:custom.library"
exclude-result-prefixes="msxml umbraco.library custom">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<msxml:script language="C#" implements-prefix="custom">
public System.Xml.XPath.XPathNodeIterator SplitMultiPageText(string unsplitText,
string pageDelimeter) {
System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
string[] splitPages = System.Text.RegularExpressions.Regex.Split(unsplitText, pageDelimeter);
System.Xml.XmlElement pagesElement = xmlDoc.CreateElement("pages");
foreach (string page in splitPages) {
System.Xml.XmlElement pageElement = xmlDoc.CreateElement("page");
pageElement.AppendChild(xmlDoc.CreateCDataSection(page));
pagesElement.AppendChild(pageElement);
}
return pagesElement.CreateNavigator().Select(".");
}
</msxml:script>
<xsl:param name="currentPage"/>
<xsl:variable name="content" select="/macro/content" />
<xsl:variable name="pageNumber" select="/macro/pageNumber" />
<xsl:variable name="joinContent">
<xsl:choose>
<xsl:when test="/macro/joinContent = ''">
<xsl:value-of select="boolean(false)" />
</xsl:when>
<xsl:otherwise><xsl:value-of select="/macro/joinContent" /></xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="splitExpr"><hr class="pagebreak" /></xsl:variable>
<xsl:template match="/">
<xsl:variable name="pages" select="custom:SplitMultiPageText($content, $splitExpr)" />
<xsl:variable name="pageCount" select="count($pages/page)" />
<xsl:choose>
<xsl:when test="$pageCount = 1 or string($joinContent) = 'true'">
<xsl:for-each select="$pages/page">
<xsl:value-of
select="umbraco.library:RenderMacroContent(., $currentPage/#id)"
disable-output-escaping="yes" />
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<p class="multiPageNotice">This document is split among multiple pages. To view
the other pages, use the <strong>page selector</strong> near the bottom of the screen.</p>
<xsl:for-each select="$pages/page">
<xsl:if test="position() = $pageNumber or ( $pageNumber = '' and position() = 1 )">
<xsl:value-of
select="umbraco.library:RenderMacroContent(., $currentPage/#id)"
disable-output-escaping="yes" />
</xsl:if>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="$pageCount > 1 and string($joinContent) != 'true'">
<div class="pageSelector">
<p>Page: </p>
<ul>
<xsl:for-each select="$pages/page">
<li>
<xsl:choose>
<xsl:when test="position() = $pageNumber or ( $pageNumber = '' and position() = 1 )">
<xsl:value-of select="position()" />
</xsl:when>
<xsl:otherwise>
<a href="{umbraco.library:NiceUrl($currentPage/#id)}/page/{position()}">
<xsl:value-of select="position()" />
</a>
</xsl:otherwise>
</xsl:choose>
</li>
</xsl:for-each>
</ul>
</div>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Given your comment, it's possible that the macro persists content to a store that is independent of the application/umbraco config, e.g. an xml file or a db-based session. This would obviously persist the data outside of the application restarting. Obviously, we would have to see the code for the macro though.
here my code-
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="ArrayOfLinkEntity" name="bindLink">
<ul>
<xsl:for-each select="LinkEntity[ParentLinkId=0]">
<li>
<xsl:variable name="linkId" select="LinkId"/>
<xsl:variable name="child" select="count(/ArrayOfLinkEntity/LinkEntity[ParentLinkId=$linkId])"/>
<xsl:value-of select="$child"/>
<xsl:choose>
<xsl:when test="($child > 0)">
<a href="#" data-flexmenu="flexmenu1" onclick="javascript:setPageLinkId({$linkId});">
<xsl:value-of select="LinkTitle"/>
<img src="../images/down.gif" border="0"/>
</a>
</xsl:when>
<xsl:otherwise >
<a href="#" onclick="javascript:setPageLinkId({$linkId});">
<xsl:value-of select="LinkTitle"/>
</a>
</xsl:otherwise>
</xsl:choose>
</li>
</xsl:for-each>
</ul>
</xsl:template>
</xsl:stylesheet>
but I am getting $child=0 always.but there exists children.
my xml structure-
<ArrayOfLinkEntity xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<LinkEntity>
<EntityId>00000000-0000-0000-0000-000000000000</EntityId>
<LinkId>1</LinkId>
<SequenceNo>1</SequenceNo>
<ParentLinkId>0</ParentLinkId>
<LinkTitle>Home</LinkTitle>
<SubLink />
</LinkEntity> ...
</ArrayOfLinkEntity>
What should I do? Please suggest.
but I am getting $child=0 always.but
there exists children.
If by "children" you mean a LinkEntity with ParentLinkId child that is equal to the LinkId of the current node, the result you get is correct.
The only LinkEntity has an LinkId 1, but there are no LinkEntity elements in the provided XML document whose ParentLinkId is 1.
You need to show a complete (but the shortest possible) XML document on which your code exhibits this issue. Without being able to repro the problem, no one can give you a logical advice.
From your following code:
<xsl:variable name="linkId" select="LinkId"/>
<xsl:variable name="child" select="count(/ArrayOfLinkEntity/LinkEntity[ParentLinkId=$linkId])"/>
This occurs within a for-each loop where the only nodes being looped over are LinkEntity's with ParentLinkId = 0. But from your source XML, the value of LinkId = 1, and in your variable assignment for $child, you are selecting on LinkEntity's with ParentLinkId = 1, which don't exist in your source XML data.
If I misunderstood something please let me know, but from what I can see that may be the problem.
I need to loop through an XML document (no problem over there) and check if a value that i find is already in a (a) tag in a div in my XSL document that i am generating, only if the value is not in that (a) tag i should create a new (a) tag for it and put in in the div that i am checking...
Any one knows how to do it dynamically in XSLT?
<div id="tags"><span class="l_cap"> </span>
all
<xsl:for-each select="root/nodes/node/data/genres">
<xsl:for-each select="value">
**<xsl:if test="not(contains())">**
<xsl:value-of select="current()"/>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
sorry for before, what i am trying to do is: in the if statement, check if the current value is already exist in the div if not, add it, if is, don't do anything...
10x again
It sounds like you're trying to create a distinct list of all of the "genres" in your list.
Assuming a data structure which looks a bit like this:
<root>
<nodes>
<node>
<data>
<genres>
<value>One</value>
<value>Two</value>
<value>Two</value>
<value>Three</value>
<value>Two</value>
</genres>
</data>
</node>
</nodes>
</root>
And a stylesheet which looks a bit like this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="genres" match="value" use="."/>
<xsl:template match="/">
<div>
<xsl:for-each select="/root/nodes/node/data/genres/value">
<xsl:if test="generate-id(.) = generate-id(key('genres', .)[1])">
<xsl:value-of select="."/>
</xsl:if>
</xsl:for-each>
</div>
</xsl:template>
</xsl:stylesheet>
Then you will end up with something like this:
<div>
One
Two
Three
</div>
This is a fairly standard XSLT 1.0 technique. It uses keys (described here: http://www.xml.com/pub/a/2002/02/06/key-lookups.html ) to create a sort of index of all the /root/nodes/node/data/genres/value entries. Then it loops through all of the entries, but only prints the first one of each type. The end result is that each value will only be output once.