<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
    xmlns:dc="http://purl.org/dc/elements/1.1/"
    xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
    xmlns:admin="http://webns.net/mvcb/"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:content="http://purl.org/rss/1.0/modules/content/">

    <channel>
    
    <title><![CDATA[MetaSushi Blog]]></title>
    <link>http://metasushi.com/blog</link>
    <description></description>
    <dc:language>en</dc:language>
    <dc:creator>isaac.raway@gmail.com</dc:creator>
    <dc:rights>Copyright 2012</dc:rights>
    <dc:date>2012-05-17T12:05:47+00:00</dc:date>
    <admin:generatorAgent rdf:resource="http://expressionengine.com/" />
    

    <item>
      <title><![CDATA[Form submission summaries in ProForm]]></title>
      <link>http://metasushi.com/site/form-summary</link>
      <guid>http://metasushi.com/site/form-summary#When:12:05:47Z</guid>
      <description><![CDATA[<p>I've just added a new parameter to <a href="http://devot-ee.com/add-ons/proform-drag-and-drop-form-builder">ProForm</a> to enable form submission summaries, in response to a client request last night.</p>

<p>Check it out:</p>

<h4><b>last_step_summary="yes"</b></h4>
<p>The <b>last_step_summary</b> parameter turns on a special mode for the last step of a multistep form, where all fields are returned by the {fields} loop. This can be used to present a summary of the form about to be submitted before the user actually submits it, giving them a chance to go back and change their responses.</p>
<p>Note that in order for this to work correctly, you should create an empty step at the end of the form without any fields inside of it. Otherwise the form will be expecting those fields to be set, and if they are required, it will not process the form submission.</p>
<p>This parameter is not supported by the <kbd>Simple Form Tag</kbd> since you cannot provide a custom loop for that tag and therefore it is not needed.</p>
<div class="tip">
  <p><b>Example Usage</b></p>
    <pre class="brush: xml">
        &#123;exp:proform:form form="support_request" last_step_summary="yes"&#125;
            &lt;!-- Step tabs, hidden fields, etc. here --&gt;
            &#123;if on_last_step&#125;
                &lt;h3&gt;Summary&lt;/h3&gt;
                &lt;p&gt;Be sure to click &lt;b&gt;Submit&lt;/b&gt; to send the request.&lt;/p&gt;
                &lt;p&gt;If you need to make revisions, use the Previous button or the tabs above.&lt;/p&gt;
                &lt;hr/&gt;
                &#123;fields&#125;
                    &#123;if field_label&#125;
                        &lt;p&gt;&lt;strong&gt;&#123;field_label&#125;:&lt;/strong&gt; &#123;field_value&#125;&lt;/p&gt;
                    &#123;/if&#125;
                &#123;/fields&#125;
                &lt;hr/&gt;
            &#123;if:else&#125;
                &#123;fieldrows&#125;
                    &#123;fields&#125;
                        &lt;!-- Render fields normally based on their type (see the full example template) --&gt;
                    &#123;/fields&#125;
                &#123;/fieldrows&#125;
            &#123;/if&#125;
            &lt;!-- Previous and Submit buttons and CAPTCHA here --&gt;
        &#123;/exp:proform:form&#125;
    </pre>
</div>

<p><b>Results</b></p>
<p>Here's what it looks like in the browser:</p>

<img src="https://img.skitch.com/20120517-g79emq41rrcuf5h5b84sc6251m.png" />]]></description>
      <dc:subject><![CDATA[]]></dc:subject>
      <dc:date>2012-05-17T12:05:47+00:00</dc:date>
    </item>

    <item>
      <title><![CDATA[ProForm 1.0 Released!]]></title>
      <link>http://metasushi.com/site/proform-1.0-released</link>
      <guid>http://metasushi.com/site/proform-1.0-released#When:13:04:59Z</guid>
      <description><![CDATA[<p>Hello, lovely ExpressionEngine community!</p>

<p>I am very happy to announce the final release of ProForm 1.0.</p>

<p>This represents a major milestone for ProForm, and I think you'll really like the improvements I've made. This new release features an improved and much cleaner UI, multistep form support, a single template tag to render forms automatically, and even better support for 100% custom form markup. This is the biggest release for the module to date - and a lot of love and effort has gone into making it the best it can be.</p>

<p>I hope you like it.</p>

<p>Check out the new build on Devot:ee at:<br/>
    <a href="http://devot-ee.com/add-ons/proform-drag-and-drop-form-builder">http://devot-ee.com/add-ons/proform-drag-and-drop-form-builder</a></p>

<p>Read up on the new features in the documentation at:<br/>
  <a href="http://metasushi.com/documentation/proform/">http://metasushi.com/documentation/proform/</a></p>

<p>As always, please let me know if you have any suggestions or questions. Best contact method is through the <a href="http://devot-ee.com/add-ons/support/proform-drag-and-drop-form-builder/viewforum/1840">support forums</a>.</p>
    

<p>- Isaac</p>

<a href="http://twitter.com/airways">@Airways</a>


<h4>Screenshots</h4>

<p>
  <a href="http://metasushi.com/uploads/promo-images/proform-multistep-example.jpg" class="thickbox"><img src="http://metasushi.com/uploads/promo-images/proform-multistep-example.jpg" width="75%" /></a><br/>
  <b>New template showing multiple form step support</b></p>

<p>
  <a href="http://metasushi.com/uploads/promo-images/proform-form-settings.jpg" class="thickbox"><img src="http://metasushi.com/uploads/promo-images/proform-form-settings.jpg" width="75%" /></a><br/>
  <b>Form settings</b>
</p>

<p>
  <a href="http://metasushi.com/uploads/promo-images/proform-entries.jpg" class="thickbox"><img src="http://metasushi.com/uploads/promo-images/proform-entries.jpg" width="75%" /></a><br/>
  <b>Entries listing</b>
</p>

<p>
  <a href="http://metasushi.com/uploads/promo-images/proform-view-entry.jpg" class="thickbox"><img src="http://metasushi.com/uploads/promo-images/proform-view-entry.jpg" width="75%" /></a><br/>
  <b>Full entry view</b>
</p>

<p>
  <a href="http://metasushi.com/uploads/promo-images/proform-toolbox.jpg" class="thickbox"><img src="http://metasushi.com/uploads/promo-images/proform-toolbox.jpg" width="75%" /></a><br/>
  <b>Form layout - the new Toolbox</b>
</p>

<p>
  <a href="http://metasushi.com/uploads/promo-images/proform-overrides.jpg" class="thickbox"><img src="http://metasushi.com/uploads/promo-images/proform-overrides.jpg" width="75%" /></a><br/>
  <b>Form layout - overrides</b>
</p>


<h4>## Changelog ##</h4>

<p>
  
<b>Forms</b><br/>
* Added multistep form support<br/>
* Added official HTML email support<br/>
* Created new Toolbar UI, which makes adding and managing fields from the form layout much easier<br/>
* Added new, single line tag to render a complete form by rendering the new sample template automatically:<br/>
&nbsp;&nbsp; * &#123;exp:proform:simple form="contact_us"&#125;<br/>
* Added all-new sample template with:<br/>
&nbsp;&nbsp;  * Much better styling - and better HTML base if you want to do custom styles<br/>
&nbsp;&nbsp;  * Full multistep support<br/>
&nbsp;&nbsp;  * Default placeholders based on type and validation<br/>
&nbsp;&nbsp;  * Automatic jQuery UI selectors for date and time fields<br/>
* Added support for AJAX posting (returns form results and errors as a JSON object)<br/>
* Added support for headings inside a form<br/>
* Added option to allow redirecting data for a form to an existing DB table (experimental)<br/>
* Made all settings that ask for a field name into dropdowns<br/>
<br/>
<b>Entries</b><br/>
* Added options to control which columns show in entries listing<br/>
* Added new view entry page to show all fields and browser info for an entry<br/>
* Fixed some problems with entry listing pagination and incorrectly truncating values<br/>
<br/>
<br/>
<b>Fields</b><br/>
* Added "time" field type<br/>
* Added back in real textarea field type to simplify creating these fields<br/>
* Renamed "string" fieldtype to "text", to match the HTML input type<br/>
* Added support for a blank option to lists ("i.e. 'Select an Item...' message)<br/>
* Hiding length option for fields that do not use it<br/>
* Added field option to specify if fields are reusable (where they will show up in the new Toolbar's Library section)<br/>
* Added list of forms that use a shared field to the field's edit page<br/>
<br/>
<b>Templates</b><br/>
* Added a {field_validation} loop inside the {fields} loop to help with custom client-side validation / styling<br/>
* Added message:* params to allow overriding validation error messages<br/>
* Added error_delimiters to allow specifying the wrapper markup for error messages<br/>
* Added variable_prefix param to allow for specifying a custom prefix for all variables (resolves potential collisions with other tags)<br/>
* Added new {hidden_fields} variable pair<br/>
* Added new {selected} variable inside of {field_setting_list} to allow reselecting items more easily<br/>
* Added support for multiselect lists and proper validation for them<br/>
<br/>
<b>Fixed Issues</b><br/>
* Disabled automatic Field Name generation from Field Label, if editing an existing field.<br/>
* Fixed bugs with having multiple forms on a page<br/>
* Fixed a bug where fields are not properly renamed in a form's DB table<br/>
* Fix problem with dirty warning incorrectly showing when clicking submit in some CP pages<br/>
* Fixed some bugs where IDs of Mailing Lists and Upload Preferences where incorrect due to an invalid use of array_merge<br/>
* Fixed a bug where editing a field removed form-specific overrides<br/>
* Fixed bugs in parsing some combinations of list field type options<br/>
<br/>
<b>Documentation</b><br/>
* Heavily updated documentation<br/>
* Added quick-start info to README<br/>
* Added installation page to documentation<br/>
<br/>
<b>Internal</b><br/>
* Removed requirements to set an encryption_key - config is now stored in DB and represented by a secure hash<br/>
* Added logic to make uploaded filenames unique so that they cannot be guessed<br/>
* Refactored some internal structures to be much cleaner<br/>
* Refactored handling of form sessions to be easier to work with<br/>
* Added additional notification debugging code<br/>
* Added hidden option to allow use of encrypted form data (experimental)<br/>
</p>]]></description>
      <dc:subject><![CDATA[]]></dc:subject>
      <dc:date>2012-04-12T13:04:59+00:00</dc:date>
    </item>

    <item>
      <title><![CDATA[Newsletter Online]]></title>
      <link>http://metasushi.com/site/newsletter-online</link>
      <guid>http://metasushi.com/site/newsletter-online#When:04:47:04Z</guid>
      <description><![CDATA[<p>The <a href="http://metasushi.com/newsletter/">MetaSushi EE Add-ons Newsletter</a> is now available. To stay up to date with the latest ExpressionEngine add-on news from MetaSushi, be sure to <a href="http://metasushi.com/newsletter/">subscribe</a> today (very low volume).</p>]]></description>
      <dc:subject><![CDATA[]]></dc:subject>
      <dc:date>2012-03-12T04:47:04+00:00</dc:date>
    </item>

    <item>
      <title><![CDATA[ProForm Extensions - Sending an SMS Notification]]></title>
      <link>http://metasushi.com/site/proform-extensions-sending-an-sms-notification</link>
      <guid>http://metasushi.com/site/proform-extensions-sending-an-sms-notification#When:01:56:36Z</guid>
      <description><![CDATA[<p><a href="http://devot-ee.com/add-ons/proform-drag-and-drop-form-builder">ProForm</a> has an extensive list of available hooks with which you can extend it's behavior by writing fairly simple custom extensions. You can find a complete list and description of each hook on the <a href="http://metasushi.com/documentation/proform/extending/">ProForm Extension</a> documentation page.</p>

<p>In this post, I'm going to walk you through a simple custom notification extension for ProForm, built using one of these hooks.</p>

<p>Let's say you want to send an SMS notification out to a service technician when a particular form is filled out on your site. With ProForm, we can easily accomplish this using the Twilio API and a simple custom extension.</p>

<h4>Create the Extension</h4>
<p>If you want to follow along, create a new blank extension using the ExpressionEngine package builder <a href="http://pkg.io/">pkg.io</a>. I'll also include the complete source code at the end of this post.</p>
<p>I'll be using the name <b>Sms_custom_notification_ext</b> for this example extension. pkg.io will create a file named <b>ext.sms_custom_notification.php</b> for the extension, according to the ExpressionEngine conventions.</p>

<h3>Register the Hook</h3>
<p>Open the <b>ext.sms_custom_notification.php</b> file and add the following code to register the <b>proform_insert_end</b> hook in the extension:</p>

<div class="box"><p>File: <strong>ext.sms_custom_notification.php</strong></p></div>
<pre class="brush: php">
&lt;?php // Snippet:

    public function activate_extension()
    &#123;
        $extensions = array(
            array(
                'class'     =&gt; __CLASS__,
                'method'    =&gt; 'proform_insert_end',
                'hook'      =&gt; 'proform_insert_end',
                'settings'  =&gt; serialize($this-&gt;settings),
                'version'   =&gt; $this-&gt;version,
                'enabled'   =&gt; 'y'
            ),
        );

        foreach($extensions as $data)
        &#123;
            $this-&gt;EE-&gt;db-&gt;insert('extensions', $data);
        &#125;
    &#125;
</pre>

<p>Here's the documentation for the hook that we're using:</p>

<table border="1">
  <tr><th><b>Hook</b></th><th><b>Params</b></th><th><b>Return</b></th>
    <tr><td><b>proform_insert_end</b></td>
    <td>$module, $form_obj, $data</td>
    <td>none</td>
</tr>
<tr><td colspan="3">
    <p><b>Description</b></p>
    <p>This hook is called just after inserting the data for the form submission into the database. This can be used to push the final data array to an external source, generating some sort of custom notification, or a number of other purposes.<p></td>
</tr>
</table> 

<p>There are two things to keep in mind: <b>proform_insert_end</b> is only called if validation passed for the submission, and it is not called for <em>share</em> type forms. For those forms, you would want to use the <b>proform_no_insert</b> hook instead.</p>

<h3>Set Twilio API Settings</h3> 
<p>We'll be using the Twillio API and the <a href="https://github.com/twilio/twilio-php">twilio-php</a> library to send our SMS message. Make sure to install the PEAR module or include it in a folder named Services within the extension package directory.</p>

<p>The first thing we need to do is sign up for a Twilio account. They provide a number of free SMS messages for testing, and signing up is very quick, so you should actually do it!</p>

<p>To create a Twilio API object, we need our <b>sid</b> and <b>token</b> values, which you can get from your profile page in Twillio. We're going to store these along with the number we want to test, and the number we are sending from (again, assigned by Twilio).</p>

<p>Create these new private variables on the extension class:</p>

<div class="box"><p>File: <strong>ext.sms_custom_notification.php</strong></p></div>
<pre class="brush: php">
&lt;?php // Snippet:

    private $twilio_sid    = 'xxxxxxxx';
    private $twilio_token  = 'xxxxxxxx';
    private $twilio_from   = '555678678';
    private $twilio_notify = '111234234'; 
</pre>

<h3>Create a Hook Method</h3>
<p>Now we will create a new method to handle this hook. We'll fill in each of these steps with a single line of code:</p>

<div class="box"><p>File: <strong>ext.sms_custom_notification.php</strong></p></div>
<pre class="brush: php">
&lt;?php // Snippet:

    function proform_insert_end($module, $form_obj, $data)
    &#123;
        // 1. Create a new Twilio API object
        // 2. Send the SMS
        // 3. We haven't change the data, but we must return it:
        return $data;
    &#125;
</pre>

<h4>1. Create the Twilio API Object</h4>
<p>First we just need to use the <b>sid</b> and <b>token</b> values to create the new object, within the <b>proform_insert_end</b> method:</p>

<div class="box"><p>File: <strong>ext.sms_custom_notification.php</strong></p></div>
<pre class="brush: php">
&lt;?php // Snippet:

    // 1. Create a new Twilio API object
    $client = new Services_Twilio($this-&gt;twilio_sid, $this-&gt;twilio_token);
</pre>

<h4>2. Send the Message</h4>
<p>The next step is to actually send the message! We're going to use the saved cell numbers we set above to do this, and construct a short summary of our form post for the on-call tech to respond to.</p>

<div class="box"><p>File: <strong>ext.sms_custom_notification.php</strong></p></div>
<pre class="brush: php">
&lt;?php // Snippet:

    // 2. Send the SMS
    $message = $client-&gt;account-&gt;sms_messages-&gt;create(
        $this-&gt;twilio_from,          // From Twilio number
        $this-&gt;twilio_notify,        // To admin number
        'New '.$form_obj-&gt;form_name.' post:'
        .' Name: '.$data['name']
        .' Phone: '.$data['phone']
        .' Severity: '.$data['severity']
    ); 
</pre>

<h4>3. Return the Data Array</h4>
<p>Finally, we need to return the data array (even though we did not modify it) so that the rest of the form processing will continue as normal:</p>

<div class="box"><p>File: <strong>ext.sms_custom_notification.php</strong></p></div>
<pre class="brush: php">
&lt;?php // Snippet:

    // 3. We haven't change the data, but we must return it:
    return $data;
</pre>

<p>That's all there is to it! We've successfully created a quick extension to send notification SMS messages to our cell phone when a new form submission is posted. This is really just scratching the surface of what is possible with ProForm and a little custom extension code.</p>

<h3>Complete Code</h3>

<div class="box"><p>File: <strong>ext.sms_custom_notification.php</strong></p></div>
<pre class="brush: php">
&lt;?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');

// Make sure to install the PEAR module or include it in the Services folder within this extension!
// The module is available at https://github.com/twilio/twilio-php
require 'Services/Twilio.php';

class Sms_custom_notification_ext &#123;
    
    public $settings        = array();
    public $description     = 'Sends an SMS notification when a form is submitted.';
    public $docs_url        = '';
    public $name            = 'SMS Custom Notification';
    public $settings_exist  = 'n';
    public $version         = '1.0';
    
    private $EE;
    private $twilio_sid    = 'xxxxxxxx';
    private $twilio_token  = 'xxxxxxxx';
    private $twilio_from   = '555678678';
    private $twilio_notify = '111234234';
    
    public function __construct($settings = '')
    &#123;
        $this-&gt;EE =& get_instance();
        $this-&gt;settings = $settings;
    &#125;
    
    public function activate_extension()
    &#123;
        $extensions = array(
            array(
                'class'     =&gt; __CLASS__,
                'method'    =&gt; 'proform_insert_end',
                'hook'      =&gt; 'proform_insert_end',
                'settings'  =&gt; serialize($this-&gt;settings),
                'version'   =&gt; $this-&gt;version,
                'enabled'   =&gt; 'y'
            ),
        );

        foreach($extensions as $data)
        &#123;
            $this-&gt;EE-&gt;db-&gt;insert('extensions', $data);
        &#125;
    &#125;
    
    function disable_extension()
    &#123;
        $this-&gt;EE-&gt;db-&gt;where('class', __CLASS__);
        $this-&gt;EE-&gt;db-&gt;delete('extensions');
    &#125;
    
    function update_extension($current = '')
    &#123;
        if ($current == '' OR $current == $this-&gt;version)
        &#123;
            return FALSE;
        &#125;
    &#125;
    
    function proform_insert_end($module, $form_obj, $data)
    &#123;
        // 1. Create a new Twilio API object
        $client = new Services_Twilio($this-&gt;twilio_sid, $this-&gt;twilio_token);
        
        // 2. Send the SMS
        $message = $client-&gt;account-&gt;sms_messages-&gt;create(
            $this-&gt;twilio_from,          // From Twilio number
            $this-&gt;twilio_notify,        // To admin number
            'New '.$form_obj-&gt;form_name.' post:'
            .' Name: '.$data['name']
            .' Phone: '.$data['phone']
            .' Severity: '.$data['severity']
        );
        
        // 3. We haven't change the data, but we must return it:
        return $data;
    &#125;
    

&#125; 

</pre>]]></description>
      <dc:subject><![CDATA[]]></dc:subject>
      <dc:date>2012-02-27T01:56:36+00:00</dc:date>
    </item>

    <item>
      <title><![CDATA[AjaxPublish + Illuminated + Publish Bar]]></title>
      <link>http://metasushi.com/site/ajaxpublish-illuminated-publish-bar</link>
      <guid>http://metasushi.com/site/ajaxpublish-illuminated-publish-bar#When:02:15:19Z</guid>
      <description><![CDATA[<p>I've been playing with a new extension I'm calling AJaxPublish. It basically hooks into the publish page to prevent the need for a full page reload when saving an entry.</p>

<p>By combining this with my field type <a href="http://devot-ee.com/add-ons/illuminated">Illuminated</a> and the awesome <a href="http://devot-ee.com/add-ons/publish-bar">Publish Bar</a> extension, I've been able to put together a combination that almost feels like editing in a local editor like TextMate. Kind of cool if I do say so myself.</p>

<p>
  <a href="http://metasushi.com/uploads/promo-images/illuminated_publishbar_ajaxpublish.jpg" class="thickbox"><img src="http://metasushi.com/uploads/promo-images/illuminated_publishbar_ajaxpublish.jpg" width="75%" /></a>
</p>





]]></description>
      <dc:subject><![CDATA[]]></dc:subject>
      <dc:date>2012-02-05T02:15:19+00:00</dc:date>
    </item>

    <item>
      <title><![CDATA[RSS Fixed]]></title>
      <link>http://metasushi.com/site/rss-fixed</link>
      <guid>http://metasushi.com/site/rss-fixed#When:01:48:25Z</guid>
      <description><![CDATA[<p>Looks like the RSS feed was broken there. Should be all fixed now!</p>]]></description>
      <dc:subject><![CDATA[]]></dc:subject>
      <dc:date>2012-02-05T01:48:25+00:00</dc:date>
    </item>

    <item>
      <title><![CDATA[New field type - Illuminated]]></title>
      <link>http://metasushi.com/site/new-field-type-illuminated</link>
      <guid>http://metasushi.com/site/new-field-type-illuminated#When:17:18:18Z</guid>
      <description><![CDATA[<p>Tired of struggling with a WYSIWYG editor to edit content on your site? Speak HTML? Then Illuminated is for you - a high quality editing surface that supports full syntax highlighting for HTML and JavaScript code - right in your publish page.</p>

<p>Illuminated is the anti-WYSIWYG editor - Illuminate your content and get work done.
  <br/><br/>
  <a href="http://metasushi.com/uploads/promo-images/illuminated-field.jpg" class="thickbox"><img src="http://metasushi.com/uploads/promo-images/illuminated-field.jpg" width="75%" /></a>
</p>

<strong>Features</strong>

<p>Features output parsing to make entering content a bit easier:</p>

<ul>
  <li>&#123;page_url:url_title&#125; will be replaced with the full URL to any page</li>
  <li>&#123;image:filename.jpg&#125; will be replaced with a full image tag</li>
  <li>&#123;image_src:filename.jpg&#125; will be replaced with the full URL to the image</li>
</ul>

<b>Links</b>
<ul>
  <li><a href="http://devot-ee.com/add-ons/illuminated">Purchase and Documentation on Devot:ee</a></li>
</ul>
]]></description>
      <dc:subject><![CDATA[]]></dc:subject>
      <dc:date>2012-01-19T17:18:18+00:00</dc:date>
    </item>

    <item>
      <title><![CDATA[Easy custom plugins]]></title>
      <link>http://metasushi.com/site/easy-custom-plugins</link>
      <guid>http://metasushi.com/site/easy-custom-plugins#When:03:14:07Z</guid>
      <description><![CDATA[<div class="box">
  <p><strong>Edit: </strong>I've recently (1/17/2011) given a short talk on this at the <a href="http://twitter.com/TwinCitiesEE">@TwinCitiesEE</a> user group. Slides from that talk are <a href="http://airways.github.com/simple-plugins.html">here</a>. Read on for more detailed discussion of the topic.</p>
</div>

<p>Imagine a scenario where you have some special data that you wish to query out of either built in ExpressionEngine tables or out of custom tables added by a third-party package.</p>

<p>Usually what most people would do at this point is resort to using the query plugin to obtain data from a custom query. However, I want to show you a better way to achieve the same kind of access to data in internal tables, but in a much safer and more controllable way.</p>

<p>First a little about why the query plugin isn't such a great idea in this situation. Generally, when we need to query tables in a MySQL database, we need to pass in some information that we have been given by the user or at least which has passed through the user's machine in the form of a link in a URL segment, or in a value posted to a form. For instance, you may want to get some custom member information based on a member ID. You could do this using the Query plugin like in the following example.</p>

<p>Imagine we have a template group named account and a template named member_info which performs this custom query. The root URL of our example site site is <strong>http://www.example.com/</strong>.</p>

<div class="box">
  <p>URL: <strong>http://www.example.com/account/member_info/45</strong><br />
    Template: <strong>account/member_info</strong></p>
</div>

<pre class="brush: xml">
&lt;h2&gt;Member Info&lt;/h2&gt;
&#123;exp:query sql="SELECT * FROM exp_members WHERE member_id=&#123;segment_3&#125;"&#125;
	&nbsp; Screen Name: &#123;screen_name&#125;&lt;br/&gt;
	&nbsp; Registered Email Address: &#123;email&#125;&lt;br/&gt;
	&nbsp; Username: &#123;username&#125;&lt;br/&gt;
	&nbsp; &lt;a href="&#123;site_url&#125;account/edit/&#123;segment_3&#125;"&gt;Edit&lt;/a&gt;
&#123;/exp:query&#125;
</pre>

<p>This query is incredibly dangerous for a number of reasons.</p>

<p>First, imagine that we visited the following URL:</p>

<div class="box"><p><strong>http://www.example.com/account/member_info/0 or member_id &gt; 1</strong></p></div>

<p>This may not look like a valid URL and you may expect ExpressionEngine to filter out this kind of request, but it is a valid URL and there is no filtering which can stop this. This request would show us all of the member information in the system.</p>

<p>Of course in this case we could just as easily use the current member's logged in ID - and this would be a very good way to fix this particular type of request, however most use of the Query plugin are quite a bit more complicated than this and cannot be so easily patched.</p>

<p>The alternative approach that I would like to recommend is to create a custom plugin that is designed to perform this exact query, and which constructs it not with SQL but instead with the CodeIgniter Active Record class.</p>

<p>The first step to create a new plugin is to create a package directory in the <strong>system/expressionengine/third_party</strong> folder with the name you wish to give the plugin. For instance, we might want to call this plugin "member_info", in which case we would create this directory, which must be in all lowercase:</p>

<div class="box"><p><strong>system/expressionengine/third_party/member_info</strong></p></div>

<p>Inside of this directory, we then need to create a PHP file with a special prefix so that ExpressionEngine knows that it is a plugin. This filename must also be in all lowercase:</p>

<div class="box"><p><strong>system/expressionengine/third_party/member_info/pi.member_info.php</strong></p></div>

<div class="box"><p><strong>Edit</strong>: You may want to use the awesome <a href="http://pkg.io">pkg.io</a> site to create the initial class file for you.</p></div>

<p>After creating the plugin file, we then put in the boilerplate plugin code. You can use this as the basis of all of your custom querying plugins.</p>

<p>All plugins contain two major items. The first is a $plugin_info array which contains all of the meta data for this plugin. The second is the actual class that contains the plugin's custom logic.</p>

<div class="box"><p><strong>Note:</strong> The name of the class must match the name of the file - except with the first letter capitalize and the rest lowercase.</p></div>

<div class="box"><p>File: <strong>pi.boilerplate.php</strong></p></div>

<pre class="brush: php">
&lt;?php
$plugin_info = array(
    'pi_name'           =&gt; 'Example',
    'pi_version'        =&gt; '1.0',
    'pi_author'         =&gt; 'Your Name',
    'pi_author_url'     =&gt; '',
    'pi_description'    =&gt; 'Exemplifies plugins',
    'pi_usage'          =&gt; &lt;&lt;&lt;USAGE
    How to call this plugin:
    &#123;exp:example:some_query param1="something"&#125;
    &#123;/exp:example:some_query&#125;
USAGE
    );

    class Example &#123;
        public __construct() &#123;
            $this-&gt;EE = &amp;get_instance();
        &#125;

        public function some_query() &#123;
            // Get params from $this-&gt;EE-&gt;TMPL-&gt;fetch_param("param");
            // Get the contents of a tag pair as $this-&gt;EE-&gt;TMPL-&gt;tagdata;
            // Return your results as a string
        &#125;
    &#125;
</pre>

<p>The boilerplate plugin defines a tag named some_query, which you will want to change the name of, delete or add to. The name of your tags is completely up to you, and remember that a plugin can define multiple tags. This is useful to group all of the related custom query code for a particular site into a single file.</p>

<p>To continue our example, we would create the following Member_info plugin in order to replace the unsafe Query plugin call:</p>

<div class="box"><p>File: <strong>system/expressionengine/third_party/member_info/pi.member_info.php</strong></p></div>

<pre class="brush: php">
&lt;?php
	$plugin_info = array(
        'pi_name'        =&gt; 'Member_info',
        'pi_version'        =&gt; '1.0',
        'pi_author'        =&gt; 'Your Name',
        'pi_author_url'        =&gt; '',
        'pi_description'    =&gt; 'Gets member information for the supplied ID',
	    'pi_usage'        =&gt; &lt;&lt;&lt;USAGE
	How to call this plugin:
	&#123;exp:member_info:query member_id="something"&#125;
	&#123;/exp:member_info:query&#125;
USAGE
	);
    
	class Member_info &#123;
	    public __construct() &#123;
	        $this-&gt;EE = &amp;get_instance();
	    &#125;
	
	    public function query() &#123;
	        // Get params fromfetch_param(). Default value is FALSE.
	        $requested_member_id = $this-&gt;EE-&gt;TMPL-&gt;fetch_param("member_id");
	        // Get the contents of the pair tag
            $result = $this-&gt;EE-&gt;TMPL-&gt;tagdata;
    
	        // Variables to be used if we can't find the member
	        $vars = array(
	            'found' =&gt; FALSE
	        );
	        if($requested_member_id) &#123;
	            $query = $this-&gt;EE-&gt;db-&gt;where('member_id', $requested_member_id)-&gt;get('members');
	            // Check that we have at least one row
	            if($query-&gt;num_rows() &gt; 0) &#123;
	                // Replace the default return vars array with the result row
	                $vars = $query-&gt;row_array();
	                $vars['found'] = TRUE;
	            &#125;
	            // Check if request is for their own data, if not we can provide less info:
	            $vars['is_current_user'] = $requested_member_id == $this-&gt;EE-&gt;session-&gt;member_id;
	        &#125;
    
	        // Parse results variables into the output
	        $result = $this-&gt;EE-&gt;TMPL-&gt;parse_variables($result, array($row));
    
	        // Return your results as a string
	        return $result;
	    &#125;
	&#125;
</pre>

<p>Here's how we'll use the plugin:</p>

<div class="box">
	<p>URL: <strong>http://www.example.com/account/member_info/45</strong><br />
      Template: <strong>account/member_info</strong></p>
</div>

<pre class="brush: xml">
	&lt;h2&gt;Member Info&lt;/h2&gt;
	&#123;exp:member_info:query member_id="&#123;segment_3&#125;"&#125;
	    &#123;if found&#125;
	        Screen Name: &#123;screen_name&#125;&lt;br/&gt;
	        &#123;if is_current_user&#125;
	            Registered Email Address: &#123;email&#125;&lt;br/&gt;
	            Username: &#123;username&#125;&lt;br/&gt;
	            &lt;a href="&#123;site_url&#125;account/edit/&#123;segment_3&#125;"&gt;Edit&lt;/a&gt;
	        &#123;/if&#125;
	    &#123;if:else&#125;
	        Invalid request.
	    &#123;/if&#125;
	&#123;/exp:member_info:query&#125;
</pre>

<p>Using a custom plugin with CodeIgniter's Active Record class gives us two main advantages in this example:</p>

<ol>
	<li>The contents of the member_id value are automatically escaped for us by the where() method so that we cannot suffer injection attacks. The previous attack URL will simply print out "Invalid request" now.</li>
	<li>Our query will still work even if the DB prefix is changed since the get() method prefixes it for us automatically (we only passed in "members" rather than "exp_members" like we have to with the query tag).</li>
</ol>

<p>Other advantages are:</p>

<ul>
	<li>We can control the data that is queried with much more flexible conditional statements.</li>
	<li>We can cache the data or stop queries if they have been run too often.</li>
	<li>We have more precise control over the output of values.</li>
	<li>We can more easily filter and modify the parameters sent in to query the database.</li>
	<li>We can more easily provide default values (such as date ranges).</li>
</ul>

<p>Once you get used to this technique it actually becomes just as easy as using the built in query plugin, and provides much more security and flexibility for very little time investment. I'd highly recommend trying it the next time you need to create a custom query of any kind on a project!</p>
]]></description>
      <dc:subject><![CDATA[]]></dc:subject>
      <dc:date>2012-01-12T03:14:07+00:00</dc:date>
    </item>

    <item>
      <title><![CDATA[Channel file handling for ExpressionEngine]]></title>
      <link>http://metasushi.com/site/channel-file-handling-for-expressionengine</link>
      <guid>http://metasushi.com/site/channel-file-handling-for-expressionengine#When:16:41:10Z</guid>
      <description><![CDATA[<p>
	I&#39;ve been thinking about the way that file handling is done in ExpressionEngine 2.0 lately. I think I&#39;ve come up with a design that would be a lot easier to work with and provide a lot of additional options beyond what we have right now. Basically the idea boils down to this:</p>
<p>
	<em><strong>Files should be stored as channel entries.</strong></em></p>
<p>
	Actually, the idea is pretty obvious just from looking at the UI of ExpressionEngine 2.0. The Files listing is even put under the Content menu item, and has a very similar look to the listing of channel entries - but where it breaks down with the current implementation is the backend. Files aren&#39;t stored as channel entries, even though the UI implies that they would be.</p>
<p>
	Imagine being able to create multiple <strong><em>File Channels</em></strong>, each one with it&#39;s own meta data fields created as Channel Fields. Relationship fields like Playa would allow more complex relationships between files and the content that uses them. Every existing custom field type could be used to provide really advanced meta data for file uploads. The possibilities are endless.</p>
<p>
	Basically this implementation would need two things:</p>
<ol>
	<li>
		Create a simple File Upload field that is available in all channels. Create a Channel Preference that shows or hides this field. Each channel with this option enabled becomes what we currently know as a "File Upload Preference".</li>
	<li>
		Re-work file management UI (including the built in File upload field type) to use this new data backend instead of the current files tables and File Upload Preferences.</li>
</ol>
<p>
	So that&#39;s my idea. Your thoughts?</p>
]]></description>
      <dc:subject><![CDATA[]]></dc:subject>
      <dc:date>2012-01-06T16:41:10+00:00</dc:date>
    </item>

    <item>
      <title><![CDATA[New metasushi.com add-on website launched!]]></title>
      <link>http://metasushi.com/site/new-metasushi.com-add-on-website-launched</link>
      <guid>http://metasushi.com/site/new-metasushi.com-add-on-website-launched#When:01:49:17Z</guid>
      <description><![CDATA[<p>
	Just in time for the new year, I&#39;m finally launching the all new <strong>metasushi.com</strong>!</p>
<p>
	I&#39;m planning a series of blog posts with tutorial content on ExpressionEngine as well as my own add-ons. Stay tuned!</p>
]]></description>
      <dc:subject><![CDATA[]]></dc:subject>
      <dc:date>2012-01-01T01:49:17+00:00</dc:date>
    </item>

    
    </channel>
</rss>
