In this article I will show how I have integrated Drupal feed from www.gerixsoft.com into WordPress application on LinkedIn.
When I first tried to add GerixSoft's feed into WordPress application, it refused saying "Only WordPress feeds are supported". After comparing Drupal and WordPress feeds, I have figured out they are using the same RSS standard. The only difference is meta "generator" element, absent on Drupal but present on WordPress XML.
Wild guess have confirmed that WordPress is using this string to block RSS from non-WordPress platforms.
To fix this, open includes/common.inc, locate format_rss_channel function and apply the following patch:
/**
* Formats an RSS channel.
*
* Arbitrary elements may be added using the $args associative array.
*/
function format_rss_channel($title, $link, $description, $items, $langcode = NULL, $args = array()) {
global $language;
$langcode = $langcode ? $langcode : $language->language;
$output = "<channel>\n";
$output .= ' <title>'. check_plain($title) ."</title>\n";
$output .= ' <link>'. check_url($link) ."</link>\n";
// The RSS 2.0 "spec" doesn't indicate HTML can be used in the description.
// We strip all HTML tags, but need to prevent double encoding from properly
// escaped source data (such as & becoming &amp;).
$output .= ' <description>'. check_plain(decode_entities(strip_tags($description))) ."</description>\n";
// © by Andriy Gerasika from GerixSoft, Ltd.
$output .= "<generator>http://wordpress.org/?v=MU</generator>\n";
$output .= ' <language>'. check_plain($langcode) ."</language>\n";
$output .= format_xml_elements($args);
$output .= $items;
$output .= "</channel>\n";
return $output;
}
Note, when you'll be resubmitting feed into WordPress application, it still might say feed is not from WordPress – just try several times and it will accept the feed ultimately (seems there is some sort of cache on the server).
XSLT name() function is evil and should be avoided just like GoTo.
For example, it is very bad to write
<xsl:apply-templates select="*[name()!='a:b']"/>
because it will exclude both of
<a:b/> <a:b xmlns:a="totallyDifferentNamespace"/>
the correct way to write above apply-templates is
<xsl:apply-templates select="*[not(self::a:b)]"/>
or in XSLT 2.0
<xsl:apply-templates select="* exclude a:b"/>
assuming xmlns:a="someSpecificNamespace"
Test code is attached below.
The XML Pipeline in XSLT technique requires every xsl:template and xsl:apply-templates in the chained stylesheet to have mode attribute.
This could be simplified if XSLT would allow specifying mode in xsl:include. So <xsl:include href="link1.xsl" mode="link1"/> would mean "use link1 as default mode in link1.xsl":
If xsl:include/@mode would be available in XSLT processors, XML Pipeline code would look much simpler:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common">
<xsl:output method="xml" omit-xml-declaration="no" indent="no" encoding="UTF-8"/>
<xsl:include href="link1.xsl" mode="link1"/>
<xsl:include href="link2.xsl" mode="link2"/>
<xsl:include href="link3.xsl" mode="link3"/>
<xsl:template match="/">
<xsl:variable name="link1">
<xsl:apply-templates mode="link1" select="node()"/>
</xsl:variable>
<xsl:variable name="link2">
<xsl:apply-templates mode="link2" select="exsl:node-set($link1)/node()"/>
</xsl:variable>
<xsl:variable name="link3">
<xsl:apply-templates mode="link3" select="exsl:node-set($link2)/node()"/>
</xsl:variable>
<xsl:copy-of select="exsl:node-set($link3)/node()"/>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:import href="identity.xsl" />
<xsl:template match="a">
<aa>
<xsl:apply-templates select="@*|node()"/>
<b/>
</aa>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:import href="identity.xsl" />
<xsl:template match="b">
<bb>
<xsl:apply-templates select="@*|node()"/>
<c/>
</bb>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:import href="identity.xsl" />
<xsl:template match="c">
<cc>
<xsl:apply-templates select="@*|node()"/>
</cc>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template priority="-9" match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Attached you can find my patches to Saxon 9.1 and Xalan-J 2.7.1 enabling @mode in xsl:include.
Most often people implement XML Pipeline in regular programming languages, sequentially invoking XSLT transformations one-by-one, "manually" feeding resulting output from one transformation to another.
Bespoke slow performance, this results in lesser flexibility since people stop using XSLT's declarative power and revert back to imperative world, which is not acceptable for XML transformations.
In this article I will show the most efficient way of implementing XML Pipeline using native XSLT capabilities.
The code outlining the approach consists of the following files:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common">
<xsl:output method="xml" omit-xml-declaration="no" indent="no" encoding="UTF-8"/>
<xsl:include href="link1.xsl"/>
<xsl:include href="link2.xsl"/>
<xsl:include href="link3.xsl"/>
<xsl:template match="/">
<xsl:variable name="link1">
<xsl:apply-templates mode="link1" select="node()"/>
</xsl:variable>
<xsl:variable name="link2">
<xsl:apply-templates mode="link2" select="exsl:node-set($link1)/node()"/>
</xsl:variable>
<xsl:variable name="link3">
<xsl:apply-templates mode="link3" select="exsl:node-set($link2)/node()"/>
</xsl:variable>
<xsl:copy-of select="exsl:node-set($link3)/node()"/>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template priority="-9" mode="link1" match="@*|node()">
<xsl:copy>
<xsl:apply-templates mode="link1" select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template mode="link1" match="a">
<aa>
<xsl:apply-templates mode="link1" select="@*|node()"/>
<b/>
</aa>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template priority="-9" mode="link2" match="@*|node()">
<xsl:copy>
<xsl:apply-templates mode="link2" select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template mode="link2" match="b">
<bb>
<xsl:apply-templates mode="link2" select="@*|node()"/>
<c/>
</bb>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template priority="-9" mode="link3" match="@*|node()">
<xsl:copy>
<xsl:apply-templates mode="link3" select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template mode="link3" match="c">
<cc>
<xsl:apply-templates mode="link3" select="@*|node()"/>
</cc>
</xsl:template>
</xsl:stylesheet>
The key point of this approach is usage of EXSLT node-set function. This function converts result-nodes back into source-nodes. This way output of link1.xsl is fed as input to link2.xsl, and output of link2.xsl is fed as input to link3.xsl. [2]
This approach leverages full power of XSLT, it is fast and declarative. Transformations are applied without leaving XSLT processor, non-linear pipelines can be implemented easily:
<xsl:variable name="link2">
<!-- some logic whether or not launch link2.xsl -->
<xsl:choose>
<xsl:when test="not(.//d)">
<xsl:apply-templates mode="link2" select="exsl:node-set($link1)/node()"/>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="exsl:node-set($link1)/node()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
I am using this technique for a number of years, and so far it works great.
[1] By default MSXML does not implement EXSLT, though it does have node-set function. It is in the xmlns:msxsl="urn:schemas-microsoft-com:xslt" namespace. There is chain_msxml.xsl — separate main file for MSXML that is using node-set function from msxsl namespace.
[2] xsl:copy-of in the last statement is included for debugging purposes. For example, if something goes wrong in link2.xsl, just change one symbol in xsl:copy-of to make it look like <xsl:copy-of select="exsl:node-set($link23)/node()"/> and pipeline will stop at 2nd transformation.
[3] exslt:node-set() is not required for XSLT 2.0 processor
Sample code is attached below.
If user is uninstalling your application, it is reasonable to ask him what he did not liked in the application. Usually developers open web browser for submission of feedback.
I think this is not a good practise — most users do not like applications opening web browser without their confirmation. My opinion it is a lot better to implement sending of such kind of feedback using desktop's regular mail application, such as Outlook and Thunderbird.
For example, this is IndieVolume Uninstall Feedback window:

This is InnoSetup pascal code I have developed to implement above window:
#include "fixfonts.iss"
#include "feedback.iss"
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
if CurUninstallStep = usUninstall then
begin
UninstallFeedback('IndieVolume Uninstall Feedback', 'Send', 'Cancel',
'To help us with future versions of IndieVolume, we want to know about any troubles or difficulties you have experienced while using IndieVolume.'#13#10 +
''#13#10'Please let us know why are you uninstalling IndieVolume. Thank You.',
'support@gerixsoft.com', 'IndieVolume Uninstall Feedback');
end;
end;
Attached you can find feedback.iss. For fixfonts.iss see this post
InnoSetup does not support Windows system font, it is using MS Sans Serif 8 instead.
Use the following code to fix this:
#include "fixfonts.iss" procedure InitializeWizard(); begin SetSystemFont(WizardForm.Font); FixSystemFont(WizardForm); end;
fixfonts.iss is attached below. This code has been in use in IndieVolume for years, confirmed to work ok.
If you have applied my techniques fixing Large Fonts and System Font to all forms in your Delphi application, most likely there will be two forms still untouched. These will be the forms hidden inside MessageDlg and InputQuery VCL functions.
Fortunately, MessageDlg supports Large Fonts and System Font ok, but InputQuery supports only Large Fonts. In this post I will show how to modify InputQuery to support System Font.
Screenshot: Original InputQuery when System Font is Tahoma, DPI=120

InputQuery implementation is hidden inside VCL, so a patch to VCL source code is needed.
The resulting InputQuery window will look like:
