Can I exclude a specific file name with Wix Heat using transforms? - wix

Is it possible to exclude a specific file name with Wix using transforms?
I can exclude files that contain a certain string, but this excludes any file name matching the string. For example I can exclude file.exe with the following;
<xsl:key name="fileexe-search" match="wix:Component[contains(wix:File/#Source, 'file.exe')]" use="#Id"/>
but this will also exclude files with file.exe in their name, like file.exe.config.
Thanks.

Looks like you should use ends-with instead of contains. But ends-with does not exist in XSLT 1.0. :)
This answer gives enough details to get the idea of how to implement it. Basically it is a combination of substring and string-length functions.
Besides, you should also consider normalizing the casing before comparison. That is, it is better to lower-case (or upper-case) both strings - the original and the one it ends with. This post can give you an idea of how to do it.
Keeping all this in mind, you will end up with something similar to this:
<!-- The starting backslash is there to filter out files like 'abcfile.exe' -->
<!-- Besides, it's lower-cased to ease comparison -->
<xsl:variable name="FileName">\file.exe</xsl:variable>
<xsl:variable name="ABC">ABCDEFGHIJKLMNOPQRSTUVWXYZ</xsl:variable>
<xsl:variable name="abc">abcdefghijklmnopqrstuvwxyz</xsl:variable>
<xsl:key name="fileexe-search" match="wix:Component[translate(substring(wix:File/#Source, string-length(wix:File/#Source) - string-length($FileName) + 1), $ABC, $abc) = $FileName]" use="#Id"/>

Although the answer provided by #Yan works I prefer to use C# which is simpler to use.
<xsl:stylesheet version="1.0"
...
xmlns:my="urn:my-installer">
...
<msxsl:script language="C#" implements-prefix="my">
<msxsl:using namespace="System.IO" />
<![CDATA[
public bool EndsWith(string str, string end)
{
if (string.IsNullOrEmpty(str))
return false;
if (string.IsNullOrEmpty(end))
return false;
return str.EndsWith(end);
}
]]>
</msxsl:script>
...
Usage example:
<xsl:key name="ignored-components-search" match="wix:Component[my:EndsWith(wix:File/#Source, '.pssym')
or my:EndsWith(wix:File/#Source, '.pdb')
or my:EndsWith(wix:File/#Source, '.cs')
or my:EndsWith(wix:File/#Source,'.xml')
]" use="#Id" />

Related

Using Xsl <fo:external-graphic> to hand over data

Is there a way in XSL 1.0 to hand over variables or parameters using XSL fo:external-graphic like I would do when I'm using xsl:call-template
I know how I could work around the problem but I just wanted to know if there is a way I am not seeing.
If your SVGs are small enough, you could use fo:instream-foreign-object. (If the SVGs are very large, the size of the XSL-FO file might become a problem.)
main.xsl:
<xsl:import href="svg/svg_graphic.xsl" />
<xsl:template match="some/context">
<fo:instream-foreign-object>
<xsl:call-template name="make-svg">
<xsl:with-param name="param-a" select="..." />
</call-template>
</fo:instream-foreign-object>
</xsl:template>
svg_graphic.xsl:
<xsl:template name="make-svg">
<xsl:param name="param-a" select="..." />
<svg:svg>
...
</svg:svg>
</xsl:template>
<fo:external-graphic src="svg/svg_graphic.xsl" /> is not going to work. Inside your XSLT stylesheet, the elements in the XSL-FO namespace are just literal result elements. They are copied to the result tree, and they are not otherwise acted on by the XSLT processor. XSLT-specific attributes on literal result elements (where the attribute in the XSLT namespace and is defined as meaning something when used on literal result elements) are acted on by the XSLT processor. Attribute value templates ({...}) in attribute values of literal attributes (and some XSLT-defined attributes) are acted on by the XSLT processor.
There is no XSLT 1.0 way to get the XSLT processor to run another stylesheet based on the value of an XSL-FO-defined attribute.
There's also no XSLT 1.0 way to generate multiple result documents from one run of a stylesheet. Your XSLT processor probably has a processor-specific (or EXSLT) way to do that. The extension, if it exists, might not let you generate part of the XSL-FO result document, generate an SVG result document, and then go back to generating more of the XSL-FO document.

Duplicating a job in Pentaho Data Integration for different connections

I've generated a job via the Copy Tables wizard in Spoon UI, that copies some tables from an oracle database source to an SQL Server one, and made some changes to the job as well.
Now I want to duplicate the same job (same tables and same changes), but changing just the connexions. Is that possible in Spoon ?
I've looked through the Spoon UI and didn't find any option that lets me duplicate the job with changing connexions.
EDIT
After I created the two steps: one for generating rows and the other for obfuscating passwords, In the encrypted field, I do not get the 'Encrypted : Obfusctaed Password' output as expected
here is what the step generate rows looks like :
and here is an other picture for the Modified Java Script Value :
You need to make a copy of your kjb file. Jobs and transformations are in fact XML files. You can then edit it manually.
This is pretty straight-forward, with <connection> tags so you should be able to figure it all out by yourself.
I find it the fastest way if you want to keep two jobs instead of changing db connection credentials every time.
If you need to provide an obfuscated password (they are not encrypted, just obfuscated) you can create a transformation that will obfuscate it for you providing you the value to put into XML file.
Steps to reproduce creating a transformation for obfuscating passwords in Kettle 6.1 (for older versions the name of the Script Values / Mod step is Modified Java Script Value):
Step Generate rows with just 1 row storing password as value
Step Script Values / Mod for basic obfuscation
There is example in $KETTLE_HOME/samples/transformation/job-executor.
Pass connection parameters to sub-job
Bad thing u cant pass jdbc driver name so, they have to be same type of database with different connection settings
There is no way to do what you want directly from Pentaho, and one option is to directly alter the transformation's XML to change connections. So the idea is the following:
Figure out how the connection's XML will look like. For this just
register a new connection, use it somewhere in your transformation
and watch the XML source code for element like
........
Make a physical copy of your transformations
Replace connection definition and reference in the XML file. For this you may use XSLT like this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- This template will replace the connection definition -->
<xsl:template match="connection[./name='SOURCE_CONNECTION_NAME']">
<!-- This is the connection configuration -->
<connection>
<name>TARGET_CONNECTION_NAME</name>
<server>localhost</server>
<type>ORACLE</type>
<access>Native</access>
<database><!-- DB NAME --> </database>
<port>1521</port>
<username><!-- USERNAME --> </username>
<password><!-- PWD --></password>
<servername/>
<data_tablespace><!-- --></data_tablespace>
<index_tablespace/>
<attributes>
<attribute><code>FORCE_IDENTIFIERS_TO_LOWERCASE</code><attribute>N</attribute></attribute>
<attribute><code>FORCE_IDENTIFIERS_TO_UPPERCASE</code><attribute>N</attribute></attribute>
<attribute><code>IS_CLUSTERED</code><attribute>N</attribute></attribute>
<attribute><code>PORT_NUMBER</code><attribute>1521</attribute></attribute>
<attribute><code>PRESERVE_RESERVED_WORD_CASE</code><attribute>Y</attribute></attribute>
<attribute><code>QUOTE_ALL_FIELDS</code><attribute>N</attribute></attribute>
<attribute><code>SUPPORTS_BOOLEAN_DATA_TYPE</code><attribute>Y</attribute></attribute>
<attribute><code>SUPPORTS_TIMESTAMP_DATA_TYPE</code><attribute>Y</attribute></attribute>
<attribute><code>USE_POOLING</code><attribute>N</attribute></attribute>
</attributes>
</connection>
</xsl:template>
<!-- And that one will replace the connection's reference in table input/table output -->
<xsl:template match="connection[text()='SOURCE_CONNECTION_NAME']">
<connection>TARGET_CONNECTION_NAME</connection>
</xsl:template>
</xsl:stylesheet>
Ah, I do believe you can do this however I have not done it myself as of yet. No need. But I believe you can use shared objects to get this kind of functionality that you want and just have the one (much easier to maintain) transformation. Here's a forum link where it's discussed.
Let me know how it works out. Pretty curious.
http://forums.pentaho.com/showthread.php?75069-Fully-Dynamic-Database-Configuration-Including-underlying-databsae-type

XSLT 1.0: Key based on a whitespace separated attribute

I have an XML containing elements a with an attribute #ref that contains at least one pointer and MAY contain any number of pointers separated by whitespaces: <a ref="#p1 #p2"/>. In an XSLT 1.0 stylesheet, I need to apply a template to all and only those elements b referenced in an a/#ref within the document.
So, I thought the idea would be to define a <xsl:key name="k1" match="a" use="my:refs(#ref)"/> with a function splitting the value of #ref into its constituents and then have a <xsl:template match="b[key('k1', #xml:id)]"/>. If I interpret the specs correctly:
The use attribute is an expression specifying the values of the key; the
expression is evaluated once for each node that matches the pattern. If the
result is a node-set, then for each node in the node-set, the node that
matches the pattern has a key of the specified name whose value is the
string-value of the node in the node-set;
I need to provide a function to #use that returns a node-set consisting of the values of a/#ref (minus the leading '#') as a string.
My solution was to define <xsl:key name="k1" match="a" use="str:tokenize(#ref, '# ')/> and <xsl:template match="a[key('k1', #xml:id)">.
When I use Xalan as processor, this gives me the wanted result. Saxon 9.6.0.7, however, complains about a circular key definition.
Now I am a bit confused: is my solution valid at all? and if so, why is Saxon complaining? Are there other/better(/real) solutions? [see comments].
Edit: Additional question: How would I get my result in XSLT 2.0? Simply using tokenize(#ref, '#') in the key definition is not enough because of the whitespaces while for normalize-space(tokenize(#ref, '#')) Saxon will give the same error XTDE0640.
Thanks,
Dario
example XML:
<root>
<b xml:id="#p1">P1</b>
<b xml:id="#p2">P2</b>
<b xml:id="#pn">Pn</b>
<a ref="#p2" />
<a ref="#p1 #pn" />
</root>
The output should be
P1
P2
Pn
(no particular order required).

xsl:template match not working

I have an image (in the content) with a src attribute
http://myPage/rss.gif
I wan't to change the attribute. The xsl:template match expression doesn't work..
<replace css:content="#content" css:theme=".content" />
<xsl:template match="img/#src[contains(., 'rss.gif')]">
<xsl:attribute name="src">/++theme++myPackage/images/<xsl:value-of select="." /></xsl:attribute>
</xsl:template>
What am I doing wrong?
In your example you select an img-tag containing the string rss.gif in its src-attribute, this string is probably http://myPage/rss.gif.
Then you add an attribute src to the img-tag beeing its value the concatenation of the strings /++theme++myPackage/images/ and the original value http://myPage/rss.gif.
The original value is added by <xsl:value-of select="." /></xsl:attribute>. This selects the whole value of the src-attribute!
Thus you get something like:
<img id="content" src="/++theme++myPackage/images/http://myPage/rss.gif" />
For test purposes you could first add an img-element whith the expected src-attribute to your theme.html file and check if the image file is accessed. Please check also that the value of src in your content file only contains the string that you want to append to /++theme++myPackage/images/.
BTW, in your example you replace the theme element by class: css:theme=".content". Remind that this will replace all elements with the class content. Consider using id or a narrower selector e.g. div.content or better div#someid.content.

Using a variable as part of a XPath selection

I'm looking to use a variable as part of an XPath expression.
My problem might be the msxsl node-set function... not sure. But don't let that cloud your judgement... read on...
I'm using a .NET function to load up the content file, which is passed in via bespoke XML content. The #file results in an XML file.
The bespoke XML the sits on the page looks like :
<control name="import" file="information.xml" node="r:container/r:group[#id='set01']/r:item[1]" />
The XSL looks like :
<xsl:variable name="document">
<xsl:copy-of select="ext:getIncludedContent(#file)" />
</xsl:variable>
I'm then translating this to a node-set so I can query the document
<xsl:variable name="preset-xml" select="msxsl:node-set($document)" />
The source file I am loading in looks like :
<container>
<group id="set01">
<item>value01</item>
<item>value02</item>
<item>value03</item>
</group>
<group id="set02">
<item>value04</item>
<item>value05</item>
</group>
</container>
It works up until this point. I can see the source file being brought thru and output as XML.
My problem comes in when I am trying to query the source file with an XPath expression fed in from the content.
I've tried :
<xsl:value-of select="$preset-xml/#node" />
Clearly that doesn't work as it looks for #node as a direct child of the loaded in XML.
I've tried :
<xsl:variable name="node" select="#node" />
<xsl:value-of select="$preset-xml/$node" />
But it doesn't like the second variable.
I've tried concat($preset-xml,'/',$node), but that still draws out the entire document in the result.
The only way I can get this working at the moment is to write out the full expression in the template :
<xsl:value-of select="$preset-xml/r:container/r:group[#id='set01']/r:item[1]" />
Which correctly brings thru value01
But that is then a hard coded solution.
I want to develop a solution which can be manipulated from the content to suit any sort of imported content, with the parameters declared in the content file.
p.s. I'm using XSLT 1.0 because the tech admin won't change the Microsoft parser to support later versions of XSL, so any solution would need to be written with that in mind.
There is no feature similar to reflection in XSLT. MS XSLT engine, in particular, compiles XPath queries when the XSLT was loaded, not when it was applied to your XML document. There is a reason to this: it separates the code (XSLT) from the data (input XML).
What are you trying to achieve by mixing up code and data; are you sure you really need to execute an arbitrary XPath expression from the input XML? Think, for example, what if the user will pass in the #name equal to e.g. //*, and then you'll send to the output the content of the entire input XML document, which might contain some sensitive data.
If you really need to do the thing you described, you may write your own XSLT extension object, which will accept the document root and the XPath query and will return the latter applied to the former. Then you'll be able to call it like that:
<xsl:value-of select="myExtensionNs:apply-xpath($preset-xml, #node)"/>
As for your original attempt with concat($preset-xml,'/',$node), concat is the string concatenating function. You supply some arguments to it, and it returns the concatenation of its string representations. So, concat($preset-xml,'/',$node) will return the string value of $preset-xml, concatenated with /, concatenated with the string value of $node. If you were trying to write a needed XPath query, concat('$preset-xml','/',$node) would seem more logical (note the quotes); still, there is no way to evaluate the resulted string (e.g. $preset-xml/r:container/r:group[#id='set01']/r:item[1]) to the value of corresponging XPath query; it could only be sent to output.
One possible trick is to call an parametrized xsl:template which carries out the crucial select comparison part and evaluate it's output as string.
I had a similar problem (count all nodes of an arbitrary attribute value) which I solved this way (btw: $sd = 'document($filename)' so I load a secondary xml files content):
<xsl:template match="/">
<xsl:variable name="string_cntsuccess">
<xsl:for-each select="./Testcase">
<xsl:variable name="tcname" select="#location"/>
<xsl:for-each select="$sd//TestCase[#state='Passed']">
<xsl:call-template name="producedots">
<xsl:with-param name="name" select="$tcname"/>
</xsl:call-template>
</xsl:for-each>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="cntsuccess" select="string-length($string_cntsuccess)"/>
</xsl:template>
<xsl:template name="producedots">
<xsl:param name="name"/>
<xsl:variable name="ename" select="#name"/>
<xsl:if test="$name = $ename">
<xsl:text>.</xsl:text>
</xsl:if>
</xsl:template>