Choice is not an illusion

n: smd_multi_choice | v: 0.20 | f: Development / Textpattern plugins

Documentation for the Textpattern plugin smd_multi_choice by Stef Dawson follows this short message from our sponsor ;-)

Plugin list buttonPlugin download buttonCompressed plugin download button

smd_multi_choice

A generic ‘if condition’ tester that can take action if some article, file, link, URL, <txp:variable /> or global PHP variable matches one or more tests. The difference between this and smd_if is that you do not need to nest successive tests because it performs comparisons using a switch... case construct.

Features

  • Supports all major article, file, and link variables such as section, category, custom fields, id, query string, author, body, excerpt, yahde yahde, plus checking url vars, server vars, txp and PHP vars
  • Tests include equality, less than, greater than, divisible by, begins, ends, contains, and ‘character’ tests such is isalpha, isnum, etc
  • Tests can either terminate when a match is found, or “fall through” to the next test (“AND” logic)
  • Multiple tests can have the same outcome (“OR” logic)
  • The tested field & matched value are available to the case container so you can display them
  • Default condition in the event no test cases match

Author

Stef Dawson.

Installation / Uninstallation

Download the plugin from either textpattern.org, or the software page, paste the code into the TXP Admin -> Plugins pane, install and enable the plugin. Visit the forum thread for more info or to report on the success or otherwise of the plugin.

To uninstall, simply delete from the Admin -> Plugins page.

Usage

The plugin gives two tags which must be used in tandem. Use <txp:smd_switch /> to tell the plugin which item you are interested in comparing against and then use one or more <txp:smd_case /> tags inside the switch to take action depending on the value.

Use the tags in any page, form or article context. Can also be used inside file, image (in the future) or link lists to take action depending on attributes of the current item.

smd_switch

At the place you wish to compare a field with a set of values, put this container tag.

Attributes

  • item : the thing you are interested in. It could be the result of another tag-in-tag, a fixed string(!), or a variable if you prefix the item with ?. Example variables are ?article_image, ?url_title, ?my_txp_var, ?my_url_var, and so on
  • look_in : list of places to look for the item. Default: SMD_ALL which checks all items in the following list, in order. You should never need to change this, but you may choose from any of the following and comma-separate each one you wish to consider:
    • txpvar : a <txp:variable />
    • svrvar : something in $_SERVER
    • file : variable from the current file in a file_download_list
    • image : variable from the current image in an image_list
    • link : variable from the current link in a linklist
    • gbl : variable from the global article context (a.k.a. $pretext)
    • article : variable from the current article
    • urlvar : something in $_GET or $_POST
    • phpvar : something in the global scope
  • leave_empty : in most cases if a variable is not set you will want it to remain that way so your switch will do nothing. But you may decide that you want the variable name itself to become the item, i.e. if you used item="?quantity" and there was no ‘quantity’ variable anywhere, it would compare against the actual word ‘quantity’. This may be useful when specifying defaults
  • var_prefix : if you are nesting smd_switch tags you’ll find that any inner {smd_mc_value} replacements will take on the values of the outer tag. For this reason you can specify a prefix that will be used in all replacements. Thus if you set var_prefix="in_" on your inner smd_switch tag, you would use {in_mc_value} and {in_mc_item} in the inner container/form. Default: smd_
  • debug : set to 1 to turn on diagnostic info for all contained smd_case tags

smd_case

Inside your <txp:smd_switch> container put one or more <txp:smd_case /> tags to test the item against each successive value. Case tags are usually containers that will execute the enclosed content if the value matches the item, but see example 4 for use as a self-closing tag.

Attributes

  • value : the value you want to check against. It can either be a fixed value or another field in the current context (see the <txp:smd_switch /> tag’s item attribute for info). Default: unset
  • type : the type of test you want to perform:
    • eq : Equality (the default)
    • gt : item is greater than value
    • ge : item is greater than or equal to value
    • lt : item is less than value
    • le : item is less than or equal to value
    • divisible : item is wholly divisible by value1
    • begins : item starts with value
    • contains : item has value somewhere within its contents
    • ends : item ends with value
    • isnum : value is made up entirely of digits
    • isalpha : value is made up entirely of alphabetic characters
    • isalnum : value is made up entirely of alphanumeric characters
    • islower : value is made up entirely of lower case characters2
    • isupper : value is made up entirely of upper case characters2
    • ispunct : value is made up entirely of punctuation characters
    • isspace : value is made up entirely of ‘whitespace’ characters
    • empty : item is empty (i.e. has no content) — similar to value=""
  • look_in : exactly the same options as for smd_switch except they apply to the value attribute
  • leave_empty : exactly the same behaviour for smd_switch except it applies to the value attribute
  • case_sensitive : whether you wish to take case into account when compare item and value; 1=yes, 0=no. Default: 0
  • numeric : whether you wish to compare the values numerically (1) instead of alpha-numerically (0). Useful when comparing numbers because ‘f’ is actually greater than ‘1’ in the ASCII table. Default: 0. Note that a numeric test is implied if using type="divisible"
  • fallthru : under normal circumstances, when one of the tests matches any further tests are ignored. Thus the order of the tests is important. Sometimes you may wish to take more than one action, e.g. if the item is greater than value1 then display something, and also display something else if it begins with value2. This type of “AND” condition is achieved by setting fallthru="1". In the event the case matches this condition, the next case tag will still be checked. For “OR” conditions, see example 4
  • default : inside any switch, one and only one of your case tags can be designated a ‘default’ by setting this attribute to 1. This default branch will be taken if none of the preceding case tags match anything. It is usually the last tag in a switch. Default: 0
  • debug : set to 1 to show diagnositc information for this case tag only. Default: 0

1 This test forces ‘numeric’ on automtically. Also, 0 is regarded as not wholly divisible by any value

2 case_sensitive is automatically set to 1 for these tests

Replacement variables

Some replacement variables are available within the <txp:smd_case> container so you may output the matched values directly, for example, in error messages:

  1. {smd_mc_item} (or {smd_switch}): the value of the switch item being looked at
  2. {smd_mc_value} (or {smd_case}): the value of the case value that matched the item

When testing more than one value to produce a single outcome, the most recent value that matches is the one assigned to the replacement variable. Note that the smd_ part is the default setting and may be changed with the var_prefix attribute.

Examples

Example 1

On your error_default page you could put this:

<txp:smd_switch item="?txp_error_code">
   <txp:smd_case value="403">
      <p>Forbidden access. Code: 403</p>
   </txp:smd_case>
   <txp:smd_case value="404">
      <p>Page not found. Code: 404</p>
   </txp:smd_case>
   <txp:smd_case value="500">
      <p>Something went horribly wrong. Code: 500</p>
   </txp:smd_case>
   <txp:smd_case default="1">
      <p>Whoops! Error code: {smd_mc_item} occurred</p>
   </txp:smd_case>
</txp:smd_switch>

Example 2

Test some value obtained from public-side form input:

<txp:smd_switch item="?quantity" look_in="urlvar">
   <txp:smd_case value="2" type="lt"
        numeric="1">
      <p>Value {smd_switch} is too small</p>
   </txp:smd_case>
   <txp:smd_case value="20" type="gt"
        numeric="1">
      <p>Value {smd_switch} is too large</p>
   </txp:smd_case>
</txp:smd_switch>

Example 3

Building on example 2, this runs the 2nd and 3rd tests regardless of whether the 2nd one returned true. It also adds a check to see if the quantity URL variable is set at all and issues a warning of not (an empty <txp:smd_case> is the same as <txp:smd_case value="">)

<txp:smd_switch item="?quantity" look_in="urlvar">
   <txp:smd_case>
      Please supply a quantity<br />
   </txp:smd_case>
   <txp:smd_case value="2" type="lt"
        numeric="1">
      Value {smd_switch} is too small<br />
   </txp:smd_case>
   <txp:smd_case value="20" type="gt"
        numeric="1" fallthru="1">
      Value {smd_switch} is too large<br />
   </txp:smd_case>
   <txp:smd_case value="100" type="gt"
        numeric="1">
      ... and out of this world!<br />
   </txp:smd_case>
   <txp:smd_case default="1">
      Everything's fine
   </txp:smd_case>
</txp:smd_switch>

Thus, you see the following if you add these values to the address bar:

  • ?quantity=1 : Value 1 is too small
  • ?quantity=15 : Everything’s fine
  • ?quantity=25 : Value 25 is too large
  • ?quantity=125 : Value 125 is too large… and out of this world!

Example 4

Sometimes you may want to perform the same action for a number of matches. For these instances you may use the <txp:smd_case /> tag in its Single form. List all the cases that you want to have the same outcome, then use the container of the last one to perform the action if any of the preceding cases matched.

<txp:smd_switch item="?custom1">
   <txp:smd_case value="3" type="divisible" />
   <txp:smd_case value="7" type="divisible" />
   <txp:smd_case value="9" type="divisible">
     {smd_switch} is divisible by 3, 7 or 9
   </txp:smd_case>
   <txp:smd_case value="5" type="ends" />
   <txp:smd_case value="0" type="ends">
     Yay! Numbers ending in 5 or 0 win a teddy.
   </txp:smd_case>
</txp:smd_switch>

If you add fallthru="1" to the value="9" case tag, both ‘divisible’ and ‘teddy bear’ sets of conditions would be tested and you’d see varying combinations of the output based on the contents of custom1.

Example 5

This one checks that the URL variable qty is within the range of a stock custom field. This one uses a nested smd_switch tag. The first (outer) switch checks if the value is numeric and then uses a second switch to compare its value to the stock level in the custom field. If the outer switch detects the value is not numeric, the default case fires to inform the visitor of the error.

<txp:smd_switch item="?qty">
   <txp:smd_case type="isnum">
      <txp:smd_switch item="?qty" var_prefix="inner_">
         <txp:smd_case value="0">
            Please select a quantity to order
         </txp:smd_case>
         <txp:smd_case value="?stock" type="le">
            There are enough of those in stock. Place order?
         </txp:smd_case>
         <txp:smd_case default="1" value="?stock">
           There are only {inner_mc_value} in stock. Please choose a lower number
         </txp:smd_case>
      </txp:smd_switch>
   </txp:smd_case>
   <txp:smd_case default="1" value="?stock">
      Sorry, "{smd_mc_item}" is not valid input. Please choose a valid order quantity.
   </txp:smd_case>
</txp:smd_switch>

So if someone enters ?qty=0 or ?qty=fred then a special message is shown to prompt them to enter a valid number. If the qty variable is lower than the stock level of the current article then all is well. But if any other value is used, the default condition of the inner smd_switch kicks in and tells the visitor that there are not enough to satisfy their ravenous needs.

A couple of other things to note:

  • the inner switch uses the var_prefix attribute so the variable can be displayed correctly to the visitor without clashing with the value in the outer switch. In this case the switch values happen to be the same so it doesn’t really matter, but if you were testing a separate variable then it might
  • the default cases both use value="?stock" even though they would appear not to be strictly necessary. They are used so the replacement variables {smd_mc_item} and {inner_mc_value} can be inserted in the output. If you omit the value attribute then the replacements are not ‘primed’ because the case tag does not know where to look for its value

Changelog

  • 10 Apr 09 | 0.10 | Initial release
  • 01 Aug 10 | 0.20 | Added empty type (thanks gomedia) ; added var_prefix ; fixed default case firing unnecessarily ; fixed problem with multiple switch tags on a page

Source code

If you’d rather wander aimlessly through thousands of lines of PHP source code, you’ll need to step into the view source page.

Legacy software

If, for some inexplicable reason, you need last century's version of a plugin, it can probably be found on the plugin archive page.

Experimental software

If you’re feeling brave, or fancy chucking your keys in the bowl, you can test out some of my beta code. It can be found on the plugin beta page.