sebduggan.ukPersonal website and blog of Seb Duggan, a UK-based web developer.2023-03-18T18:13:17Zhttps://sebduggan.uk/Seb Dugganseb@sebduggan.comRelaunching my site in Eleventy2023-03-18T18:13:17Zhttps://sebduggan.uk/posts/relaunching-my-site-in-eleventy/<p>When I last rebuilt my personal site, back in 2017, I was full of good intentions. It was built on Hugo, which was the SSG I chose at the time (a coin flip between Hugo and Jekyll). It had a basic theme, and could be published by simply committing to a Git repo. What could possibly go wrong?</p>
<p>Jump forward 6 years, and the only content I posted was three related articles within the first month. And ever since, it has simply stagnated. It didn't help that for some reason I found it difficult to get the environment set up on my new laptop...</p>
<p>Now, in 2023, it seems that all the cool kids are using <a href="https://www.11ty.dev/">Eleventy</a> - and I can see why. A combination of speed (it blows the other SSGs out of the water) and ease of use (being based on Node, it’s using stuff I‘ve already got set up and installed) makes it the obvious choice. On top of that, there’s an active and friendly community out there too.</p>
<p>The release of Eleventy 2.0 seemed the perfect time to make the switch. This site is currently bare-bones, but if I waited until I had the site perfect I’d never get it launched (or write any new content for it).</p>
<p>So, I’ll be developing the site out in the open, gradually implementing new features and designs. But this is what I have done so far:</p>
<ul>
<li>migrated my existing content (what little there is of it) into Eleventy</li>
<li>based the site on the <a href="https://www.11ty.dev/docs/starter/">eleventy-base-blog starter</a></li>
<li>converted much of the site to be based on <a href="https://www.11ty.dev/docs/languages/webc/">WebC</a> components and layouts. This seems really cool!</li>
<li>added a <a href="https://www.robincussol.com/optimize-your-img-tags-with-eleventy-image-and-webc/">WebC component to replace images</a> with responsive alternate sizes and formats</li>
<li>implemented <a href="https://www.hoeser.dev/blog/2023-02-07-eleventy-shiki-simple/">Shiki</a> for rendering code examples, in place of the standard Eleventy syntax highlighter. Also added a custom language definition to handle CFML code blocks</li>
<li>added Netlify CMS (which seems to have become <a href="https://decapcms.org">Decap CMS</a> since I started on this project) to make it easier to draft and publish new content. I’m hoping this will be the magic bullet that will encourage me to post more regularly!</li>
</ul>
<p>And, for the future, I plan to overhaul the theme to make it mine; make more use of WebC components (did I mention how cool these are?); and add webmentions to my posts.</p>
<p>So, here’s to a new beginning - I hope I can keep to my good intentions…</p>
Preside selectData(): related objects and joins2017-02-21T12:00:00Zhttps://sebduggan.uk/posts/preside-selectdata-related-objects-and-joins/<p>In the first two parts of this series, I've looked at <a href="https://sebduggan.uk/posts/preside-selectdata-the-basics/">selecting</a> and <a href="https://sebduggan.uk/posts/preside-selectdata-filtering-data/">filtering</a> data from Preside objects using <code>selectData()</code>.</p>
<p>In this third and final instalment, we'll be accessing the power of related objects. Simple, standalone data objects are all very well, but even a very basic application will define connections between objects. These will be in the form of <strong>many-to-many</strong> and <strong>many-to-one</strong> relationships (also <strong>one-to-many</strong>, which is a many-to-one relationship looked at from the other direction).</p>
<p>If we look at the <code>starwars_character.cfc</code> object, we can see both types of relationship:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #569CD6">component</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #6A9955">// ... other basic properties defined ...</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">property</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"homeworld"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">relationship</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"many-to-one"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">relatedTo</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"starwars_planet"</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">property</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"films"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">relationship</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"many-to-many"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">relatedTo</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"starwars_film"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">relatedVia</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"starwars_film_character"</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">property</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"species"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">relationship</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"many-to-many"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">relatedTo</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"starwars_species"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">relatedVia</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"starwars_character_species"</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">property</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"starships"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">relationship</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"many-to-many"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">relatedTo</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"starwars_starship"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">relatedVia</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"starwars_starship_pilot"</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">property</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"vehicles"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">relationship</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"many-to-many"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">relatedTo</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"starwars_vehicle"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">relatedVia</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"starwars_vehicle_pilot"</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span></code></pre>
<p>We can see from this that a character has a <code>homeworld</code> property, by which it can be related to one single planet; but then can be related to multiple <code>films</code>, <code>vehicles</code>, etc.</p>
<h3 id="basic-joins" tabindex="-1">Basic joins <a class="header-anchor" href="https://sebduggan.uk/posts/preside-selectdata-related-objects-and-joins/">#</a></h3>
<p>The key to <code>selectData()</code> joins is to use a dotted syntax in your column definition, very much like you would in SQL. Bear in mind though that you are using property names rather than table names, e.g. <code>property_name_of_related_object.property_on_related_object</code>:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">characters</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_character"</span></span>
<span class="line"><span style="color: #D4D4D4"> , selectFields = [</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #CE9178">"name"</span></span>
<span class="line"><span style="color: #D4D4D4"> , </span><span style="color: #CE9178">"homeworld.name as homeworld_name"</span></span>
<span class="line"><span style="color: #D4D4D4"> ]</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre>
<p>Here we are selecting each character along with the name of their homeworld. Note that <code>homeworld.name</code> is actually retrieving data from the <code>starwars_planet</code> object -- but the property name is <code>homeworld</code>.</p>
<p>Behind the scenes, Preside is converting this to the following SQL (but you usually won't need to worry about this):</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">sql</div><code><span class="line"><span style="color: #569CD6">select</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">`starwars_character`</span><span style="color: #D4D4D4">.</span><span style="color: #CE9178">`name`</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">`homeworld`</span><span style="color: #D4D4D4">.</span><span style="color: #CE9178">`name`</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">as</span><span style="color: #D4D4D4"> homeworld_name</span></span>
<span class="line"><span style="color: #569CD6">from</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">`pobj_starwars_character`</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">`starwars_character`</span></span>
<span class="line"><span style="color: #569CD6">left join</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">`pobj_starwars_planet`</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">`homeworld`</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">on</span><span style="color: #D4D4D4"> (</span><span style="color: #CE9178">`homeworld`</span><span style="color: #D4D4D4">.</span><span style="color: #CE9178">`id`</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">`starwars_character`</span><span style="color: #D4D4D4">.</span><span style="color: #CE9178">`homeworld`</span><span style="color: #D4D4D4">)</span></span>
<span class="line"></span></code></pre>
<h3 id="extended-joins" tabindex="-1">Extended joins <a class="header-anchor" href="https://sebduggan.uk/posts/preside-selectdata-related-objects-and-joins/">#</a></h3>
<p>This gives you data from an object that is directly related to your main object, but Preside allows you to traverse joins as deep as you like. For instance, you might want to get values from an object related to an object related to your main object. To do this, you simple separate the objects in your chain with a <code>$</code>.</p>
<p>In this example, we are getting a list of characters, their homeworld, and the titles of films in which that homeworld has appeared:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">characters</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_character"</span></span>
<span class="line"><span style="color: #D4D4D4"> , selectFields = [</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #CE9178">"name"</span></span>
<span class="line"><span style="color: #D4D4D4"> , </span><span style="color: #CE9178">"homeworld.name as homeworld"</span></span>
<span class="line"><span style="color: #D4D4D4"> , </span><span style="color: #CE9178">"homeworld$films.title as homeworld_film"</span></span>
<span class="line"><span style="color: #D4D4D4"> ]</span></span>
<span class="line"><span style="color: #D4D4D4"> , orderBy = </span><span style="color: #CE9178">"name, homeworld.name, homeworld$films.episode_id"</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre>
<blockquote>
<p>You will see that the <strong>many-to-many</strong> relationships are stored in linking tables (whose names are defined here using the <code>relatedVia</code> attribute, but you can leave Preside to set default names for these tables). When selecting data using <code>selectData()</code> you can ignore these linking tables, as Preside does all the heavy lifting for you -- it will create SQL joins between <code>starwars_character</code> and <code>starwars_film_character</code>, and then between <code>starwars_film_character</code> and <code>starwars_film</code>.</p>
</blockquote>
<p>Or, one step further down the rabbit-hole, we can also add all the species that appeared in films in which that homeworld appeared:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">characters</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_character"</span></span>
<span class="line"><span style="color: #D4D4D4"> , selectFields = [</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #CE9178">"name"</span></span>
<span class="line"><span style="color: #D4D4D4"> , </span><span style="color: #CE9178">"homeworld.name as homeworld"</span></span>
<span class="line"><span style="color: #D4D4D4"> , </span><span style="color: #CE9178">"homeworld$films.title as homeworld_film"</span></span>
<span class="line"><span style="color: #D4D4D4"> , </span><span style="color: #CE9178">"homeworld$films$species.name as homeworld_film_species"</span></span>
<span class="line"><span style="color: #D4D4D4"> ]</span></span>
<span class="line"><span style="color: #D4D4D4"> , orderBy = </span><span style="color: #CE9178">"name, homeworld.name, homeworld$films.episode_id, homeworld$films$species.name"</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre>
<p>Note that we use the same syntax for relationships when defining <code>orderBy</code> parameters (and also <code>groupBy</code>, <code>filter</code>, etc.).</p>
<blockquote>
<p>Because we are returning a query with joins, there will be many rows of data for each character, containing all the combinations of homeworlds, films and species that match the request. You might want to use the <code>group</code> attribute on your <code>cfloop</code> or <code>cfoutput</code> for the query in order to group the data meaningfully.</p>
</blockquote>
<h3 id="join-types" tabindex="-1">Join types <a class="header-anchor" href="https://sebduggan.uk/posts/preside-selectdata-related-objects-and-joins/">#</a></h3>
<p>By default, most joins that Preside performs will be <code>left outer join</code> (joins to required properties will, however, use an <code>inner join</code>). So the following query will return <em>all</em> characters and the starships they are associated with, or a blank/null value for the starship if they are not associated with any:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">characters</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_character"</span></span>
<span class="line"><span style="color: #D4D4D4"> , selectFields = [ </span><span style="color: #CE9178">"name"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"starships.name as starship"</span><span style="color: #D4D4D4"> ]</span></span>
<span class="line"><span style="color: #D4D4D4">)</span></span>
<span class="line"></span></code></pre>
<p>However, it may be that you only wish to return those characters who actually have starships assigned to them. In SQL this is done using an <code>inner join</code>; you can instruct Preside to use inner joins for all joins in a query by using the <code>forceJoins</code> argument:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">characters</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_character"</span></span>
<span class="line"><span style="color: #D4D4D4"> , selectFields = [ </span><span style="color: #CE9178">"name"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"starships.name as starship"</span><span style="color: #D4D4D4"> ]</span></span>
<span class="line"><span style="color: #D4D4D4"> , forceJoins = </span><span style="color: #CE9178">"inner"</span></span>
<span class="line"><span style="color: #D4D4D4">)</span></span>
<span class="line"></span></code></pre>
<p>Valid values for this are <code>left</code> (the default) and <code>inner</code>.</p>
<h3 id="extra-joins-and-subqueries" tabindex="-1">Extra joins and subqueries <a class="header-anchor" href="https://sebduggan.uk/posts/preside-selectdata-related-objects-and-joins/">#</a></h3>
<p>Of course, sometimes the data you want to extract from the database will not be accessible via the object's defined relationships. Maybe you need a subquery that does some calculation for you, or maybe you need to join a table to itself.</p>
<p>This is achieved using the <code>extraJoins</code> argument. This is an array of explicitly defined subqueries, along with all the information needed to join it to your main query:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">characters</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_character"</span></span>
<span class="line"><span style="color: #D4D4D4"> , selectFields = [ </span><span style="color: #CE9178">"name"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"homeworld.name as homeworld_name"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"homeworld_count.character_count"</span><span style="color: #D4D4D4"> ]</span></span>
<span class="line"><span style="color: #D4D4D4"> , orderBy = </span><span style="color: #CE9178">"homeworld_count.character_count desc, name"</span></span>
<span class="line"><span style="color: #D4D4D4"> , filter = </span><span style="color: #CE9178">"homeworld_count.homeworld = starwars_character.homeworld"</span></span>
<span class="line"><span style="color: #D4D4D4"> , extraJoins = [ {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">subQuery</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">"select count( id ) as character_count, homeworld from pobj_starwars_character group by homeworld"</span></span>
<span class="line"><span style="color: #D4D4D4"> , </span><span style="color: #9CDCFE">subQueryAlias</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">"homeworld_count"</span></span>
<span class="line"><span style="color: #D4D4D4"> , </span><span style="color: #9CDCFE">subQueryColumn</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">"homeworld"</span></span>
<span class="line"><span style="color: #D4D4D4"> , </span><span style="color: #9CDCFE">joinToTable</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">"starwars_character"</span></span>
<span class="line"><span style="color: #D4D4D4"> , </span><span style="color: #9CDCFE">joinToColumn</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">"homeworld"</span></span>
<span class="line"><span style="color: #D4D4D4"> , </span><span style="color: #9CDCFE">type</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">"inner"</span></span>
<span class="line"><span style="color: #D4D4D4"> } ]</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre>
<p>This query will return a list of characters along with the total count of characters from the same homeworld. All 6 of the parameters for the <code>extraJoin</code> are required:</p>
<p><code>subQuery</code> is the SQL of the subquery. Note that table names in here are the actual database table names, <em>not</em> the name of the Preside object. <code>subQueryAlias</code> gives the subquery a name, and <code>subQueryColumn</code> is the column in this query that will be used to join to a column on the main query (defined by <code>joinToTable</code> (Preside object name) and <code>joinToColumn</code>). Finally, the <code>type</code> parameter defines the type of join (<code>inner</code> or <code>left</code>) that will be used to join to the subquery.</p>
<p>To clarify how all this fits together, here is the generated SQL from this request:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">sql</div><code><span class="line"><span style="color: #569CD6">select</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">`starwars_character`</span><span style="color: #D4D4D4">.</span><span style="color: #CE9178">`name`</span><span style="color: #D4D4D4">, homeworld.name </span><span style="color: #569CD6">as</span><span style="color: #D4D4D4"> homeworld_name, homeworld_count.character_count</span></span>
<span class="line"><span style="color: #569CD6">from</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">`pobj_starwars_character`</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">`starwars_character`</span></span>
<span class="line"><span style="color: #569CD6">left join</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">`pobj_starwars_planet`</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">`homeworld`</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">on</span><span style="color: #D4D4D4"> (</span><span style="color: #CE9178">`homeworld`</span><span style="color: #D4D4D4">.</span><span style="color: #CE9178">`id`</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">`starwars_character`</span><span style="color: #D4D4D4">.</span><span style="color: #CE9178">`homeworld`</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #569CD6">inner join</span><span style="color: #D4D4D4"> (</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">select</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">count</span><span style="color: #D4D4D4">( id ) </span><span style="color: #569CD6">as</span><span style="color: #D4D4D4"> character_count, homeworld</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">from</span><span style="color: #D4D4D4"> pobj_starwars_character</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">group by</span><span style="color: #D4D4D4"> homeworld</span></span>
<span class="line"><span style="color: #D4D4D4">) </span><span style="color: #CE9178">`homeworld_count`</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">on</span><span style="color: #D4D4D4"> (</span><span style="color: #CE9178">`homeworld_count`</span><span style="color: #D4D4D4">.</span><span style="color: #CE9178">`homeworld`</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">`starwars_character`</span><span style="color: #D4D4D4">.</span><span style="color: #CE9178">`homeworld`</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #569CD6">where</span><span style="color: #D4D4D4"> homeworld_count.homeworld = starwars_character.homeworld</span></span>
<span class="line"><span style="color: #569CD6">order by</span><span style="color: #D4D4D4"> homeworld_count.character_count </span><span style="color: #569CD6">desc</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">`starwars_character`</span><span style="color: #D4D4D4">.</span><span style="color: #CE9178">`name`</span></span>
<span class="line"></span></code></pre>
<blockquote>
<p>Remember from <a href="https://sebduggan.uk/posts/preside-selectdata-the-basics/">part one</a> how you can use the <code>getSqlAndParamsOnly</code> argument to return a query and its parameters? This can come in very handy when you're building complex joins with subqueries...</p>
</blockquote>
<p>There is one more (optional) parameter that an <code>extraJoin</code> can take, and that is <code>additionalClauses</code>. This is a SQL string which will be added to the <code>on</code> clause which joins the subquery. The following (rather contrived) example will return a list of characters, their homeworld, and the number of characters from that homeworld who share the same hair colour.</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">result</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_character"</span></span>
<span class="line"><span style="color: #D4D4D4"> , selectFields = [ </span><span style="color: #CE9178">"name"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"homeworld.name as homeworld_name"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"count( homeworld_subquery.hair_color ) as same_hair_color_count"</span><span style="color: #D4D4D4"> ]</span></span>
<span class="line"><span style="color: #D4D4D4"> , orderBy = </span><span style="color: #CE9178">"name"</span></span>
<span class="line"><span style="color: #D4D4D4"> , groupBy = </span><span style="color: #CE9178">"starwars_character.id, homeworld_subquery.hair_color"</span></span>
<span class="line"><span style="color: #D4D4D4"> , filter = </span><span style="color: #CE9178">"homeworld_subquery.homeworld = starwars_character.homeworld"</span></span>
<span class="line"><span style="color: #D4D4D4"> , extraJoins = [ {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">subQuery</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">"select id, homeworld, hair_color from pobj_starwars_character"</span></span>
<span class="line"><span style="color: #D4D4D4"> , </span><span style="color: #9CDCFE">subQueryAlias</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">"homeworld_subquery"</span></span>
<span class="line"><span style="color: #D4D4D4"> , </span><span style="color: #9CDCFE">subQueryColumn</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">"homeworld"</span></span>
<span class="line"><span style="color: #D4D4D4"> , </span><span style="color: #9CDCFE">joinToTable</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">"starwars_character"</span></span>
<span class="line"><span style="color: #D4D4D4"> , </span><span style="color: #9CDCFE">joinToColumn</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">"homeworld"</span></span>
<span class="line"><span style="color: #D4D4D4"> , </span><span style="color: #9CDCFE">type</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">"inner"</span></span>
<span class="line"><span style="color: #D4D4D4"> , </span><span style="color: #9CDCFE">additionalClauses</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">"homeworld_subquery.hair_color = starwars_character.hair_color"</span></span>
<span class="line"><span style="color: #D4D4D4"> } ]</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre>
<blockquote>
<p>Both the <code>subQuery</code> and <code>additionalClauses</code> values may include dynamically defined filter parameters, using the same <code>:parameter_name</code> syntax as elsewhere. These dynamic values would be defined in the <code>filterParams</code> argument of the main query, and may be shared by the main query and its subqueries.</p>
</blockquote>
<h3 id="aggregate-functions" tabindex="-1">Aggregate functions <a class="header-anchor" href="https://sebduggan.uk/posts/preside-selectdata-related-objects-and-joins/">#</a></h3>
<p>In the previous example, you'll notice that we've used a <code>count()</code> aggregate function in our <code>selectFields</code>. This shows how easy it is to include aggregate data functions (this combines here with the <code>groupBy</code> clause). Here are a few more examples of simple aggregation of data:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">longestVehicle</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_vehicle"</span></span>
<span class="line"><span style="color: #D4D4D4"> , selectFields = [ </span><span style="color: #CE9178">"max( length ) as max_length"</span><span style="color: #D4D4D4"> ]</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #9CDCFE">vehiclesClasses</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_vehicle"</span></span>
<span class="line"><span style="color: #D4D4D4"> , selectFields = [ </span><span style="color: #CE9178">"vehicle_class"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"count( vehicle_class ) as vehicle_count"</span><span style="color: #D4D4D4"> ]</span></span>
<span class="line"><span style="color: #D4D4D4"> , groupBy = </span><span style="color: #CE9178">"vehicle_class"</span></span>
<span class="line"><span style="color: #D4D4D4"> , orderBy = </span><span style="color: #CE9178">"vehicle_count desc, vehicle_class"</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre>
<p>There are of course occasions when a subquery using an <code>extraJoin</code> will be the better solution, but most basic aggregation can be achieved just by including here -- especially as of Preside 10.8.0, which adds to <code>selectData()</code> the <code>having</code> argument:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">vehiclesClasses</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_vehicle"</span></span>
<span class="line"><span style="color: #D4D4D4"> , selectFields = [ </span><span style="color: #CE9178">"vehicle_class"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"count( vehicle_class ) as vehicle_count"</span><span style="color: #D4D4D4"> ]</span></span>
<span class="line"><span style="color: #D4D4D4"> , groupBy = </span><span style="color: #CE9178">"vehicle_class"</span></span>
<span class="line"><span style="color: #D4D4D4"> , having = </span><span style="color: #CE9178">"count( vehicle_class ) >= 4"</span></span>
<span class="line"><span style="color: #D4D4D4"> , orderBy = </span><span style="color: #CE9178">"vehicle_count desc, vehicle_class"</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre>
<p>This will return a list of vehicle classes with counts of vehicles, for classes with 4 or more vehicles. <code>having</code> functions exactly as it does in raw SQL, basically acting as a <code>where</code> clause which acts on the aggregate function.</p>
<p>Finally, you can use your DBMS's proprietary aggregate functions, just like any other function. A particularly useful one is MySQL's <code>group_concat()</code>, which you can use to generate lists of grouped data directly in the database; it is handy for getting a list of IDs of related records in order to pre-populate object pickers, etc. Note that it is MySQL only: PostgreSQL has the similar <code>string_agg()</code> function; but MS SQL Server doesn't let you do this without some ugly fiddling about with <code>xml path</code>. So if you're looking to create a database-agnostic application, you'll probably be better sticking with standard SQL and grouping the data in your CFML code.</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">characters</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_character"</span></span>
<span class="line"><span style="color: #D4D4D4"> , selectFields = [ </span><span style="color: #CE9178">"id"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"name"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"group_concat( films.title separator ', ' ) as films_appeared_in"</span><span style="color: #D4D4D4"> ]</span></span>
<span class="line"><span style="color: #D4D4D4"> , groupBy = </span><span style="color: #CE9178">"id"</span></span>
<span class="line"><span style="color: #D4D4D4"> , orderBy = </span><span style="color: #CE9178">"name"</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre>
<h3 id="conclusion" tabindex="-1">Conclusion <a class="header-anchor" href="https://sebduggan.uk/posts/preside-selectdata-related-objects-and-joins/">#</a></h3>
<p>I hope I've demonstrated in these posts the power of getting data from Preside's object model. I've learned a lot from putting this together, especially about complex joins and the trickier filters.</p>
<p>If you have any questions or suggestions, there is usually someone who can help on the <a href="https://presidecms-slack.herokuapp.com/">Preside Slack channel</a>, which is the best place to get any Preside questions answered...</p>
Preside selectData(): filtering data2017-02-13T12:00:00Zhttps://sebduggan.uk/posts/preside-selectdata-filtering-data/<p>If you've read <a href="https://sebduggan.uk/posts/preside-selectdata-the-basics/">part one of this series</a>, you know how to retrieve data from Preside objects using <code>selectData()</code>. But any web application will need you to refine the data down to a subset of the whole dataset, or just a simple record (think of a SQL <code>where</code> clause).</p>
<h3 id="simple-filtering" tabindex="-1">Simple filtering <a class="header-anchor" href="https://sebduggan.uk/posts/preside-selectdata-filtering-data/">#</a></h3>
<p>Every Preside data object has a unique id, and we can use this to retrieve a single record from the database:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">film</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_film"</span></span>
<span class="line"><span style="color: #D4D4D4"> , id = </span><span style="color: #CE9178">"9F049349-18C4-4ED6-848BC0A41F510821"</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre>
<p>In practice, of course, the ID you pass in is most likely to be dynamic -- maybe defined in the URL or form parameters in a request from another page.</p>
<p>Selecting by <code>id</code> (the record's primary key) is a special case; if you want to filter the records on any other field or fields, you pass a struct of filters to the <code>filter</code> argument:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">film</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_film"</span></span>
<span class="line"><span style="color: #D4D4D4"> , filter = {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">episode_id</span><span style="color: #D4D4D4"> = </span><span style="color: #B5CEA8">5</span></span>
<span class="line"><span style="color: #D4D4D4"> }</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre>
<p>You can filter by multiple column values at the same time - if you do this using a <code>filter</code> struct, it will return records that match <em>all</em> the filter values, equivalent to SQL's <code>where ... and ...</code>. In the next example, we'd are asking for all characters who are female <em>and</em> have brown hair:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">characters</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_character"</span></span>
<span class="line"><span style="color: #D4D4D4"> , filter = {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">gender</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">"female"</span></span>
<span class="line"><span style="color: #D4D4D4"> , </span><span style="color: #9CDCFE">hair_color</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">"brown"</span></span>
<span class="line"><span style="color: #D4D4D4"> }</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre>
<p>This method of filtering also allows you to match a column against one of a set of values (a SQL <code>where ... in (...)</code> clause). This is achieved by passing an array of values in the filter struct. So this next query would retrieve characters whose eyes are either brown, blue or black:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">characters</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_character"</span></span>
<span class="line"><span style="color: #D4D4D4"> , filter = {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">eye_color</span><span style="color: #D4D4D4"> = [ </span><span style="color: #CE9178">"brown"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"blue"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"black"</span><span style="color: #D4D4D4"> ]</span></span>
<span class="line"><span style="color: #D4D4D4"> }</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre>
<h3 id="more-complex-filters" tabindex="-1">More complex filters <a class="header-anchor" href="https://sebduggan.uk/posts/preside-selectdata-filtering-data/">#</a></h3>
<p>All the examples so far have been filtering for an "equals" match. But what if you need to do a greater than or non-equality comparison, or a complex grouping of conditions?</p>
<p>For this, you simply pass any valid SQL string to the <code>filter</code> argument instead of a struct:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">theGoodFilms</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_film"</span></span>
<span class="line"><span style="color: #D4D4D4"> , filter = </span><span style="color: #CE9178">"episode_id >= 4 and episode_id != 6"</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre>
<p>Of course, usually the values will be dynamic, according to the requiredments of the request. To pass parameterised values into the filter, pass a struct of values in via the <code>filterParams</code> argument:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">film</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_film"</span></span>
<span class="line"><span style="color: #D4D4D4"> , filter = </span><span style="color: #CE9178">"title != :title"</span></span>
<span class="line"><span style="color: #D4D4D4"> , filterParams = {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">title</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">"A New Hope"</span></span>
<span class="line"><span style="color: #D4D4D4"> }</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #9CDCFE">skywalkers</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_character"</span></span>
<span class="line"><span style="color: #D4D4D4"> , filter = </span><span style="color: #CE9178">"name like :name"</span></span>
<span class="line"><span style="color: #D4D4D4"> , filterParams = {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">"%Skywalker"</span></span>
<span class="line"><span style="color: #D4D4D4"> }</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre>
<p>In these cases, because the parameter name is the same as the field name (<code>title</code>, <code>name</code>), Preside is able to use the object definition to know what the SQL type is of the parameter (in both cases, it's <code>varchar</code>). However, often you will need to give the parameter a different name (for example, if we were executing the previous example with <code>filterParams</code>). You will now need to define explicitly the type of the parameter by setting its value to be a struct containing <code>type</code> (the SQL type of the field, as defined in your object, e.g. <code>int</code> or <code>varchar</code>) and <code>value</code> (the actual value to be parameterised):</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">theGoodFilms</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_film"</span></span>
<span class="line"><span style="color: #D4D4D4"> , filter = </span><span style="color: #CE9178">"episode_id >= :minEpisode and episode_id != :excludeEpisode"</span></span>
<span class="line"><span style="color: #D4D4D4"> , filterParams = {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">minEpisode</span><span style="color: #D4D4D4"> = { </span><span style="color: #9CDCFE">type</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"int"</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">value</span><span style="color: #D4D4D4">=</span><span style="color: #B5CEA8">4</span><span style="color: #D4D4D4"> }</span></span>
<span class="line"><span style="color: #D4D4D4"> , </span><span style="color: #9CDCFE">excludeEpisode</span><span style="color: #D4D4D4"> = { </span><span style="color: #9CDCFE">type</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"int"</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">value</span><span style="color: #D4D4D4">=</span><span style="color: #B5CEA8">6</span><span style="color: #D4D4D4"> }</span></span>
<span class="line"><span style="color: #D4D4D4"> }</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre>
<p>Using the <code>filterParams</code> technique, you can still pass in arrays for SQL <code>in ( ... )</code> queries:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">characters</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_character"</span></span>
<span class="line"><span style="color: #D4D4D4"> , filter = </span><span style="color: #CE9178">"eye_color not in ( :eye_color )"</span></span>
<span class="line"><span style="color: #D4D4D4"> , filterParams = {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">eye_color</span><span style="color: #D4D4D4"> = [ </span><span style="color: #CE9178">"brown"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"blue"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"black"</span><span style="color: #D4D4D4"> ]</span></span>
<span class="line"><span style="color: #D4D4D4"> }</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #9CDCFE">characters</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_character"</span></span>
<span class="line"><span style="color: #D4D4D4"> , filter = </span><span style="color: #CE9178">"eye_color not in ( :colours )"</span></span>
<span class="line"><span style="color: #D4D4D4"> , filterParams = {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">colours</span><span style="color: #D4D4D4"> = { </span><span style="color: #9CDCFE">type</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"varchar"</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">value</span><span style="color: #D4D4D4">=[ </span><span style="color: #CE9178">"brown"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"blue"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"black"</span><span style="color: #D4D4D4"> ] }</span></span>
<span class="line"><span style="color: #D4D4D4"> }</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre>
<blockquote>
<p><strong>WARNING!</strong> Never include variables directly in the <code>filter</code> string. These will be inserted directly into the generated SQL statement, and will leave you wide open to <a href="https://en.wikipedia.org/wiki/SQL_injection">SQL injection attacks</a>. <strong>Always</strong> pass dynamic variables in via <code>filterParams</code>.</p>
</blockquote>
<h3 id="extra-filters" tabindex="-1">Extra filters <a class="header-anchor" href="https://sebduggan.uk/posts/preside-selectdata-filtering-data/">#</a></h3>
<p>There are occasions when you will have a basic set of filters for a scenario, but you may also want in addition to apply other filters (some of which might be generated by helper methods). You <em>could</em> achieve this by appending all the filters to the main filter set, but the neater way is by using the <code>extraFilters</code> argument.</p>
<p><code>extraFilters</code> is an array of filter conditions, each of which can be constructed using any of the variations discussed above. Each item in the array will be a struct with a <code>filter</code> value and an optional <code>filterParams</code> value.</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">characters</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_character"</span></span>
<span class="line"><span style="color: #D4D4D4"> , filter = { </span><span style="color: #9CDCFE">gender</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"female"</span><span style="color: #D4D4D4"> }</span></span>
<span class="line"><span style="color: #D4D4D4"> , extraFilters = [</span></span>
<span class="line"><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">filter</span><span style="color: #D4D4D4"> = { </span><span style="color: #9CDCFE">hair_color</span><span style="color: #D4D4D4">=[ </span><span style="color: #CE9178">"black"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"brown"</span><span style="color: #D4D4D4"> ] }</span></span>
<span class="line"><span style="color: #D4D4D4"> }</span></span>
<span class="line"><span style="color: #D4D4D4"> , {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">filter</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">"eye_color != :excludeColour"</span></span>
<span class="line"><span style="color: #D4D4D4"> , </span><span style="color: #9CDCFE">filterParams</span><span style="color: #D4D4D4"> = {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">excludeColour</span><span style="color: #D4D4D4"> = { </span><span style="color: #9CDCFE">type</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"varchar"</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">value</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"blue"</span><span style="color: #D4D4D4"> }</span></span>
<span class="line"><span style="color: #D4D4D4"> }</span></span>
<span class="line"><span style="color: #D4D4D4"> }</span></span>
<span class="line"><span style="color: #D4D4D4"> , </span><span style="color: #9CDCFE">getMyExtraCustomFilter</span><span style="color: #D4D4D4">()</span></span>
<span class="line"><span style="color: #D4D4D4"> ]</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre>
<p>In the above example we see a regular filter, plus <code>extraFilters</code> containing a simple filter, a filter with explicitly defined parameters, and a filter returned from a separate method. All four of these filters will be combined into one SQL <code>where</code> clause.</p>
<h3 id="saved-filters" tabindex="-1">Saved filters <a class="header-anchor" href="https://sebduggan.uk/posts/preside-selectdata-filtering-data/">#</a></h3>
<p>Preside allows you to pre-define filters to be used by your application in your <code>Config.cfc</code> file. These are actually of more use in other scenarios, such as filtering the records shown in an <code>objectPicker</code> form control, but they can also be used by <code>selectData()</code>.</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #6A9955">// application/config/Config.cfc</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">void</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">configure</span><span style="color: #D4D4D4">() {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #6A9955">// other configuration options...</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">settings</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">filters</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">created_in_last_month</span><span style="color: #D4D4D4"> = { </span><span style="color: #9CDCFE">filter</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">"datemodified >= date_add( now(), interval -1 month )"</span><span style="color: #D4D4D4"> };</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">settings</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">filters</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">modified_in_last_week</span><span style="color: #D4D4D4"> = { </span><span style="color: #9CDCFE">filter</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">"datemodified >= date_add( now(), interval -3 day )"</span><span style="color: #D4D4D4"> };</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span></code></pre>
<p>The <code>savedFilters</code> are then passed in as an array, and will be applied in addition to any other filters you have defined.</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">recentVehicles</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_vehicle"</span></span>
<span class="line"><span style="color: #D4D4D4"> , selectFields = [ </span><span style="color: #CE9178">"id"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"name"</span><span style="color: #D4D4D4"> ]</span></span>
<span class="line"><span style="color: #D4D4D4"> , savedFilters = [ </span><span style="color: #CE9178">"created_in_last_month"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"modified_in_last_week"</span><span style="color: #D4D4D4"> ]</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre>
<blockquote>
<p>When defining saved filters in <code>Config.cfc</code>, they will generally not be dynamic; however, you can define a saved filter as a closure function which will return the filter configuration based on its internal logic. The closure will take two arguments, the first of which will be an empty struct of arguments, but the second will be the <code>ColdboxController</code> object, which will allow you to access various useful bits of information from the application and request.</p>
</blockquote>
<h3 id="summary" tabindex="-1">Summary <a class="header-anchor" href="https://sebduggan.uk/posts/preside-selectdata-filtering-data/">#</a></h3>
<p>We now know how to select and filter data from single Preside objects, and have seen how flexible and powerful the data filtering is.</p>
<p>In the next part of this series I'll be looking at joins, and how easy Preside makes it to access data from multiple, related objects.</p>
Preside selectData(): the basics2017-02-11T12:00:00Zhttps://sebduggan.uk/posts/preside-selectdata-the-basics/<p><img src="https://sebduggan.uk/posts/preside-selectdata-the-basics/src/assets/img/uploads/preside-logo.svg" alt="Preside"></p>
<p>A little background to start with: <a href="https://www.preside.org">Preside</a> is an open-source web development platform, created at <a href="http://www.pixl8.co.uk">Pixl8 Interactive</a>, which runs on the Lucee application server. While at first glance it may look like any other CMS (it was originally called Preside CMS), it does so much more, and can form the hub of some very complex integrations between other applications. The key to this is an incredibly flexible content storage model, and the ability to extend or override almost any aspect of the application to suit your own individual needs.</p>
<p>I've only been using Preside for a few months, and started in my role as a Senior Developer at Pixl8 in December 2016. So I'm constantly learning new things about Preside, which puts me in an ideal position to write about it from the perspective of a newcomer. I expect to write plenty about Preside here in the future!</p>
<hr>
<p>The foundation of Preside's power is in its data layer implementation, which is based on <strong>Preside Data Objects</strong>. I may write in the future about aspects of these, but the <a href="https://docs.presidecms.com/devguides/presidedataobjects.html">official guide</a> covers their creation and configuration in a lot of detail. What I'm going to look at here (over the course of several posts) is retrieving data from Preside Objects, which is all done via the <code>selectData()</code> method of the <code>PresideObjectService</code>.</p>
<p>In order to demonstrate <code>selectData()</code>, we'll first need some data. A great source of sample data is the <a href="https://bitbucket.org/jjannek/preside-ext-starwars">Star Wars Preside Extension</a>, which imports data from <a href="http://swapi.co/">The Star Wars API</a> into Preside, and that's what I'll be using throughout these posts.</p>
<h3 id="the-simplest-query" tabindex="-1">The simplest query <a class="header-anchor" href="https://sebduggan.uk/posts/preside-selectdata-the-basics/">#</a></h3>
<p>The following is as simple as it gets. The only required argument for <code>selectData()</code> is <code>objectName</code>:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">films</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_film"</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre>
<p>This will return every column of every record from the specified object. One thing to note is that -- unlike ORMs and ORM-like approaches -- <code>selectData()</code> returns its data as a query resultset. This gives several benefits, not least of which is the ability to construct some very complex data requests to suit any requirement.</p>
<p>For all these examples, I have injected the <code>PresideObjectService</code> as a property:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">property</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"presideObjectService"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">inject</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"presideObjectService"</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span></code></pre>
<p>You can, if you want, use an "Auto Service Object", which is the given object decorated with the service API CRUD methods. This can be done either within your method:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">filmObject</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.getObject( </span><span style="color: #CE9178">"starwars_film"</span><span style="color: #D4D4D4"> );</span></span>
<span class="line"></span></code></pre>
<p>Or via injection:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">property</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"filmObject"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">inject</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"presidecms:object:starwars_film"</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span></code></pre>
<p>Your simplest query would then look like this:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">films</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">filmObject</span><span style="color: #D4D4D4">.selectData();</span></span>
<span class="line"></span></code></pre>
<h3 id="refining-your-query" tabindex="-1">Refining your query <a class="header-anchor" href="https://sebduggan.uk/posts/preside-selectdata-the-basics/">#</a></h3>
<p>That's all very well, but it's best usually only to retrieve the columns you require. This is done using the <code>selectFields</code> argument, an array of the columns you want:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">films</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_film"</span></span>
<span class="line"><span style="color: #D4D4D4"> , selectFields = [ </span><span style="color: #CE9178">"title"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"episode_id"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"director"</span><span style="color: #D4D4D4"> ]</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre>
<p>And <code>orderBy</code> will let you sort your results:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">films</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_film"</span></span>
<span class="line"><span style="color: #D4D4D4"> , selectFields = [ </span><span style="color: #CE9178">"title"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"episode_id"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"director"</span><span style="color: #D4D4D4"> ]</span></span>
<span class="line"><span style="color: #D4D4D4"> , orderBy = </span><span style="color: #CE9178">"episode_id"</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre>
<p><code>orderBy</code> is a simple SQL <code>order by</code> statement, so can be as complex as you like:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">vehicles</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_vehicle"</span></span>
<span class="line"><span style="color: #D4D4D4"> , selectFields = [ </span><span style="color: #CE9178">"name"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"model"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"vehicle_class"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"crew"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"passengers"</span><span style="color: #D4D4D4"> ]</span></span>
<span class="line"><span style="color: #D4D4D4"> , orderBy = </span><span style="color: #CE9178">"crew desc, passengers desc, name"</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre>
<h3 id="aliases-and-sql-functions" tabindex="-1">Aliases and SQL functions <a class="header-anchor" href="https://sebduggan.uk/posts/preside-selectdata-the-basics/">#</a></h3>
<p>If you wish, you can rename your columns using simple SQL aliases:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">films</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_film"</span></span>
<span class="line"><span style="color: #D4D4D4"> , selectFields = [ </span><span style="color: #CE9178">"title"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"episode_id as episode"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"director"</span><span style="color: #D4D4D4"> ]</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre>
<p>And you can insert SQL logic into your field definition:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">films</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_film"</span></span>
<span class="line"><span style="color: #D4D4D4"> , selectFields = [</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #CE9178">"episode_id"</span></span>
<span class="line"><span style="color: #D4D4D4"> , </span><span style="color: #CE9178">"concat( 'Episode ', episode_id, ': ', title ) as full_title"</span><span style="color: #D4D4D4"> ]</span></span>
<span class="line"><span style="color: #D4D4D4"> , orderBy = </span><span style="color: #CE9178">"episode_id"</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre>
<blockquote>
<p><strong>Note:</strong> While Preside's default database is MySQL/MariaDB, there is also beta support for MSSQL and PostgreSQL. When you're writing your own applications -- where you know what database you will be using -- you can use SQL syntax specific to that database. But if you plan on releasing, for example, a Preside extension, you should ensure either that you are using standard SQL syntax, or you provide logic that will provide alternate syntax depending on the DBMS in use.</p>
</blockquote>
<h3 id="paging-your-results" tabindex="-1">Paging your results <a class="header-anchor" href="https://sebduggan.uk/posts/preside-selectdata-the-basics/">#</a></h3>
<p>You will often want to limit the number of rows returned, and if you're creating a paged interface, you'll also want to specify which row to start from. This is achieved using <code>maxRows</code> and <code>startRow</code> respectively:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">pagedFilms</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_film"</span></span>
<span class="line"><span style="color: #D4D4D4"> , selectFields = [ </span><span style="color: #CE9178">"title"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"episode_id"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"director"</span><span style="color: #D4D4D4"> ]</span></span>
<span class="line"><span style="color: #D4D4D4"> , orderBy = </span><span style="color: #CE9178">"episode_id"</span></span>
<span class="line"><span style="color: #D4D4D4"> , maxRows = </span><span style="color: #B5CEA8">3</span></span>
<span class="line"><span style="color: #D4D4D4"> , startRow = </span><span style="color: #B5CEA8">4</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre>
<h3 id="new-in-preside-10-8-0" tabindex="-1">New in Preside 10.8.0 <a class="header-anchor" href="https://sebduggan.uk/posts/preside-selectdata-the-basics/">#</a></h3>
<p>There are a few arguments for <code>selectData()</code> which have been introduced in the forthcoming 10.8.0 release of Preside.</p>
<p><code>distinct</code> does exactly what you would expect: so the following query would return a distinct list of all the different hair colours of Star Wars characters, if that's the sort of thing you're interested in...</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">hairColours</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_character"</span></span>
<span class="line"><span style="color: #D4D4D4"> , selectFields = [ </span><span style="color: #CE9178">"hair_color"</span><span style="color: #D4D4D4"> ]</span></span>
<span class="line"><span style="color: #D4D4D4"> , distinct = </span><span style="color: #569CD6">true</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre>
<p>If all you want from your query is to get a count of matching records, use <code>recordCountOnly</code>:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">filmCount</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_film"</span></span>
<span class="line"><span style="color: #D4D4D4"> , recordCountOnly = </span><span style="color: #569CD6">true</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre>
<p>Finally, there are occasions when you may want simply to return the SQL generated by the method, rather than actually executing it. This might be useful for unit tests, or -- as we will discover in a later part of this series -- when constructing complex joins.</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">result</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">presideObjectService</span><span style="color: #D4D4D4">.selectData(</span></span>
<span class="line"><span style="color: #D4D4D4"> objectName = </span><span style="color: #CE9178">"starwars_character"</span></span>
<span class="line"><span style="color: #D4D4D4"> , getSqlAndParamsOnly = </span><span style="color: #569CD6">true</span></span>
<span class="line"><span style="color: #D4D4D4">);</span></span>
<span class="line"></span></code></pre>
<p>This will return a struct with two items: <code>sql</code>, which contains the SQL statement, and <code>params</code>, an array of all the SQL parameters for that query (more of this when we get to filters in a later post).</p>
<h3 id="summary" tabindex="-1">Summary <a class="header-anchor" href="https://sebduggan.uk/posts/preside-selectdata-the-basics/">#</a></h3>
<p>In this post we've looked at the basics of retrieving data in Preside with <code>selectData()</code> with some very trivial examples.</p>
<p>Real-world applications are, of course, more complicated, and in future parts I'll be talking about <strong><a href="https://sebduggan.uk/posts/preside-selectdata-filtering-data/">filtering records</a></strong>, <strong>joins</strong>, <strong>subqueries</strong> and <strong>aggregate functions</strong>.</p>
Let's Encrypt SSL with Apache reverse proxy2017-02-07T12:00:00Zhttps://sebduggan.uk/posts/lets-encrypt-and-apache-reverse-proxy/<p>After years of avoiding SSL like the plague, unless the website absolutely, positively demanded it -- due to certificates being both expensive and a nightmare to install/renew -- I've just discovered <a href="https://letsencrypt.org/">Let's Encrypt</a>, thanks to the one-click simplicity of adding SSL to this site via the <a href="https://www.netlify.com">Netlify</a> dashboard...</p>
<p>Within 20 minutes of first having the idea, I had all the (more than 20) sites on my CentOS webserver running over HTTPS -- <em>at no cost</em>.</p>
<p>I won't go into the details of setting it all up here, as it's all covered perfectly well over at <a href="https://certbot.eff.org/">Certbot</a>. But I will share the one issue I did have (which added an extra 15 minutes to the setup process).</p>
<p>My sites are powered by <a href="http://lucee.org/">Lucee</a> running on Tomcat, served via a reverse proxy (mod_proxy) on an Apache web server. The problem was that the Lucee sites were unaware that they were being served over SSL, and so sites which were set up to redirect to HTTPS -- if not already -- were going into an endless redirect loop.</p>
<p>The solution is simple. There is an HTTP header, <code>X-Forwarded-Proto</code>, which is a de-facto standard for identifying the originating protocol of an HTTP request when routed through a reverse proxy. If you set it in your vhost definitions on SSL sites, most applications should know that they are being served over HTTPS.</p>
<p>The easiest way (if you've used <code>certbot --apache</code> to register and install your certificates) is to edit the file <code>/etc/letsencrypt/options-ssl-apache.conf</code> (location may differ in different flavours of Linux). Simply add the following:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">apache</div><code><span class="line"><span style="color: #6A9955"># ...</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955"># Intermediate configuration, tweak to your needs</span></span>
<span class="line"><span style="color: #569CD6">RequestHeader</span><span style="color: #D4D4D4"> set X-Forwarded-Proto "https"</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955"># ...</span></span>
<span class="line"></span></code></pre>
<p>...and restart Apache, and all traffic passing through the proxy will be identified as originating from an HTTPS request.</p>
Working with FoxyCart data feeds in CFML2016-05-03T12:00:00Zhttps://sebduggan.uk/posts/working-with-foxycart-data-feeds-in-cfml/<p>On a couple of the sites I manage, I have employed <a href="http://www.foxycart.com/">FoxyCart</a> to handle the shopping cart and e-commerce. It provides a simple integration into an existing site with very little fuss, and makes it easy to offload the business of running a webshop.</p>
<p>One of the features offered is to post back an <a href="https://wiki.foxycart.com/v/2.0/transaction_xml_datafeed">XML data feed</a> of each completed transaction, in real time, to your server. You may want this to maintain your own database of orders placed; in my case, it is used to handle MailChimp subscriptions if they ticked the appropriate box during checkout.</p>
<p>However, the encoding/encrypting of the data feed is somewhat arcane, and has taken me a couple of days, on and off, to get up and running in CFML. In fact, due to time constraints, I initially had to spin up a <a href="https://m.do.co/c/37a2233a2f63">DigitalOcean</a> droplet running PHP in order to handle the process until I could get it working in CFML. (There was a tried and tested PHP solution which I could get running in no time.)</p>
<p>This guide is to help any other poor souls in the future who have to wrestle with this...</p>
<p>The way it works is this: FoxyCart does an RC4 encryption of the XML feed using your own API key (they'll generate one for you, or you can set it yourself), and it then URL-encodes the rseult and POSTs it to your server (you've previously told them what script on your server to fire it at).</p>
<p>Easy, I thought. <code>urlDecode()</code> and then <code>decrypt()</code> using the RC4 algorithm.</p>
<p>Nope.</p>
<p>After much trial and error (mostly error), and hacking the PHP so I could compare exactly what the data was at each stage of the process, I finally solved it. There are two things you need to bear in mind:</p>
<ul>
<li>When doing the <code>urlDecode()</code>, you need to specify the encoding as <strong>ISO-8859-1</strong> (Latin-1). If you use any other encoding, you'll get a bunch of gibberish with loads of high-ASCII values.</li>
<li>Do not use the CFML <code>decrypt()</code> function - it doesn't seem to work as expected. I found a number of RC4 libraries in various languages, each doing pretty much the same thing, and there was a <a href="http://cfrc4.riaforge.org/">CFML version written by Steve Hicks</a>. It needed a slight modification, though, as Steve's version was expecting the encrypted text to be in Hex format - which it isn't.</li>
</ul>
<p>Having done that, you should have a nice XML string which you can parse as normal and then do whatever you will with the data.</p>
<p>These are the two files you'll need to implement the data feed processing - <code>foxychimp.cfm</code>, which is the file you should point at in the FoxyCart admin, and <code>RC4.cfc</code>, which is my modified version of Steve Hicks' original library.</p>
<p>{% codetitle "foxychimp.cfm" %}</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">html</div><code><span class="line"><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">fcApiKey</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">"Insert_your_FoxyCart_API_key_here"</span><span style="color: #808080"> /></span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955"><!---</span></span>
<span class="line"><span style="color: #6A9955"> Decode the URL-encoded response, using the ISO-8859-1</span></span>
<span class="line"><span style="color: #6A9955"> (Latin-1) encoding</span></span>
<span class="line"><span style="color: #6A9955">---></span></span>
<span class="line"><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">encryptedFeed</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">urlDecode(form[</span><span style="color: #F44747">"FoxyData"],</span><span style="color: #D4D4D4"> </span><span style="color: #F44747">"ISO-8859-1")</span><span style="color: #808080"> /></span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955"><!---</span></span>
<span class="line"><span style="color: #6A9955"> Initialise the RC4 component and call its decrypt method.</span></span>
<span class="line"><span style="color: #6A9955"> Note the 3rd argument, specifying that the data we are</span></span>
<span class="line"><span style="color: #6A9955"> passing in is not in Hex format</span></span>
<span class="line"><span style="color: #6A9955">---></span></span>
<span class="line"><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">RC4</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">createObject(</span><span style="color: #F44747">"component","RC4")</span><span style="color: #808080"> /></span></span>
<span class="line"><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">feedDataXml</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">RC4.rc4decrypt(src</span><span style="color: #F44747">=</span><span style="color: #CE9178">encryptedFeed,</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">key</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">fcApiKey,</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">inputHex</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">false)</span><span style="color: #808080"> /></span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955"><!---</span></span>
<span class="line"><span style="color: #6A9955"> Now you have an XML string which you can parse</span></span>
<span class="line"><span style="color: #6A9955">---></span></span>
<span class="line"><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">feedData</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">xmlParse(feedDataXml)</span><span style="color: #808080"> /></span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955"><!---</span></span>
<span class="line"><span style="color: #6A9955"> ...and do what you like with the feed data</span></span>
<span class="line"><span style="color: #6A9955">---></span></span>
<span class="line"><span style="color: #808080"><</span><span style="color: #F44747">cfloop</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">array</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"#feedData.foxyData.transactions.transaction#"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"transaction"</span><span style="color: #808080">></span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #6A9955"><!---</span></span>
<span class="line"><span style="color: #6A9955"> Do whatever you like with each transaction data in here</span></span>
<span class="line"><span style="color: #6A9955"> ---></span></span>
<span class="line"></span>
<span class="line"><span style="color: #808080"></</span><span style="color: #F44747">cfloop</span><span style="color: #808080">></span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955"><!---</span></span>
<span class="line"><span style="color: #6A9955"> FoxyCart expects a simple text response of "foxy"</span></span>
<span class="line"><span style="color: #6A9955"> to signify that everything has worked.</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955"> You can put error handling in which can return any other string,</span></span>
<span class="line"><span style="color: #6A9955"> which will then show up in your FoxyCart error logs.</span></span>
<span class="line"><span style="color: #6A9955">---></span></span>
<span class="line"><span style="color: #808080"><</span><span style="color: #F44747">cfcontent</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">reset</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"true"</span><span style="color: #808080"> /></span></span>
<span class="line"><span style="color: #D4D4D4">foxy</span></span>
<span class="line"></span></code></pre>
<p>{% codetitle "RC4.cfc" %}</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">html</div><code><span class="line"><span style="color: #6A9955"><!---</span></span>
<span class="line"><span style="color: #6A9955">ColdFusion RC4 Component</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">Written by Steve Hicks (steve@aquafusionmedia.com)</span></span>
<span class="line"><span style="color: #6A9955">http://www.aquafusionmedia.com</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">Version 1.0 - Released: April 24, 2012</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">Version 1.1 - Modified by Seb Duggan (seb@sebduggan.com)</span></span>
<span class="line"><span style="color: #6A9955"> Added arguments to allow input and output not to be Hex values</span></span>
<span class="line"><span style="color: #6A9955"> Released: May 3, 2016</span></span>
<span class="line"><span style="color: #6A9955">---></span></span>
<span class="line"></span>
<span class="line"><span style="color: #808080"><</span><span style="color: #F44747">cfcomponent</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">output</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"false"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #6A9955"><!--- Encrypt a String (src) using the Key (key) ---></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cffunction</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"RC4encrypt"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">access</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"public"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">output</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"false"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">returntype</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"string"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfargument</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"src"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">required</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"yes"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfargument</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"key"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">required</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"yes"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfargument</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"outputHex"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">type</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"boolean"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">default</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"true"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">mtxt</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">strToChars(arguments.src)</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">mkey</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">strToChars(arguments.key)</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">result</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">RC4calculate(mtxt,mkey)</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfif</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">arguments.outputHex</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfreturn</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">charsToHex(result)</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfelse</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfreturn</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">charsToStr(result)</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"></</span><span style="color: #F44747">cfif</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"></</span><span style="color: #F44747">cffunction</span><span style="color: #808080">></span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #6A9955"><!--- Decrypt a String (src) using the Key (key) ---></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cffunction</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"RC4decrypt"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">access</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"public"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">output</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"false"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">returntype</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"string"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfargument</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"src"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">required</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"yes"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfargument</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"key"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">required</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"yes"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfargument</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"inputHex"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">type</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"boolean"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">default</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"true"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfif</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">arguments.inputHex</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">mtxt</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">hexToChars(arguments.src)</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfelse</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">mtxt</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">strToChars(arguments.src)</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"></</span><span style="color: #F44747">cfif</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">mkey</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">strToChars(arguments.key)</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">result</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">RC4calculate(mtxt,mkey)</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfreturn</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">charsToStr(result)</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"></</span><span style="color: #F44747">cffunction</span><span style="color: #808080">></span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #6A9955"><!--- Set Up the Component Ready for Encryption ---></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cffunction</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"RC4initialize"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">access</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"public"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">output</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"false"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">returntype</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"any"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfargument</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"pwd"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">required</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"yes"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">sbox</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">arraynew(1)</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">mykey</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">arraynew(1)</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">b</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">0</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">intLength</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">arraylen(arguments.pwd)</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfloop</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">from</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"0"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">to</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"255"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"a"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">mykey[a</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">+</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">1]</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">arguments.pwd[(a</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">mod</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">intLength)</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">+</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">1]</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">sbox[a</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">+</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">1]</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">a</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"></</span><span style="color: #F44747">cfloop</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfloop</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">from</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"0"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">to</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"255"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"a"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">b</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">(</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">b</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">+</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">sbox[a</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">+</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">1]</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">+</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">mykey[a+1]</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">)</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">mod</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">256</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">tempswap</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">sbox[a</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">+</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">1]</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">sbox[a</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">+</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">1]</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">sbox[b</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">+</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">1]</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">sbox[b</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">+</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">1]</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">tempswap</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"></</span><span style="color: #F44747">cfloop</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfreturn</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">sbox</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"></</span><span style="color: #F44747">cffunction</span><span style="color: #808080">></span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #6A9955"><!--- Calculate the Cipher ---></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cffunction</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"RC4calculate"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">access</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"public"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">output</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"false"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">returntype</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"any"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfargument</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"plaintext"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">required</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"yes"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfargument</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"psw"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">required</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"yes"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">sbox</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">RC4initialize(arguments.psw)</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">i</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">0</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">j</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">0</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">cipher</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">arraynew(1)</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfloop</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">from</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"1"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">to</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"#arraylen(plaintext)#"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"a"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">i</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">(i</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">+</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">1)</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">mod</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">256</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">j</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">(j</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">+</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">sbox[i</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">+</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">1])</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">mod</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">256</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">temp</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">sbox[i</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">+</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">1]</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">sbox[i</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">+</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">1]</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">sbox[j</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">+</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">1]</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">sbox[j</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">+</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">1]</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">temp</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">k</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">sbox[((sbox[i</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">+</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">1]</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">+</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">sbox[j</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">+</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">1])</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">mod</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">256)</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">+</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">1]</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">cipherby</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">BitXor(arguments.plaintext[a],k)</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">arrayappend(cipher,cipherby)</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"></</span><span style="color: #F44747">cfloop</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfreturn</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">cipher</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"></</span><span style="color: #F44747">cffunction</span><span style="color: #808080">></span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #6A9955"><!--- Convert an Array of Chars into a Hex String ---></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cffunction</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"charsToHex"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">access</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"public"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">output</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"false"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">returntype</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"string"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfargument</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"chars"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">type</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"array"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">required</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"yes"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">result</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">''</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfloop</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">from</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"1"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">to</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"#arraylen(arguments.chars)#"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"i"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">fbn</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">formatBaseN(chars[i],16)</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfif</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">len(fbn)</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">eq</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">1</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">fbn</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">'0#fbn#'</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"></</span><span style="color: #F44747">cfif</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">result</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">result</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">&</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">fbn</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"></</span><span style="color: #F44747">cfloop</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfreturn</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">result</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"></</span><span style="color: #F44747">cffunction</span><span style="color: #808080">></span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #6A9955"><!--- Convert a Hex String into an Array of Characters ---></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cffunction</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"hexToChars"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">access</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"public"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">output</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"false"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">returntype</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"array"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfargument</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"hex"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">type</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"string"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">required</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"yes"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">chars</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">arraynew(1)</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfloop</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">from</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"1"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">to</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"#len(arguments.hex)#"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"i"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">step</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"2"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">arrayappend(chars,inputBaseN(mid(arguments.hex,i,2),16))</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"></</span><span style="color: #F44747">cfloop</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfreturn</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">chars</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"></</span><span style="color: #F44747">cffunction</span><span style="color: #808080">></span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #6A9955"><!--- Convert an Array of Characters into a String ---></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cffunction</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"charsToStr"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">access</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"public"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">output</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"false"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">returntype</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"string"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfargument</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"chars"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">type</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"array"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">required</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"yes"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">result</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">''</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfloop</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">from</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"1"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">to</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"#arraylen(arguments.chars)#"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"i"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">result</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">result</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">&</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">chr(chars[i])</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"></</span><span style="color: #F44747">cfloop</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfreturn</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">result</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"></</span><span style="color: #F44747">cffunction</span><span style="color: #808080">></span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #6A9955"><!--- Convert a String into an Array of Characters ---></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cffunction</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"strToChars"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">access</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"public"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">output</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"false"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">returntype</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"array"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfargument</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"str"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">type</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"string"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">required</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"yes"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">codes</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">arraynew(1)</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfloop</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">from</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"1"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">to</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"#len(arguments.str)#"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"i"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfset</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">codes[i]</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">asc(mid(arguments.str,i,1))</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"></</span><span style="color: #F44747">cfloop</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #F44747">cfreturn</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">codes</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"></</span><span style="color: #F44747">cffunction</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #808080"></</span><span style="color: #F44747">cfcomponent</span><span style="color: #808080">></span></span>
<span class="line"></span></code></pre>
SES URLs in Mura FW/1 plugins2016-03-21T12:00:00Zhttps://sebduggan.uk/posts/ses-urls-in-mura-fw1-plugins/<p>The <a href="https://github.com/stevewithington/MuraFW1">FW/1 plugin architecture</a> for Mura CMS is a very powerful way of embedding complex applications and business logic within your Mura-based website.</p>
<p>However, this comes at the cost of your SES URLs. While Mura itself generates human- and search engine-friendly URLs, your embedded FW/1 app will only have this for the top level of any display object. As you drill down into the app, you will start getting URLs that look like this:</p>
<p><code>http://example.com/statistics/?MyPluginaction=statistics:main.player&amp;playername=seb-duggan</code></p>
<p>In this example, the FW/1 app is placed in Mura on the page <code>/statistics/</code> as a plugin display object. In an ideal world (and how it would probably be if it were a standalone FW/1 site) your URL would be more like:</p>
<p><code>http://example.com/statistics/player/seb-duggan/</code></p>
<p>So, how can we achieve this?</p>
<p>One way would be by setting up a bunch of <code>.htaccess</code> rules, and this would be a perfectly workable solution. But what happens when you decide to rename <code>statistics</code> to <code>stats</code>? Wouldn't it be nice if the URLs were completely controlled by the plugin with no other dependencies, and if the plugin were smart enough to know how to route the URLs wherever it has been placed within the Mura site structure?</p>
<p>After a whole lot of trial-and-error, and whole lot more refactoring, I believe I've come up with a pretty robust (and reasonably elegant) solution...</p>
<h3 id="the-starting-point" tabindex="-1">The starting point <a class="header-anchor" href="https://sebduggan.uk/posts/ses-urls-in-mura-fw1-plugins/">#</a></h3>
<p>All my examples are loosely based on a site I've just built for my local cricket club (<a href="http://www.hambledoncricketclub.co.uk">go and take a look</a> if you like -- and see if you can spot where the FW/1 app integrates!).</p>
<p>The normal way to set up an FW/1 plugin would be something like this:</p>
<p>{% codetitle "config.xml.cfm" %}</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">xml</div><code><span class="line"><span style="color: #808080"><</span><span style="color: #569CD6">displayobjects</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">location</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"global"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #569CD6">displayobject</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"Statistics"</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">component</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"includes.displayObjects"</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">displaymethod</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"dspStatistics"</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">persist</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"false"</span><span style="color: #D4D4D4"> </span><span style="color: #808080">/></span></span>
<span class="line"><span style="color: #808080"></</span><span style="color: #569CD6">displayobjects</span><span style="color: #808080">></span></span>
<span class="line"></span></code></pre>
<p>{% codetitle "displayObjects.cfc" %}</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">any</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">dspStatistics</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">getApplication</span><span style="color: #D4D4D4">().doAction(</span><span style="color: #CE9178">"statistics:main.default"</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span></code></pre>
<p>This sets up a display object called "Statistics" which can be included into your Mura page. When rendered, it runs the <code>dspStatistics()</code> method, which in turn renders the FW/1 page <code>main.default</code> in the <code>statistics</code> subsystem.</p>
<p>So far, so easy, and if you've created an FW/1 plugin before, it should all be very familiar.</p>
<h3 id="adding-the-ses" tabindex="-1">Adding the SES <a class="header-anchor" href="https://sebduggan.uk/posts/ses-urls-in-mura-fw1-plugins/">#</a></h3>
<p>The key to the new system is adding some route definitions to your displayObject configuration file:</p>
<p>{% codetitle "config.xml.cfm" %}</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">xml</div><code><span class="line"><span style="color: #808080"><</span><span style="color: #569CD6">displayobjects</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">location</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"global"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #569CD6">displayobject</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"Statistics"</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">component</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"includes.displayObjects"</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">displaymethod</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"dspStatistics"</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">persist</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"false"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #569CD6">route</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">pattern</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"batting"</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">action</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"statistics:main.batting"</span><span style="color: #D4D4D4"> </span><span style="color: #808080">/></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #569CD6">route</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">pattern</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"batting/{'[0-9]{4}'}"</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">action</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"statistics:main.batting"</span><span style="color: #D4D4D4"> </span><span style="color: #808080">/></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #569CD6">route</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">pattern</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"player"</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">action</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"statistics:main.playerredirect"</span><span style="color: #D4D4D4"> </span><span style="color: #808080">/></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #569CD6">route</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">pattern</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"player/{member.filename}"</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">action</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"statistics:main.player"</span><span style="color: #D4D4D4"> </span><span style="color: #808080">/></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #569CD6">route</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">pattern</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"player/{member.filename}/detail"</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">action</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"statistics:main.playerdetail"</span><span style="color: #D4D4D4"> </span><span style="color: #808080">/></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"></</span><span style="color: #569CD6">displayobject</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #808080"></</span><span style="color: #569CD6">displayobjects</span><span style="color: #808080">></span></span>
<span class="line"></span></code></pre>
<p>This doesn't affect the basic functionality of the definition, but it adds in more data that we can extract and use.</p>
<p>You'll see several types of route in the example above. The route's pattern is what gets matched against that part of the URL that comes after the plugin integration point. For example, if you have your plugin object included in the page <code>/statistics/</code>, the first route would match the URL <code>/statistics/batting/</code>.</p>
<p>Parts of the URL that can vary are contained in curly braces. If the content of the curly braces is itself wrapped in single or double quotes, as in the second route, then this is a regular expression. So the second route would match <code>/batting/2016/</code> (the regex matches a pattern of 4 digits).</p>
<p>If the pattern within the braces is not wrapped in quotes, then this is a value to validate against a bean. So, the fourth route would match against <code>/statistics/player/seb-duggan/</code> - and would additionally validate that there is a member bean with a filename value of seb-duggan.</p>
<p>Each route has a fully-qualified FW/1 action, the page which should be rendered if the route is matched.</p>
<p>Writing it out like this, it seems quite complicated, but it's really very simple!</p>
<h3 id="the-heavy-lifting" tabindex="-1">The heavy lifting <a class="header-anchor" href="https://sebduggan.uk/posts/ses-urls-in-mura-fw1-plugins/">#</a></h3>
<p>There are a bunch of methods which need to be added to your plugin's <code>eventHandler.cfc</code> to make all this work. I choose to keep them all in a separate file and include them into the eventHandler, but that's up to you...</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">getSesRoutes</span><span style="color: #D4D4D4">() {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">objectsLen</span><span style="color: #D4D4D4"> = </span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">displayObject</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">""</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">result</span><span style="color: #D4D4D4"> = {};</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">route</span><span style="color: #D4D4D4"> = {};</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">routes</span><span style="color: #D4D4D4"> = [];</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">pluginXML</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">variables</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">pluginConfig</span><span style="color: #D4D4D4">.getPluginManager().getPluginXML(</span><span style="color: #569CD6">variables</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">pluginConfig</span><span style="color: #D4D4D4">.getModuleId());</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> ( </span><span style="color: #DCDCAA">structKeyExists</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">pluginXML</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">plugin</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">displayobjects</span><span style="color: #D4D4D4">,</span><span style="color: #CE9178">"displayobject"</span><span style="color: #D4D4D4">) ) {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">objectsLen</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">arrayLen</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">pluginXML</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">plugin</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">displayobjects</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">displayobject</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">objectsLen</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #C586C0">for</span><span style="color: #D4D4D4"> (</span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">i</span><span style="color: #D4D4D4">=</span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">; </span><span style="color: #9CDCFE">i</span><span style="color: #D4D4D4"><=</span><span style="color: #9CDCFE">objectsLen</span><span style="color: #D4D4D4">; </span><span style="color: #9CDCFE">i</span><span style="color: #D4D4D4">++) {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">displayObject</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">pluginXML</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">plugin</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">displayobjects</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">displayobject</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">i</span><span style="color: #D4D4D4">];</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #DCDCAA">structKeyExists</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">displayObject</span><span style="color: #D4D4D4">,</span><span style="color: #CE9178">"route"</span><span style="color: #D4D4D4">)) {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">routes</span><span style="color: #D4D4D4"> = [];</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #C586C0">for</span><span style="color: #D4D4D4"> (</span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">r</span><span style="color: #D4D4D4">=</span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">; </span><span style="color: #9CDCFE">r</span><span style="color: #D4D4D4"><=</span><span style="color: #DCDCAA">arraylen</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">displayObject</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">route</span><span style="color: #D4D4D4">); </span><span style="color: #9CDCFE">r</span><span style="color: #D4D4D4">++) {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">route</span><span style="color: #D4D4D4"> = {};</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">route</span><span style="color: #D4D4D4">[</span><span style="color: #CE9178">"pattern"</span><span style="color: #D4D4D4">] = </span><span style="color: #9CDCFE">displayObject</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">route</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">r</span><span style="color: #D4D4D4">].</span><span style="color: #9CDCFE">xmlAttributes</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">pattern</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">route</span><span style="color: #D4D4D4">[</span><span style="color: #CE9178">"action"</span><span style="color: #D4D4D4">] = </span><span style="color: #9CDCFE">displayObject</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">route</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">r</span><span style="color: #D4D4D4">].</span><span style="color: #9CDCFE">xmlAttributes</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">action</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">arrayAppend</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">routes</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">route</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4"> }</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">result</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">displayObject</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">xmlAttributes</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">displaymethod</span><span style="color: #D4D4D4">] = </span><span style="color: #9CDCFE">routes</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4"> }</span></span>
<span class="line"><span style="color: #D4D4D4"> }</span></span>
<span class="line"><span style="color: #D4D4D4"> }</span></span>
<span class="line"><span style="color: #D4D4D4"> }</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">result</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">getSesUrlMatches</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">required</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">event</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">qs</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">getQueryService</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">sql</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">""</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">dbtype</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">application</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">configBean</span><span style="color: #D4D4D4">.getDBType();</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">lengthFunction</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">""</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">dbtype</span><span style="color: #D4D4D4"> == </span><span style="color: #CE9178">"mssql"</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">lengthFunction</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">"datalength"</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4"> } </span><span style="color: #C586C0">else</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #DCDCAA">listfindnocase</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">"mysql,oracle"</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">dbtype</span><span style="color: #D4D4D4">)) {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">lengthFunction</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">"length"</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4"> } </span><span style="color: #C586C0">else</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">dbtype</span><span style="color: #D4D4D4"> == </span><span style="color: #CE9178">"postgresql"</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">lengthFunction</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">"char_length"</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4"> } </span><span style="color: #C586C0">else</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">dbtype</span><span style="color: #D4D4D4"> == </span><span style="color: #CE9178">"nuodb"</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">lengthFunction</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">"character_length"</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">sql</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">"SELECT</span></span>
<span class="line"><span style="color: #CE9178"> tcontent.filename,</span></span>
<span class="line"><span style="color: #CE9178"> tplugindisplayobjects.displaymethod</span></span>
<span class="line"><span style="color: #CE9178"> FROM</span></span>
<span class="line"><span style="color: #CE9178"> tplugindisplayobjects</span></span>
<span class="line"><span style="color: #CE9178"> INNER JOIN</span></span>
<span class="line"><span style="color: #CE9178"> tcontentobjects</span></span>
<span class="line"><span style="color: #CE9178"> ON</span></span>
<span class="line"><span style="color: #CE9178"> tplugindisplayobjects.objectid = tcontentobjects.objectid</span></span>
<span class="line"><span style="color: #CE9178"> INNER JOIN</span></span>
<span class="line"><span style="color: #CE9178"> tcontent</span></span>
<span class="line"><span style="color: #CE9178"> ON</span></span>
<span class="line"><span style="color: #CE9178"> tcontentobjects.contenthistid = tcontent.contenthistid</span></span>
<span class="line"><span style="color: #CE9178"> AND</span></span>
<span class="line"><span style="color: #CE9178"> tcontent.active = 1</span></span>
<span class="line"><span style="color: #CE9178"> WHERE</span></span>
<span class="line"><span style="color: #CE9178"> tplugindisplayobjects.moduleid = :moduleid</span></span>
<span class="line"><span style="color: #CE9178"> AND</span></span>
<span class="line"><span style="color: #CE9178"> tplugindisplayobjects.displaymethod IN (:displaymethods)</span></span>
<span class="line"><span style="color: #CE9178"> AND</span></span>
<span class="line"><span style="color: #CE9178"> tcontent.siteid = :siteid</span></span>
<span class="line"><span style="color: #CE9178"> AND</span></span>
<span class="line"><span style="color: #CE9178"> left(:fullurl, #lengthfunction#(tcontent.filename)) = tcontent.filename</span></span>
<span class="line"><span style="color: #CE9178"> ORDER BY</span></span>
<span class="line"><span style="color: #CE9178"> #lengthfunction#(tcontent.filename) DESC"</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">qs</span><span style="color: #D4D4D4">.addParam(name=</span><span style="color: #CE9178">"moduleid"</span><span style="color: #D4D4D4">, value=</span><span style="color: #569CD6">variables</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">pluginConfig</span><span style="color: #D4D4D4">.getModuleId(), cfsqltype=</span><span style="color: #CE9178">"cf_sql_varchar"</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">qs</span><span style="color: #D4D4D4">.addParam(name=</span><span style="color: #CE9178">"siteid"</span><span style="color: #D4D4D4">, value=</span><span style="color: #569CD6">arguments</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">event</span><span style="color: #D4D4D4">.getValue(</span><span style="color: #CE9178">"siteid"</span><span style="color: #D4D4D4">), cfsqltype=</span><span style="color: #CE9178">"cf_sql_varchar"</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">qs</span><span style="color: #D4D4D4">.addParam(name=</span><span style="color: #CE9178">"displaymethods"</span><span style="color: #D4D4D4">, value=</span><span style="color: #DCDCAA">structkeylist</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">variables</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">pluginConfig</span><span style="color: #D4D4D4">.getApplication().getValue(</span><span style="color: #CE9178">"sesRoutes"</span><span style="color: #D4D4D4">)), list=</span><span style="color: #569CD6">true</span><span style="color: #D4D4D4">, cfsqltype=</span><span style="color: #CE9178">"cf_sql_varchar"</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">qs</span><span style="color: #D4D4D4">.addParam(name=</span><span style="color: #CE9178">"fullurl"</span><span style="color: #D4D4D4">, value=</span><span style="color: #569CD6">arguments</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">event</span><span style="color: #D4D4D4">.getValue(</span><span style="color: #CE9178">"currentfilenameadjusted"</span><span style="color: #D4D4D4">), cfsqltype=</span><span style="color: #CE9178">"cf_sql_varchar"</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">qs</span><span style="color: #D4D4D4">.execute(sql=</span><span style="color: #9CDCFE">sql</span><span style="color: #D4D4D4">).getResult();</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">validateSesUrlParams</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">required</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">required</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">event</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">required</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">displayMethod</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">required</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">sesUrlParts</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">routes</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">variables</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">pluginConfig</span><span style="color: #D4D4D4">.getApplication().getValue(</span><span style="color: #CE9178">"sesRoutes"</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">sesUrlParams</span><span style="color: #D4D4D4"> = [];</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (not </span><span style="color: #DCDCAA">structkeyexists</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">routes</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">arguments</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">displayMethod</span><span style="color: #D4D4D4">)) {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">false</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4"> }</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #C586C0">for</span><span style="color: #D4D4D4"> (</span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">route</span><span style="color: #D4D4D4"> in </span><span style="color: #9CDCFE">routes</span><span style="color: #D4D4D4">[</span><span style="color: #569CD6">arguments</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">displayMethod</span><span style="color: #D4D4D4">]) {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">routeParts</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">listToArray</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">route</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">pattern</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"/"</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #DCDCAA">arrayLen</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">routeParts</span><span style="color: #D4D4D4">) != </span><span style="color: #DCDCAA">arrayLen</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">arguments</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">sesUrlParts</span><span style="color: #D4D4D4">)) {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #C586C0">continue</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">match</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">true</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #C586C0">for</span><span style="color: #D4D4D4"> (</span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">i</span><span style="color: #D4D4D4">=</span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">; </span><span style="color: #9CDCFE">i</span><span style="color: #D4D4D4"><=</span><span style="color: #DCDCAA">arrayLen</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">routeParts</span><span style="color: #D4D4D4">); </span><span style="color: #9CDCFE">i</span><span style="color: #D4D4D4">++) {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #DCDCAA">reFind</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">"^{[</span><span style="color: #D7BA7D">""</span><span style="color: #CE9178">''].+[</span><span style="color: #D7BA7D">""</span><span style="color: #CE9178">'']}$"</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">routeParts</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">i</span><span style="color: #D4D4D4">])) {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">match</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">validateSesUrlPartRegex</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">routeParts</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">i</span><span style="color: #D4D4D4">], </span><span style="color: #569CD6">arguments</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">sesUrlParts</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">i</span><span style="color: #D4D4D4">]);</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">arrayAppend</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">sesUrlParams</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">arguments</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">sesUrlParts</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">i</span><span style="color: #D4D4D4">]);</span></span>
<span class="line"><span style="color: #D4D4D4"> } </span><span style="color: #C586C0">else</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #DCDCAA">reFind</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">"^{.+}$"</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">routeParts</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">i</span><span style="color: #D4D4D4">])) {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">match</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">validateSesUrlPartBean</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">routeParts</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">i</span><span style="color: #D4D4D4">], </span><span style="color: #569CD6">arguments</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">sesUrlParts</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">i</span><span style="color: #D4D4D4">]);</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">arrayAppend</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">sesUrlParams</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">arguments</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">sesUrlParts</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">i</span><span style="color: #D4D4D4">]);</span></span>
<span class="line"><span style="color: #D4D4D4"> } </span><span style="color: #C586C0">else</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">routeParts</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">i</span><span style="color: #D4D4D4">] != </span><span style="color: #569CD6">arguments</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">sesUrlParts</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">i</span><span style="color: #D4D4D4">]) {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">match</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">false</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #C586C0">break</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4"> }</span></span>
<span class="line"><span style="color: #D4D4D4"> }</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (!</span><span style="color: #9CDCFE">match</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #C586C0">continue</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">arguments</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">event</span><span style="color: #D4D4D4">.setValue(</span><span style="color: #CE9178">"sesUrlParams"</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">sesUrlParams</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">arguments</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">event</span><span style="color: #D4D4D4">.setValue(</span><span style="color: #CE9178">"sesAction"</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">route</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">action</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">true</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4"> }</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">false</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">validateSesUrlPartRegex</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">required</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">required</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">pattern</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">required</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">urlPart</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">arguments</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">pattern</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">reReplace</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">arguments</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">pattern</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"^{[</span><span style="color: #D7BA7D">""</span><span style="color: #CE9178">''](.+)[</span><span style="color: #D7BA7D">""</span><span style="color: #CE9178">'']}$"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"\1"</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">refindNoCase</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">"^"</span><span style="color: #D4D4D4"> & </span><span style="color: #569CD6">arguments</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">pattern</span><span style="color: #D4D4D4"> & </span><span style="color: #CE9178">"$"</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">arguments</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">urlPart</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">validateSesUrlPartBean</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">required</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">required</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">pattern</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">required</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">urlPart</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">arguments</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">pattern</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">reReplace</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">arguments</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">pattern</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"^{(.+)}$"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"\1"</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">entity</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">listFirst</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">arguments</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">pattern</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"."</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">column</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">listLast</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">arguments</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">pattern</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"."</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">checkBean</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">arguments</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">$</span><span style="color: #D4D4D4">.getBean(</span><span style="color: #9CDCFE">entity</span><span style="color: #D4D4D4">).loadBy(</span><span style="color: #CE9178">"#column#"</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">arguments</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">urlPart</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> !</span><span style="color: #9CDCFE">checkBean</span><span style="color: #D4D4D4">.getIsNew();</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span></code></pre>
<p>The first method, <code>getSesRoutes()</code>, should be called from the eventHandler's <code>onApplicationLoad()</code> method:</p>
<p><code>variables.pluginConfig.getApplication().setValue( "sesRoutes", getSesRoutes() );</code></p>
<p>This examines the displayObject config file and extracts all the routes for each displayObject, and we cache that in the plugin's application scope.</p>
<p>We then need to handle the SES URLs by setting up a 404 handler in the eventHandler. By default, Mura will not recognise the URLs we're trying to catch, and so <code>onSite404()</code> will be invoked:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">any</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">onSite404</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">required</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">required</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">event</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">qMatches</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">getSesUrlMatches</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">arguments</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">event</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">qMatches</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">recordcount</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">sesUrlParts</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">listtoarray</span><span style="color: #D4D4D4">(</span><span style="color: #DCDCAA">replace</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">event</span><span style="color: #D4D4D4">.getValue(</span><span style="color: #CE9178">"currentfilenameadjusted"</span><span style="color: #D4D4D4">), </span><span style="color: #9CDCFE">qMatches</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">filename</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">""</span><span style="color: #D4D4D4">), </span><span style="color: #CE9178">"/"</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">validateSesUrlParams</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">event</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">qMatches</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">displaymethod</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">sesUrlParts</span><span style="color: #D4D4D4">)) {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">local</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">contentBean</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">application</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">contentManager</span><span style="color: #D4D4D4">.getActiveContentByFilename(</span><span style="color: #9CDCFE">qMatches</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">filename</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">event</span><span style="color: #D4D4D4">.getValue(</span><span style="color: #CE9178">'siteid'</span><span style="color: #D4D4D4">));</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">event</span><span style="color: #D4D4D4">.setValue(</span><span style="color: #CE9178">'contentBean'</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">local</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">contentBean</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4"> }</span></span>
<span class="line"><span style="color: #D4D4D4"> }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span></code></pre>
<p>This is where all the clever stuff happens. First, <code>getSesUrlMatches()</code> looks to see if some part at the beginning of the URL matches any of the Mura pages that include one of the displayObjects with routes defined on them.</p>
<p>If a match is found then we pass the remainder of the URL through and try to match it against the defined routes. We check each section of the URL in turn; if the URL part is a <code>{variable}</code>, then it's either matched as a regular expression, or the value is checked against the specified bean. Each variable value is appended to an array for use later by the controller methods.</p>
<p>If a route is successfully matched, then the array of variables (<code>sesUrlParams</code>) and the FW/1 action defined on the route (<code>sesAction</code>) are added to the Mura event object for use later by the FW/1 app; and finally, we load in the content for the Mura page we identified earlier and add that to the event object too.</p>
<p>If no match is found, then we simply fall through to the main Mura 404 page, so you don't need to maintain a separate 404 page in your FW/1 app. And becasue of the way we validate the value of the bean value variable, we already know before going into the FW/1 app if the value passed is a valid one.</p>
<p>The last thing that needs to be modified is the displayObjects method:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">any</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">dspStatistics</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">action</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">$</span><span style="color: #D4D4D4">.getEvent().getValue(</span><span style="color: #CE9178">"sesAction"</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">"statistics:main.default"</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">getApplication</span><span style="color: #D4D4D4">().doAction(</span><span style="color: #9CDCFE">action</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span></code></pre>
<p>Here, we are getting the controller action from the event object which will have been set if a route has matched; and if that doesn't exist, we default to the original action we had at the start.</p>
<p>Finally, you need to configure your FW/1 controller methods to accept the values we've extracted from the URLs. Simply add the following to any method that is expecting one or more variables from an SES URL:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">urlParams</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">rc</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">$</span><span style="color: #D4D4D4">.getEvent().getValue( </span><span style="color: #CE9178">"sesUrlParams"</span><span style="color: #D4D4D4">, [] );</span></span>
<span class="line"></span></code></pre>
<p>...and you have your array of each of the values matched in the URL. So, in my example, if I requested the page:</p>
<p><code>http://example.com/statistics/player/seb-duggan/detail/</code></p>
<p>...I would get the content rendered by the controller action <code>statistics:main.playerdetail</code>, and urlParams would contain one value, <code>seb-duggan</code>.</p>
<h3 id="to-wrap-up" tabindex="-1">To wrap up <a class="header-anchor" href="https://sebduggan.uk/posts/ses-urls-in-mura-fw1-plugins/">#</a></h3>
<p>It may seem complicated, but most of it is code that you can add and forget about. The configuration is contained within <code>config.xml.cfm</code> and <code>displayObjects.cfc</code>, just as it was previously, and your FW/1 controller methods will get any variables they need from the event object.</p>
<h3 id="caveats" tabindex="-1">Caveats <a class="header-anchor" href="https://sebduggan.uk/posts/ses-urls-in-mura-fw1-plugins/">#</a></h3>
<ol>
<li>I've written and tested this on Lucee. The only reason it wouldn't work on Adobe CF is if I've used some Lucee-specific syntax. But I don't think I have.</li>
<li>I really don't know what would happen if you had two plugins on the same Mura page, both of which were expecting to match an SES route. If for some reason you have this requirement, please let me know how you get on!</li>
<li>My plugin was written using Mura ORM, and so my validation works against Mura ORM beans. This could be easily modified to check against a table and column, or you could just use regular expressions and do all the data validation within the FW/1 app - but then you would lose the ability to fall through to Mura's 404 page.</li>
</ol>
RTFM carefully when applying CF hotfixes!2012-03-14T12:00:00Zhttps://sebduggan.uk/posts/rtfm-carefully-when-applying-cf-hotfixes/<p>Here's a quick tip for anyone who may be seeing some odd behaviour after applying a ColdFusion hotfix (as well as a reminder to myself to be more careful next time...).</p>
<p>I've just applied the <a href="http://helpx.adobe.com/coldfusion/kb/coldfusion-security-hotfix.html">latest security hotfix</a> to my web server, and everything seemed to have worked fine - except that I suddenly started receiving a bunch of ColdFusion error notifications from a couple of my websites.</p>
<p>The error was being reported as <code>coldfusion.runtime.Cast._double(J)D</code>, and a little Googling directed me to <a href="https://groups.google.com/forum/?fromgroups#!topic/mxunit/wMfp3aYsFLs">this thread on the MXUnit mailing list</a>, with a solution right at the end. As it's not exclusively an MXUnit issue (it seemed to come about when getting the difference between two <code>getTickCount()</code> values) I thought I'd share the solution here.</p>
<p>What it boils down to is this. When the installation instructions say:</p>
<blockquote>
<p>If hf901-00001.jar, hf901-00002.jar or hf901-00003.jar exist, delete them.</p>
</blockquote>
<p>...make sure you look at the filenames very carefully, and <strong>do not delete chf9010002.jar</strong> - which is the updater for Cumulative HotFix 2.</p>
<p>Once I'd restored the file I'd deleted in error, everything worked again as it should. Roll on ColdFusion 10 with its automated update process...</p>
Deploy your website changes using Git2012-03-13T12:00:00Zhttps://sebduggan.uk/posts/deploy-your-website-changes-using-git/<p>This is something I've been meaning to look at ever since a conversation I had at Scotch on the Rocks 2011. Many of us -- even if we use the latest and greatest in source control -- still use FTP to deploy our site changes to production. This causes several headaches, among them remembering which files have changed, and getting all those files deployed simultaneously.</p>
<p>After looking around the web, I have found several resources that helped me set up Git to deploy my site.</p>
<h3 id="git-branching-model" tabindex="-1">Git branching model <a class="header-anchor" href="https://sebduggan.uk/posts/deploy-your-website-changes-using-git/">#</a></h3>
<p>The first thing to read, if you've not already done so, is <a href="http://nvie.com/posts/a-successful-git-branching-model/">A successful Git branching model</a>. You can make this as complex as you like, but at the very least you should have a <strong>master</strong> branch and a <strong>develop</strong> branch. All your development work is done in the develop branch (or others); when you've finished developing a feature and are ready to release it, merge it into the master branch.</p>
<p><strong>The master branch should always contain the production code</strong> -- so, when we merge our code into the master branch, we then want a way to automate pushing this directly to our production server.</p>
<h3 id="set-up-ssh-access-and-git-on-your-server" tabindex="-1">Set up SSH access and Git on your server <a class="header-anchor" href="https://sebduggan.uk/posts/deploy-your-website-changes-using-git/">#</a></h3>
<p>The next thing to do is set up your server to allow SSH access (if it doesn't have it already) and install Git.</p>
<p>I'm running a Windows 2008 server, so decided to use Cygwin to simplify installation of both. I found excellent instructions on <a href="http://www.shannoncornish.com/blog/2009/04/git-server-windows-2008/">setting up a Git server on Windows 2008</a> -- follow all the instructions apart from installing Python, which isn't needed for our purposes.</p>
<h3 id="set-up-remote-git-repository" tabindex="-1">Set up remote Git repository <a class="header-anchor" href="https://sebduggan.uk/posts/deploy-your-website-changes-using-git/">#</a></h3>
<p>Now that you've got Git and SSH installed, here comes the fun stuff.</p>
<p>First, we need to set up a bare Git repository on the server. This should be located somewhere completely separate from your web root. Personally, I put the Git repositories in my <code>cygwin/home/git</code> directory (the home directory for my "git" user).</p>
<p>So, having logged in via SSH as "git":</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">$ mkdir mywebsite.git</span></span>
<span class="line"><span style="color: #D4D4D4">$ cd mywebsite.git</span></span>
<span class="line"><span style="color: #D4D4D4">$ git init --bare</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span></code></pre>
<p>This creates a "bare" Git repo, which means that it contains all the Git commit data, but no checked-out HEAD – essentially, it's just the contents of the .git directory in a normal git repo.</p>
<p>Now comes the real magic. We're going to create a Git "hook", so that when we push the latest commits to this repository, it automatically checks it out to your webroot, with no user intervention necessary!</p>
<p>Create a file <code>post-receive</code> in the <code>hooks</code> directory of your repository:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">bash</div><code><span class="line"><span style="color: #6A9955">#!/bin/sh</span></span>
<span class="line"><span style="color: #9CDCFE">GIT_WORK_TREE</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">/path/to/webroot/of/mywebsite</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">git</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">checkout</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">-f</span></span>
<span class="line"></span></code></pre>
<p>Make sure the target directory exists, as Git won't create it for you. Finally, set permissions on the file:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">$ chmod +x hooks/post-receive</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span></code></pre>
<p>And that's your server setup completed! (Thanks to <a href="http://toroid.org/ams/git-website-howto">this site</a> for the help...)</p>
<h3 id="push-your-website" tabindex="-1">Push your website <a class="header-anchor" href="https://sebduggan.uk/posts/deploy-your-website-changes-using-git/">#</a></h3>
<p>Back on your local machine, set up a remote Git repository – I call mine "production" – which points to the repo on your server:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">$ git remote add production git@myserver.com:mywebsite.git</span></span>
<span class="line"><span style="color: #D4D4D4">$ git push production +master:refs/heads/master</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span></code></pre>
<p>...and hey presto! Your Git repo has been pushed to the production server – and when it finished the site was automatically checked out into your webroot!</p>
<p>Finally, to update the site in future, simply type:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">$ git push production</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span></code></pre>
<p>(Admittedly, I do all my Git committing and pushing through the excellent <a href="http://www.git-tower.com/">Tower</a> -- which makes it even easier!)</p>
<h3 id="a-bit-of-help-needed-please" tabindex="-1">A bit of help needed, please... <a class="header-anchor" href="https://sebduggan.uk/posts/deploy-your-website-changes-using-git/">#</a></h3>
<p>I would like a little advice from someone who knows their Git, if possible.</p>
<p>I have files which I don't want copied to the webserver. Some of these - for instance, user-submitted files -- I will add to <code>.gitignore</code>, because I don't want them subject to source control at all; those are the easy ones.</p>
<p>But there are others -- for instance, site config files -- which I <em>do</em> want to be under source control, but the version on my live server will be different from that on my dev server, and I don't want them to be overwritten by the automated update procedure.</p>
<p>Does anyone know of a simple way to achieve this?</p>
Server crash, possibly due to hacking attempt2011-04-22T12:00:00Zhttps://sebduggan.uk/posts/server-crash-possibly-due-to-hacking-attempt/<p>This morning, I couldn't reach my web server. Dead. Nothing.</p>
<p>I contacted my server company, who very quickly diagnosed that the server had blue-screened; they rebooted it and all is now well. Looking at my web logs, I think it was down for about an hour -- not disastrous, but definitely inconvenient and not A Good Thing. <em>(Note to self: set up a monitoring service so I know when it's down!)</em></p>
<p>So I started looking at the system logs to see if I could find what caused it to crash. And there was a big clue, right in front of me.</p>
<p>Yesterday, I installed SSH on the server (see my previous post on <a href="https://sebduggan.uk/posts/deploy-your-website-changes-using-git/">deploying via Git</a>). And, 40 minutes later, I started getting login attempts (from Germany, Poland, Thailand, etc.) -- all trying to login to SSH as "root". Over the course of 15 hours, I logged over 4200 failed SSH login attempts.</p>
<p>The attempts stopped several hours before the server crashed, but I can only assume the events are related. I think I've go it more locked down now...</p>
<h3 id="what-i-ve-learned" tabindex="-1">What I've learned... <a class="header-anchor" href="https://sebduggan.uk/posts/server-crash-possibly-due-to-hacking-attempt/">#</a></h3>
<p>Of course, these tips apply to most areas of security, but they do bear repeating!</p>
<h4 id="1-don-t-use-obvious-login-names" tabindex="-1">1. Don't use obvious login names <a class="header-anchor" href="https://sebduggan.uk/posts/server-crash-possibly-due-to-hacking-attempt/">#</a></h4>
<p>You log in with a username and password. If you have a default username, such as "root", the hacker already knows one half of the the combination. (I didn't have "root" as a username, so all their attempts failed at the first hurdle.)</p>
<h4 id="2-use-non-trivial-passwords" tabindex="-1">2. Use non-trivial passwords <a class="header-anchor" href="https://sebduggan.uk/posts/server-crash-possibly-due-to-hacking-attempt/">#</a></h4>
<p>If you have a simple password, it's that much easier to brute-force a login. If you don't already, get yourself a password manager (I use <a href="http://agilewebsolutions.com/onepassword">1Password</a>) to generate and store unique, complex passwords. If you have a password made up of 16 random characters, it's going to take a while to crack it.</p>
<blockquote>
<p><strong>Update:</strong> better still, as recommended in the comments, use key files to authenticate -- and turn off pasword authentication completely. Not only is this more secure, it means you can log in without having to remember a password...</p>
</blockquote>
<h4 id="3-don-t-run-it-on-the-standard-port" tabindex="-1">3. Don't run it on the standard port <a class="header-anchor" href="https://sebduggan.uk/posts/server-crash-possibly-due-to-hacking-attempt/">#</a></h4>
<p>Hackers will try to access SSH on its standard port (22). Make it that much more obscure by using a custom port -- for instance 2587 (you can use anything that doesn't clash with other services). This is especially important if you are unable to implement tip 4 below.</p>
<h4 id="4-restrict-access-to-your-ssh-port" tabindex="-1">4. Restrict access to your SSH port <a class="header-anchor" href="https://sebduggan.uk/posts/server-crash-possibly-due-to-hacking-attempt/">#</a></h4>
<p>Just because you've followed steps 1, 2 and 3, your server is still open to attacks. Hammering away at an SSH login can still affect the running and stability of your server. So, if it's practical, use your firewall to allow access to the SSH port only to specified IP addresses. If they can't get a response on a port, hackers will soon move on to somewhere they can.</p>
<p>I hope these tips are useful -- and please let me know what I've missed or got wrong (I'm by no means a security expert!).</p>
Using cfthread to speed up your web service calls2011-03-25T12:00:00Zhttps://sebduggan.uk/posts/using-cfthread-to-speed-up-your-web-service-calls/<p>I've recently taken to using the excellent <a href="http://postmarkapp.com/">Postmark</a> to send out all the transactional emails from my web sites. For just 0.15 cents per email, you get the benefits of increased deliverability and a great API to track and manage any undelivered mail.</p>
<p>If you like, you can send your emails simply by using Postmark's SMTP servers -- a really quick way to migrate to their system. But the real power comes when you start using their API. I'm not going to go into detail about how to set this up here, but in short you just have to send your message as a JSON object via HTTP Post.</p>
<p>And this is where threads come in. There are some web services you will use -- for instance, payment gateways -- where the returned result is critical to the continuation of the page request. But others -- as well as Postmark, I also send API requests to <a href="http://www.mailchimp.com/">MailChimp</a> for mailing list subscriptions -- where you really don't need to know the outcome.</p>
<p>Simply set up a new thread using <code><cfthread></code> (you're just firing and forgetting -- you don't need to worry about any joining when the thread completes), and perform your <code><cfhttp></code> magic in there. ColdFusion will get on with doing that in the background -- but in the meantime your user has already received their page without waiting for a webservice call which you never quite know how long will take to peform.</p>
<p>Of course, you can (and should) include error handling logic in your <code><cfthread></code> -- so you've got some idea of what the problem was if, for some reason, the webservice call was unsuccessful. But in most cases, this will be of concern to you and not to your user.</p>
<p>Here is an example of how I've implemented it for Postmark:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfml</div><code><span class="line"><span style="color: #6A9955"><!--- ...page processing, generate the message to be sent... ---></span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955"><!--- Fire off the webservice call in a thread ---></span></span>
<span class="line"><span style="color: #808080"><</span><span style="color: #569CD6">cfthread</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">action</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"run"</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"postmark_</span><span style="color: #569CD6">#</span><span style="color: #DCDCAA">createUUID</span><span style="color: #CE9178">()</span><span style="color: #569CD6">#</span><span style="color: #CE9178">"</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">json</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"</span><span style="color: #569CD6">#</span><span style="color: #9CDCFE">email</span><span style="color: #D4D4D4">.</span><span style="color: #CE9178">getJson()</span><span style="color: #569CD6">#</span><span style="color: #CE9178">"</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">subject</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"</span><span style="color: #569CD6">#</span><span style="color: #9CDCFE">email</span><span style="color: #D4D4D4">.</span><span style="color: #CE9178">getSubject()</span><span style="color: #569CD6">#</span><span style="color: #CE9178">"</span><span style="color: #808080">></span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #569CD6">cftry</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #6A9955"><!---</span></span>
<span class="line"><span style="color: #6A9955"> Code to call the http request and parse any errors.</span></span>
<span class="line"><span style="color: #6A9955"> Returns a struct "result" with keys "code", "status" and "message".</span></span>
<span class="line"><span style="color: #6A9955"> ...</span></span>
<span class="line"><span style="color: #6A9955"> ---></span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #569CD6">cfif</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">result</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">code</span><span style="color: #D4D4D4"> neq </span><span style="color: #B5CEA8">200</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #6A9955"><!--- Successful webservice call, but Postmark returned an error. Log it. ---></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #569CD6">cflog</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">text</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"Failed: </span><span style="color: #569CD6">#</span><span style="color: #9CDCFE">subject</span><span style="color: #569CD6">#</span><span style="color: #CE9178">; </span><span style="color: #569CD6">#</span><span style="color: #9CDCFE">result</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">code</span><span style="color: #569CD6">#</span><span style="color: #CE9178"> </span><span style="color: #569CD6">#</span><span style="color: #9CDCFE">result</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">status</span><span style="color: #569CD6">#</span><span style="color: #CE9178">"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">file</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"postmark"</span><span style="color: #D4D4D4"> </span><span style="color: #808080">/></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #569CD6">cfelse</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #6A9955"><!--- Message was accepted by Postmark. Send it. ---></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #569CD6">cflog</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">text</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"Success: </span><span style="color: #569CD6">#</span><span style="color: #9CDCFE">subject</span><span style="color: #569CD6">#</span><span style="color: #CE9178">"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">file</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"postmark"</span><span style="color: #D4D4D4"> </span><span style="color: #808080">/></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"></</span><span style="color: #569CD6">cfif</span><span style="color: #808080">></span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #569CD6">cfcatch</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">type</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"any"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #6A9955"><!--- Some other error. Log it. ---></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"><</span><span style="color: #569CD6">cflog</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">text</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"Failed: </span><span style="color: #569CD6">#</span><span style="color: #9CDCFE">subject</span><span style="color: #569CD6">#</span><span style="color: #CE9178">; </span><span style="color: #569CD6">#</span><span style="color: #9CDCFE">e</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">message</span><span style="color: #569CD6">#</span><span style="color: #CE9178">"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">file</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"postmark"</span><span style="color: #D4D4D4"> </span><span style="color: #808080">/></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"></</span><span style="color: #569CD6">cfcatch</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080"></</span><span style="color: #569CD6">cftry</span><span style="color: #808080">></span></span>
<span class="line"></span>
<span class="line"><span style="color: #808080"></</span><span style="color: #569CD6">cfthread</span><span style="color: #808080">></span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955"><!--- ...now get on with returning a page to the user... ---></span></span>
<span class="line"></span></code></pre>
<h3 id="caveat-using-cfscript" tabindex="-1">Caveat: using cfscript <a class="header-anchor" href="https://sebduggan.uk/posts/using-cfthread-to-speed-up-your-web-service-calls/">#</a></h3>
<p>There is one issue I ran into, which appears to be related to <a href="http://cfbugs.adobe.com/cfbugreport/flexbugui/cfbugtracker/main.html#bugId=83835">this bug report</a>: if you're writing your code in cfscript, you would use <code>new http();</code> to create your HTTP request. However, under certain circumstances -- it may be the ones described in the bug, or may be slightly different -- this will fail. Luckily -- because I was logging the results of the thread -- I was able to detect it, and found the following error message:</p>
<p><code>Failed: Website order: 12400; An error occurred when performing a file operation read on file C:\[path\to\website]\WEB-INF\cftags\META-INF\taglib.cftld.</code></p>
<p>There was a string of several of these over a number of hours, then it went back to normal -- making it very tricky to reproduce the error.</p>
<p>My solution was to rewrite the component using tags rather than script; fingers crossed, it has been behaving itself perfectly ever since.</p>
Tip: make sure your new IPs are clean2011-03-21T12:00:00Zhttps://sebduggan.uk/posts/tip-make-sure-your-new-ips-are-clean/<p>A cautionary tale for anyone running their own web server – something I'd never considered previously.</p>
<p>We've all been there -- you're setting up your web server, and request a bunch of IP addresses from your hosting provider. You then assign these IPs to your various web sites, and off you go...</p>
<p><em>But how many of you actually check the history of these IP addresses?</em></p>
<p>I always assumed that I was getting pristine, brand-new addresses that had never been used before. So I set up my new server, transferred all my sites from my old server, and everything seemed fine. But then I started noticing quite a number of odd requests in the logs:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">http://94.76.220.233/forumdisplay.php?fid=61</span></span>
<span class="line"><span style="color: #D4D4D4">http://94.76.220.234/archiver/?tid-144650.html</span></span>
<span class="line"><span style="color: #D4D4D4">http://94.76.220.232/19</span></span>
<span class="line"><span style="color: #D4D4D4">http://94.76.220.232/index.php?fromuid=83714</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span></code></pre>
<p>These were certainly nothing to do with me, and the fact that they were accessing the IP directly rather than using a domain name seemed suspicious. So, being an inquisitive type, I started to investigate.</p>
<p>Tracing back the referrers, I found out a number of things:</p>
<ul>
<li>The referring pages were all based in China, but were directly linking to an IP address in the UK</li>
<li>They were nearly all chat forums</li>
<li>...most of which were inaccessible without logging in</li>
<li>...and those which were accessible seemed concerned with pictures of young Chinese boys and girls</li>
<li>...at which point I stopped my digging.</li>
</ul>
<p>Now, quite aside from the fact that I didn't want any increased traffic caused by my IPs being linked to, I <em>really</em> didn't want my clients' websites tainted by anything like this.</p>
<p>My hosting company was very helpful -- within an hour they'd given me a new batch of IP addresses, and time to switch over before they remove the old ones. So I'm now just waiting for DNS servers around the world to catch up...</p>
<p>There are a few things you can do to check your new IP addresses:</p>
<h3 id="1-check-against-ip-blacklists" tabindex="-1">1. Check against IP blacklists <a class="header-anchor" href="https://sebduggan.uk/posts/tip-make-sure-your-new-ips-are-clean/">#</a></h3>
<p>Run a check at <a href="http://rbls.org">rbls.org</a> for each of your IP addresses. Admittedly, in my case this didn't turn up any information, but it might disclose previous misuse of your IP address.</p>
<h3 id="2-look-up-the-ip-address-in-search-engines" tabindex="-1">2. Look up the IP address in search engines <a class="header-anchor" href="https://sebduggan.uk/posts/tip-make-sure-your-new-ips-are-clean/">#</a></h3>
<p>Do a search on Google for the IP address. If your IP is clean, you'll see a number of results anyway, but they will just be lists of allocated IP addresses. Search for a good IP first so you know what clean results look like.</p>
<p>If you get results like the one below, you might want to request new addresses...</p>
<p><img src="https://sebduggan.uk/posts/tip-make-sure-your-new-ips-are-clean/src/assets/img/uploads/ip-google.png" alt="Google results for a contaminated IP"></p>
<p>After seeing my log files, I also checked against baidu.com, since a number of referrals came from there.</p>
<h3 id="3-check-for-unexpected-traffic" tabindex="-1">3. Check for unexpected traffic <a class="header-anchor" href="https://sebduggan.uk/posts/tip-make-sure-your-new-ips-are-clean/">#</a></h3>
<p>OK, this one needs to be done once the server's up and running. But if you see lots of unexpected requests, you could have a problem.</p>
<p>I have my default missing page handler set up to log any request which results in a 404. Not only does this help track unexpected behaviour, it's also a good tool to make sure all your sites are running as they should...</p>
<h3 id="4-take-action" tabindex="-1">4. Take action! <a class="header-anchor" href="https://sebduggan.uk/posts/tip-make-sure-your-new-ips-are-clean/">#</a></h3>
<p>If you find anything suspect about your new IPs -- <strong>ask your host for some new ones</strong>. It's not worth the risk, however small, of your sites' reputations being tarnished.</p>
CSS tip for centered site designs2010-11-18T12:00:00Zhttps://sebduggan.uk/posts/css-tip-for-centered-site-designs/<p>A quick tip for when you're designing a site with centered content.</p>
<p>If the content doesn't reach to the bottom of the window, there's no vertical scroll bar; if you then navigate to a longer page, you'll see the content jump about 10px to the left as it's centered in a slightly narrower window. Sometimes, you'll even see this jump half-way through rendering a longer page.</p>
<p>To solve this, simply add this to your CSS:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">css</div><code><span class="line"><span style="color: #D7BA7D">html</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">overflow-y</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">scroll</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span></code></pre>
<p>Now, you'll always have space reserved for the vertical scrollbar, whether or not it's required; and you won't see the sideways jumping any more.</p>
ORM gotcha: Hibernate reserved words2010-08-13T12:00:00Zhttps://sebduggan.uk/posts/orm-gotcha-hibernate-reserved-words/<p>Typically, the very first time I tried to use CF9's ORM, I ran into a bizarre problem which had me scratching my head for hours...</p>
<p>The setup was this: a table in my SQL database called "member". A <code>member.cfc</code> object with the following code:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #569CD6">component</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">output</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"false"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">persistent</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"true"</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">property</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"memberid"</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">fieldtype</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"id"</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">property</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"firstname"</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">property</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"lastname"</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span></code></pre>
<p>And a test page which called it:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfml</div><code><span class="line"><span style="color: #808080"><</span><span style="color: #569CD6">cfscript</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #9CDCFE">members</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">EntityLoad</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">"member"</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #DCDCAA">writedump</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">members</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #808080"></</span><span style="color: #569CD6">cfscript</span><span style="color: #808080">></span></span>
<span class="line"></span></code></pre>
<p>Running this page always gave the following error:</p>
<p><code>unexpected token: member near line 1, column 6 [from member]</code></p>
<p>However, if I gave the <code>EntityLoad()</code> function more arguments, for instance:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">cfc</div><code><span class="line"><span style="color: #9CDCFE">members</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">EntityLoad</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">"member"</span><span style="color: #D4D4D4">, {});</span></span>
<span class="line"></span></code></pre>
<p>...it worked perfectly.</p>
<p>After a lot of head-scratching and seeking of help on Twitter (thanks <a href="https://twitter.com/aliaspooryorik">@aliaspooryorik</a>) and Stack Overflow (thanks Henry), I had the idea that "member" might be some sort of reserved word in Hibernate. So I changed the table and component names to "user" -- and everything worked exactly as it should.</p>
<p>My guess is that it works fine if in the generated HQL query there's a WHERE clause following the <code>SELECT FROM member</code>; but if you just have the basic <code>EntityLoad("member")</code> then it doesn't have this WHERE clause, and so the reserved word "member" gets seen as an HQL keyword.</p>
<p>I guess I was just unlucky that my very first attempt at ORM ran into this problem; does anyone know of any other table/object names I should steer clear of?</p>
Serving web fonts from IIS2010-04-10T12:00:00Zhttps://sebduggan.uk/posts/serving-web-fonts-from-iis/<p>I've just started playing with web fonts for a site redesign. I came across the following gotcha (thanks, Firebug, for alerting me to it!).</p>
<p><strong>If you are running IIS 6 or higher on your web server, some of the fonts will be disabled by default.</strong></p>
<p>Your typical <code>@font-face</code> declaration might look like this:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">css</div><code><span class="line"><span style="color: #C586C0">@font-face</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">font-family</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">'VegurRegular'</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">src</span><span style="color: #D4D4D4">: </span><span style="color: #DCDCAA">url</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">'Vegur-R_0500.eot'</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">src</span><span style="color: #D4D4D4">: </span><span style="color: #DCDCAA">local</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">'Vegur'</span><span style="color: #D4D4D4">),</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">local</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">'Vegur-Regular'</span><span style="color: #D4D4D4">),</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">url</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">'Vegur-R_0500.woff'</span><span style="color: #D4D4D4">) </span><span style="color: #DCDCAA">format</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">'woff'</span><span style="color: #D4D4D4">),</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">url</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">'Vegur-R_0500.ttf'</span><span style="color: #D4D4D4">) </span><span style="color: #DCDCAA">format</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">'truetype'</span><span style="color: #D4D4D4">),</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">url</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">'Vegur-R_0500.svg#Vegur-Regular'</span><span style="color: #D4D4D4">) </span><span style="color: #DCDCAA">format</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">'svg'</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span></code></pre>
<p>This will deliver one of four different font formats, depending on your browser's capabilities. (The font is <a href="http://www.fontsquirrel.com/fontfacedemo/Vegur">Vegur</a>, a really nice-looking free font I found over at <a href="http://www.fontsquirrel.com/">Font Squirrel</a>).</p><p></p>
<p>By default, the MIME types in IIS 6 are configured to deliver EOT (as used by IE) and TTF files. But WOFF (Firefox) and SVG (iPhone, iPad and others) will not be served.</p>
<p>Simply add the following MIME type declarations via IIS Manager (HTTP Headers tab of website properties):</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">.woff application/x-woff</span></span>
<span class="line"><span style="color: #D4D4D4">.svg image/svg+xml</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span></code></pre>
<p>...and everything should work fine.</p>
Stop mail clients auto linking text as URLs2010-02-09T12:00:00Zhttps://sebduggan.uk/posts/stop-mail-clients-auto-linking-text-as-urls/<p>This is just a quickie -- a reminder for my own reference, but could also be useful to others.</p>
<p>I'm building a shop, and the email confirmations include the line:</p>
<p><code>Your credit card statement will show a payment to "MYDOMAIN.COM".</code></p>
<p>Which is fine, except that many mail clients (e.g. Apple Mail) will take MYDOMAIN.COM and link it as a URL -- which I don't want.</p>
<p>To avoid this, simply add a <strong>zero width space</strong> into the URL -- for instance:</p>
<p><code>Your credit card statement will show a payment to "MYDOMAIN&amp;#8203;.COM".</code></p>
<p>(That's the HTML version. If you're generating a plain text alternative (using CFML, of course), you'll want to use <code>#Chr(8203)#</code>).</p>
<p>The text will now look identical -- except it will not be linked by the mail client.</p>
Handling initial display states for jQuery-enhanced pages2009-08-25T12:00:00Zhttps://sebduggan.uk/posts/handling-initial-display-states-for-jquery-enhanced-pages/<p>When building a jQuery-enhanced site (or using any other JavaScript library, for that matter) I find one of the tasks I use jQuery for is to set the initial display states of various elements on the page.</p>
<p>For instance, you may have some elements that should only be displayed if JavaScript is enabled; and some which should be hidden. But what do you do to avoid the flash of unwanted content which may appear before various libraries load? Here's my solution...</p>
<p>For my example, I'll have two DIVs, called "basic" and "enhanced". Of course, in real life your requirements will most likely be more complex than this:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">html</div><code><span class="line"><span style="color: #808080"><</span><span style="color: #569CD6">div</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">id</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"basic"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> An element which only shows if JavaScript IS NOT enabled</span></span>
<span class="line"><span style="color: #808080"></</span><span style="color: #569CD6">div</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #808080"><</span><span style="color: #569CD6">div</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">id</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"enhanced"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> An element which only shows if JavaScript IS enabled</span></span>
<span class="line"><span style="color: #808080"></</span><span style="color: #569CD6">div</span><span style="color: #808080">></span></span>
<span class="line"></span></code></pre>
<p>The first thing to do is set the default, non-JavaScript state in CSS:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">css</div><code><span class="line"><span style="color: #D7BA7D">#basic</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">display</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">block</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"><span style="color: #D7BA7D">#enhanced</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">display</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">none</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span></code></pre>
<p>Previously, my hide/show method would use jQuery:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">js</div><code><span class="line"><span style="color: #DCDCAA">$</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">function</span><span style="color: #D4D4D4">(){</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">$</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">'#basic'</span><span style="color: #D4D4D4">).</span><span style="color: #DCDCAA">hide</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">$</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">'#enhanced'</span><span style="color: #D4D4D4">).</span><span style="color: #DCDCAA">show</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">});</span></span>
<span class="line"></span></code></pre>
<p>...and this would work, but there is the overhead of loading everything before the jQuery ready() function fires. This can often cause a flash of the basic-state content to show, and elements to jump around on the page. Also, there is (slight) added jQuery processing overhead if you're handling multiple elements.</p>
<p>So the method I now use -- which I'm sure I picked up in part from somewhere else, but I can't find anything by Googling! -- is to include the following script on each page:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">html</div><code><span class="line"><span style="color: #808080"><</span><span style="color: #569CD6">script</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">type</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">"text/javascript"</span><span style="color: #808080">></span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">document</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">getElementsByTagName</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">'html'</span><span style="color: #D4D4D4">)[</span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">].</span><span style="color: #9CDCFE">className</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">'js'</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #808080"></</span><span style="color: #569CD6">script</span><span style="color: #808080">></span></span>
<span class="line"></span></code></pre>
<p>It will run immediately the HTML document is processed, regardless of how long the rest of the page takes to load, and simply sets a class of "js" on the HTML element. You can then use CSS to handle what should be shown or hidden:</p>
<pre class="shiki " style="background-color: #1E1E1E" tabindex="0"><div class="language-id">css</div><code><span class="line"><span style="color: #D7BA7D">html.js</span><span style="color: #D4D4D4"> </span><span style="color: #D7BA7D">#basic</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">display</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">none</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"><span style="color: #D7BA7D">html.js</span><span style="color: #D4D4D4"> </span><span style="color: #D7BA7D">#enhanced</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">display</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">block</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span></code></pre>
<p>...and the manipulation will be there as soon as the CSS is loaded.</p>
<p>I've put this tip up here mostly as a reminder to myself for the future, but I hope other people will find it useful too...</p>