XSLT - parse date and time - excel

I am attempting to set the Type attribute of a cell in Excel via XSL. It should look at report/sheet/definition/column and see if there's a type attribute. If yes, it should set the Cell Type to that type and format it in full time (or datetime) manner.
My xml data:
<report>
<sheet>
<definition>
<columns>
<column name="Code" footerText="ISO Code" width="80" />
<column name="Time" footerText="Time" format="DateTime" width="58" type="DateTime" />
<column name="Country" footerText="Country" width="100" />
<column name="SomeNumber" footerText="Country" width="100" type="Number"/>
</columns>
</definition>
<data>
<table>
<row>
<column value="2020-04-02" key="DATE" />
</row>
<row>
<column value="123" />
<column value="10:30" />
<column value="Belarus" />
<column value="0" />
</row>
<row>
<column value="321" />
<column value="12:00" />
<column value="Austria" />
<column value="0" />
</row>
<row>
<column value="2020-04-03" key="DATE" />
</row>
<row>
<column value="456" />
<column value="08:00" />
<column value="USA" />
<column value="24" />
</row>
</table>
</data>
</sheet>
</report>
I have tried printing out $type and $index in the cell and they truly print out properly, but when I try to take the value of $type and use for <xsl:attribute name="ss:Type">, Excel provides me with an error saying it had problems during the load. After checking out the logs, it turns out that it can't parse values like 12:00 into a Time (or shall I say DateTime?). I have two questions:
Is that expected behavior that it can't parse timespan values into DateTime?
Is it normal behavior that it can't parse 2020-01-01 as DateTime as well?
<xsl:for-each select="data/table/row">
<ss:Row>
<xsl:variable name="set" select="column" />
<xsl:variable name="count" select="count($set)"/>
<xsl:for-each select="column">
<ss:Cell>
<xsl:variable name="index" select="position()"/>
<xsl:variable name="type" select="../../../definition/columns/column[$index]/#type"/>
<xsl:attribute name="ss:MergeAcross">
<xsl:choose>
<xsl:when test="$count>1">
<xsl:value-of select="0"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$mergeAcrossHeader"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<ss:Data>
<xsl:attribute name="ss:Type">
<xsl:choose>
<xsl:when test="$type!=''">
<xsl:value-of select="$type" />
</xsl:when>
<xsl:otherwise>
<xsl:text>String</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:value-of select="#value" />
</ss:Data>
</ss:Cell>
</xsl:for-each>
</ss:Row>
</xsl:for-each>

Related

Update XSLT for XLSX

I have an old XSLT stylesheet that still works, but only transforms to Excel xls files. I've used ClosedXml to convert grid data to Excel xlsx files and it works great, but I have some formatted Excel documents that are not grids and require some formatting and layout. I would like to convert those old XSLT stylesheets to create the new(er) XLSX format files but only replacing
xmlns="urn:schemas-microsoft-com:office:spreadsheet"
with
xmlns="http://schemas.openxmlformats.org/officeDocument/2006/
doesn't work.
Has anyone updated an XSLT stylesheet to produce output to Excel xlsx? If yes, please share what you needed to do to accomplish it.
Here's the XSLT stylesheet that I'm trying to convert:
<xsl:stylesheet version="1.0" xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:user="urn:my-scripts" xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">
<xsl:template match="/">
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:html="http://www.w3.org/TR/REC-html40">
<Styles>
.. styles
</Styles>
<Worksheet ss:Name="name">
<Table ss:ExpandedColumnCount="9" x:FullColumns="1" x:FullRows="1" ss:DefaultRowHeight="13.2">
<Column ss:Index="2" ss:Width="77"/>
<Column ss:Index="5" ss:Width="54"/>
<Column ss:Index="8" ss:Width="178"/>
<xsl:for-each select="ReportRows/ReportRow">
<xsl:if test="position()=1">
<Row>
<Cell ss:MergeAcross="8" ss:StyleID="s30">
<Data ss:Type="String">
<xsl:value-of select="SE_Year" /> Title #Lab
</Data>
</Cell>
</Row>
</xsl:if>
<xsl:if test="Pr_Short_Name != Prev_Project">
<Row>
<Cell ss:MergeAcross="8" ss:StyleID="s24" />
</Row>
<Row>
<Cell ss:MergeAcross="8" ss:StyleID="s27">
<Data ss:Type="String">
<xsl:value-of select="Pr_Long_Name" />
</Data>
</Cell>
</Row>
<Row ss:AutoFitHeight="0">
cells...
</Row>
</xsl:if>
<xsl:if test="GR_Short_Name != Prev_SampleTypeGroup">
<Row>
<Cell ss:MergeAcross="8" ss:StyleID="s31" />
</Row>
<Row>
<Cell ss:MergeAcross="8" ss:StyleID="s32">
<Data ss:Type="String">
<xsl:value-of select="GR_Short_Name" />
</Data>
</Cell>
</Row>
</xsl:if>
<Row ss:Height="41.4">
cells...
</Row>
</xsl:for-each>
<Row ss:Height="13.8">
<Cell ss:MergeAcross="8" ss:MergeDown="1" ss:StyleID="m15419890" />
</Row>
</Table>
<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
<PageSetup>
<Header x:Margin="0" />
<Footer x:Margin="0.3" x:Data="&L&8Form Print Date/Time: &D &T&C&8Page &P of &N" />
<PageMargins x:Bottom="0.95" x:Left="0.5" x:Right="0.5" x:Top="0.5" />
</PageSetup>
<Print>
<ValidPrinterInfo />
<Scale>100</Scale>
<HorizontalResolution>600</HorizontalResolution>
<VerticalResolution>600</VerticalResolution>
</Print>
<Selected />
<DoNotDisplayGridlines />
<Panes>
<Pane>
<Number>3</Number>
<ActiveRow>8</ActiveRow>
<ActiveCol>10</ActiveCol>
</Pane>
</Panes>
<ProtectObjects>False</ProtectObjects>
<ProtectScenarios>False</ProtectScenarios>
</WorksheetOptions>
</Worksheet>
</Workbook>
</xsl:template>
</xsl:stylesheet>

XSLT - nested foreach Excel provides no output

I am attempting to pull out some values and display an array of arrays in Excel dynamically. However, my nested for-each doesn't and I receive no output no matter what I try. I have, of course, left out some stuff within the sheet and data, so right now they seem as if they're unnecessarily nested, but that isn't the case.
XML:
<report>
<sheet>
<data>
<table>
<row>
<column value="Germany" />
<column value="Berlin" />
<column value="2321341" />
</row>
<row>
<column value="USA" />
<column value="Washington DC" />
<column value="11111" />
</row>
</table>
</data>
</sheet>
</report>
The XSL nested for-each I try to get to work. It works when I remove the inner for-each and try displaying the first value by doing <xsl:value-of select="column/#value">, so it creates two new rows, each containing the name of the country (which is the first element of the column list).
<xsl:for-each select="report/sheet/data/table/row">
<ss:Row>
<xsl:for-each select="row/columm">
<ss:Cell>
<ss:Data ss:Type="String">
<xsl:value-of select="#value" />
</ss:Data>
</ss:Cell>
</xsl:for-each>
</ss:Row>
</xsl:for-each>
First, you have a typo in:
<xsl:for-each select="row/columm">
The name of the element is column, not columm.
More importantly, the instruction:
<xsl:for-each select="report/sheet/data/table/row">
puts you in the context of row. From this context,
<xsl:for-each select="row/column">
selects nothing, because row is not a child of itself. It needs to be:
<xsl:for-each select="column">

Liferay Service Builder Table Auto Increment On Each Deployment

In liferay i have a entity as below:
<entity name="Foo" local-service="true" remote-service="true">
<!-- PK fields -->
<column name="fooId" type="long" primary="true" />
<!-- Group instance -->
<column name="groupId" type="long" />
<!-- Audit fields -->
<column name="companyId" type="long" />
<column name="userId" type="long" />
<column name="userName" type="String" />
<column name="createDate" type="Date" />
<column name="modifiedDate" type="Date" />
<!-- Other fields -->
<column name="field1" type="String" />
<column name="field2" type="boolean" />
<column name="field3" type="int" />
<column name="field4" type="Date" />
<column name="field5" type="String" />
<!-- Order -->
<order by="asc">
<order-column name="field1" />
</order>
<!-- Finder methods -->
<finder name="Field2" return-type="Collection">
<finder-column name="field2" />
</finder>
</entity>
When i change the code of the portlet. On each deployment its primary key increases by 100.
So there is anyway to set it auto increment by 1 only. And it must not be incremented by 100 on each deploy.
Option#1
Add this in your primary key column id-type="increment"
i.e
<column name="fooId" type="long" primary="true" id-type="increment" />
Cons:
This WILL BREAK IN CLUSTERED Environment
Option#2
add this in portal-ext.properties
#
# Set the number of increments between database updates to the Counter
# table. Set this value to a higher number for better performance.
#
counter.increment=1 //by default it is 100
Cons: this will impact your performance.

XSL-FO : How to make value expand vertically

I've got a name string in my XML whose length varies. In case of a long name, it expands horizontally which I cannot afford. How can I make it grow vertically? Below is what I have.
<fo:table-cell font-family="Courier" font-size="10px" display-align="before"height="0.01042in" border-style="solid" border-color="green">
<fo:block padding-top="1pt" padding-bottom="1pt" borderstyle="solid">
<fo:inline>
<xsl:text>USER INFORMATION</xsl:text>
</fo:inline>
<fo:block>
<xsl:text>
</xsl:text>
</fo:block>
<fo:inline font-family="Courier" font-size="12px" font-weight="bold">
<xsl:value-of select="USERNAME" />
</fo:inline>
</fo:block>
</fo:table-cell>
I used that link posted by Joel above in comments. When I applied template exactly, it displayed strings with odd length fine but missed the last character of even length strings. I then changed the comparison from less than< to less than or equal<= and it worked.
Hope it will help someone.Thanks.
<xsl:template name="zero_width_space_1">
<xsl:param name="data" />
<xsl:param name="counter" select="0" />
<xsl:choose>
<xsl:when test="$counter <= string-length($data)">
<xsl:value-of select='concat(substring($data,$counter,1),"​")' />
<xsl:call-template name="zero_width_space_2">
<xsl:with-param name="data" select="$data" />
<xsl:with-param name="counter" select="$counter+1" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="zero_width_space_2">
<xsl:param name="data" />
<xsl:param name="counter" />
<xsl:value-of select='concat(substring($data,$counter,1),"​")' />
<xsl:call-template name="zero_width_space_1">
<xsl:with-param name="data" select="$data" />
<xsl:with-param name="counter" select="$counter+1" />
</xsl:call-template>
</xsl:template>
<xsl:call-template name="zero_width_space_1">
<xsl:with-param name="data" select="mystring" />
</xsl:call-template>

How to add a link to the rest of the paragraph in SharePoint Announcements?

How can I create a link to the rest of the paragraph in an announcement in SharePoint, that displays the word: read more
Cheers
I've found that the cleanest, easiest way to do this is to create a template in ItemStyle.xsl which selects a substring of the body content of the announcement and displays a link below to the article itself.
After adding the following code to your ItemStyle.xsl file (in SharePoint Designer navigate to the 'Style Library/XSL Style Sheets' folder), you can modify the web part through the browser, and change the Item Style (Presentation/Styles) to 'ReadMoreAnnouncements'. This code keeps the amount of characters displayed to 190 characters (see the substring($bodyContent,1,190 function call).
<xsl:template name="removeMarkup">
<xsl:param name="string" />
<xsl:choose>
<xsl:when test="contains($string, '<')">
<xsl:variable name="nextString">
<xsl:call-template name="removeMarkup">
<xsl:with-param name="string" select="substring-after($string, '>')" />
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="concat(substring-before($string, '<'), $nextString)" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$string" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="ReadMoreAnnouncements" match="Row[#Style='ReadMoreAnnouncements']" mode="itemstyle">
<br />
<div class="RMAnnouncementsTitle">
<xsl:value-of select="#Title" />
</div>
<div class="RMAnnouncementsBody">
<xsl:variable name="bodyContent">
<xsl:call-template name="removeMarkup">
<xsl:with-param name="string" select="#Body"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="substring($bodyContent,1,190)" />
...
<br />
<a>
<xsl:attribute name="href">
/Lists/Announcements/DispForm.aspx?ID=
<xsl:value-of select="#ID">
</xsl:value-of>
</xsl:attribute>
<xsl:attribute name="class">
RMAnnouncementsMoreLink
</xsl:attribute>
read more
</a>
</div>
</xsl:template>
This should definitely work, and it's very very easy to implement.
If you have a page and can edit it in SharePoint Designer, try this.
If you want to have a web part that shows the announcement the way you want, try this.

Resources