Skip to content

aitorgr/radiant-backdoor

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

= Back Door, execute Ruby code directly inside Radiant templates

==Intro

Back door is a Radiant extension that allows executing Ruby code directly
inside Radiant templates. It provides the following main tags: <r:ruby>, <r:erb>,
<r:if>, <r:else>, <r:unless>, <r:tag> and <r:erb_tag>.

The objective of this extension is to avoid writing Radiant extensions for easy
tasks.  If you just need some Ruby/ERB code inside templates, this extension
is for you.  If you want to implement Radius tags, modify the admin interface or
define some other complex behavior you should revert to writing a complete Radiant
extension.

Download::          git://github.com/aitorgr/radiant-backdoor.git

Project home::      https://github.com/aitorgr/radiant-backdoor
Current version::   0.4.2
Author::            aitor.name[http://aitor.name]

Please discuss, report bugs and comments to the Radiant user's mailing-list[http://radiantcms.org/mailing-list]
or contact the author directly at me(at)aitor.name.

Thanks to RubyForge for hosting this project.

== Usage

This extension adds the following new tags:

=== <r:ruby>

Executes the content of the tag body as Ruby code and renders the returned value.

*Usage:*

   <r:ruby> [ruby code] </r:ruby> 

*Example:*

   With this fine extension you have access to all this information: <r:ruby> self.inspect </r:ruby> 
   This machine identifies itself as <r:ruby> `uname -a` </r:ruby> 
  
=== <r:erb>

Executes the content of the tag body as ERB code and renders the returned value.

*Usage:*

   <r:erb> [ERB template] </r:erb> 

*Example:*

  
  <r:erb>
    <table>
      <tr>
        <th>name</th> <th>password</th> <th>uid</th> <th>gid</th> <th>class</th> <th>home_dir</th> <th>shell</th>
      </tr>
      <%  File.open( "/etc/passwd") do |io|
            while line = io.gets
              next if line =~ /^\s*#/
              fields = line.split( ":")
      %>
              <tr>
               <% fields.each do |field| %>
                <td> <%= field %> </td>
              <% end %>
              </tr>
      <%    end
          end
      %>
    </table>
  </r:erb>
  

*Caveats:*

When using ruby looping constructs inside a ERB template, the Radius tags present in the template get expanded
only once, and then "repeated" multiple times by the Ruby code.  For example, the following code:

  <r:erb>
    <% 5.times do %>
      <%= <r:cycle values="1, 2, 3, 4, 5"/> %>
    <% end %>
  </r:erb>

returns "11111", though intuitively it should return "12345" (ignoring whitespace).  This is because the <r:cycle>
tag gets expanded only once.

Nearly always this is irrelevant, since Radiant tags are all side effect free, that is, they always return the same value
when called multiple times in the same context.  Notable exceptions are the <r:cycle> and <r:random> tags.

For the case of tags with side effects, use the <r:expand> tag as documented in the description for that tag.
  
=== <r:expand>

When used inside an "r:erb" tag, it expands the tag named in the "tag" attribute with the given attributes.

This tag avoids the "tag only expands once inside ruby loops" problem described in the "r:erb" tag.  It takes a required
"tag" attribute that identifies an existing tag, and expands that tag passing it the rest of attributes.

NOTE that this tag must be used inside an ERB <%= %> construct.

*Usage:"

   <r:expand tag="tag-name" [ tag-name-attribute="value"]* /> 

*Example:"

  <r:erb>
    <% <r:cycle values="1, 2, 3, 4, 5" reset="true"/> %>
    <% 5.times do %>
      <%=
      <r:expand tag="cycle" values="1, 2, 3, 4, 5"/>
      %>
    <% end %>
  </r:erb>

renders "23451" (ignoring whitespace)
  
=== <r:if>

Renders the tag body if the given Ruby expression evaluates to true. If not it renders an inner "r:else" tag, if present. Note that there are some caveats regarding the "r:else" tag, look the description of if for more information.

*Usage:*

   <r:if cond="[ruby expression]"> [HTML content] </r:if> 
   <r:if cond="[ruby expression]"> [HTML content] <r:else> [HTML content] </r:else> </r:if> 

*Example:*

  
  <r:if cond="request.env[ 'HTTP_USER_AGENT'] =~ /MSIE/">
    <!-- Internet Explorer needs some ugly hacks to render PNG images with transparency -->
    <div id="logo" style="filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src=\'/images/logo.png\',sizingMethod=\'scale\');"></div>
    <r:else>
      <!-- the rest of browsers just make it rigth -->
      <img src="/images/logo.png" id="logo" alt="Logo"/>
    </r:else>
  </r:if>
  

*Caveats:*

There are two caveats with the "r:if/unless" and "r:else" tags (used "r:if" as example, but equally applicable to "r:unless"):

* to allow the expansion of an "r:else" part inside a "r:if" tag, the containing "r:if" tag must be
  expanded even if the condition of the "r:if" tag evaluates to false.  In this case, the body of the
  "r:if" tag is ignored.  This is not a problem unless the body of the "r:if" tag has "side effects":

    
    <r:ruby>
      @counter = 0
      ""
    </r:ruby>
    <r:if cond="false">
      This text is evaluated and ignored, but the following "ruby" tag has a side effect that affects the "else" tag
      <r:ruby> @counter += 1 </r:ruby>
      <r:else> <r:ruby> @counter </r:ruby> </r:else>
    </r:if>
    

* the rendered value of the "r:if" tag contains also any data after an "r:else" tag:

    
    <r:if cond="true">
      Hello
      <r:else> Hello Radiant! </r:else>
      world!
    </r:if>
    

  evaluates to "Hello world!" (ignoring whitespace)
  
=== <r:unless>

The inverse of the "r:if" tag.  Refer to the description of the "r:if" tag for complete documentation.
  
=== <r:else>

Specifies the "else" part for "r:if" and "r:unless".  Refer to the description of the "r:if" tag for complete documentation.
  
=== <r:tag>

Defines a new tag, which can subsequently be used as a normal Radius tag.  It can be seen as a programmable and parametrized snippet.

The new tag receives the tag context with the standard name "tag" (see the example below).  Note that this tag must be rendered by
Radiant before the tag it defines is used.  This typically means that this tag must be used early in a page layout.

*Usage:*

 

  <!-- define a new tag -->
  <r:tag name="tag-name">
    [Ruby code]
  </r:tag>

  <!-- use the new defined tag -->
  <r:tag-name [params]>
    [content]
  </r:tag-name>



*Example:*



  <!-- define a new tag -->
  <r:tag name="originating_ip">
    @request.remote_ip
  </r:tag>

  <!-- use it -->
  Article posted from IP <r:originating_ip />


  
=== <r:erb_tag>

Same functionality as the "tag" tag but the body of the tag is interpreted as ERB code.  Handy for heavy parametrized templating.

*Usage:*

 

  <!-- define a new tag -->
  <r:tag name="tag-name">
    [Ruby code]
  </r:tag>

  <!-- use the new defined tag -->
  <r:tag-name [params]>
    [content]
  </r:tag-name>



*Example:*



  <!-- define a new tag -->
  <r:erb_tag name="article">
    <div class="<%= tag.attr[ "class"] || "article" %>">
      <div class="article-title"> <%= tag.attr[ "title"] %> </div>
      <div class="article-body"> <%= tag.expand %> </div>
      <div class="article-footer"> Posted in <page/> </div>
    </div>
  </r:erb>

  <!-- use it -->
  <article title="New BackDoor release">
    Blah, blah, blah
  </article>


  


== Attribute expansion

Back Door allows evaluating tag's attributes as Ruby code.  If a tag's attribute starts with the "=" symbol, the rest of
the attribute is evaluated, and the attribute's value is replaced with the evaluated value.  This works for both standard
Radiant (Radius) tags and Back Door's own tags.

As an example, consider a side-bar where we want to show first and second level pages.  But for the "news" page, we want to
limit the number of children to 3 and add an extra link to see all news.  Without Back Door this could be implemented as:

  <r:children:each>
      <h2 class="menu-heading">
          <r:link>
              <r:title />
          </r:link>
      </h2>
      <r:if_url matches="/news/">
          <r:children:each limit="3" order="desc" by="published_at">
              <h3 class="menu-subheading">
                  <r:link>
                      <r:title />
                  </r:link>
              </h3>
          </r:children:each>
          <h3 class="menu-subheading">
              <r:link> More news... </r:link>
          </h3>
      </r:if_url>
      <r:unless_url matches="/news/">
          <r:children:each order="desc" by="published_at">
              <h3 class="menu-subheading">
                  <r:link>
                      <r:title />
                  </r:link>
              </h3>
          </r:children:each>
      </r:unless_url>
  </r:children:each>

Clearly there is too much duplication.  With Back Door this can be rewritten as:

  <r:children:each>
      <h2 class="menu-heading">
          <r:link>
              <r:title />
          </r:link>
      </h2>
      <r:ruby> @news = tag.locals.page.url =~ /news/; "" </r:ruby>
      <r:children:each limit="=(@news ? 3: 1000)" order="desc" by="published_at">
          <h3 class="menu-subheading">
              <r:link>
                  <r:title />
              </r:link>
          </h3>
      </r:children:each>
      <r:if cond="@news">
          <h3 class="menu-subheading">
              <r:link> More news... </r:link>
          </h3>
      </r:if>
  </r:children:each>

Note how the "limit" attribute of the <r:children:each> tag uses the "=" symbol to make it dynamic.

== Security

None to date.  Arbitrary Ruby code can be executed with this extension.  If you don't trust the people
writing the pages in a Radiant site armed with this extension, simply don't use it.  See the TODO[link:files/TODO.html] file
for more information.

== License

The Back Door extension is available under a MIT style license.

Copyright (c) 2007 Aitor Garay-Romero <me(at)aitor.name>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

About

BackDoor extension for Radiant CMS

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages