Code snippets for symfony 1.x

Navigation

Refine Tags

Snippets tagged "view helper"

Work with IE conditional comments

sometimes you may need to include a stylesheet only for IE, the best effort you give, this box-model gives you headaches and there is no other solution. Same issue for JS files (but it should not happen anymore thanks to prototype).

In these cases you can simply add this in your <head> section

<head>

<?php include_http_metas() ?>
<?php include_metas() ?>

<?php include_title() ?>

<link rel="shortcut icon" href="/favicon.ico" />
<?php include_javascripts() ?>
<?php include_stylesheets() ?>

<!--[if IE 7]>
<?php echo stylesheet_tag('my-css-for-IE7') ?>
<![endif]-->
<!--[if lte IE 6]>
<?php echo stylesheet_tag('my-css-for-IE6-or-lower') ?>
<![endif]-->
</head>

I made a helper for this, so your header will look more like this :

<?php use_helper('IE') ?>
<?php use_ie_stylesheet('my-css-for-IE7', 'version=7') ?>
<?php use_ie_stylesheet('my-css-for-IE6-or-lower', 'version=6 operator=lte') ?>
...
<head>

<?php include_http_metas() ?>
<?php include_metas() ?>

<?php include_title() ?>

<link rel="shortcut icon" href="/favicon.ico" />
<?php include_javascripts() ?>
<?php include_stylesheets() ?>
<?php include_ie_metas() ?>
</head>

Code is not shorter, but it's more "symfony-like" as you don't explicitly write yourself the strange-syntax comments and don't have to remember their (strange) syntax anymore.

The ideal way would be adding a filter inspired by "lib/symfony/sfCommonFilter.class.php" so we would not even have to call the include_* functions ;)

IMPORTANT :

If you do this (using IE helper or not) to OVERRIDE some CSS rules, don't forget that Symfony automagically adds your CSS and JS files just before </head>, so your CSS will be inserted BEFORE all the CSS included via "use_stylesheet()". So you have to call

<?php include_stylesheets() ?>
<?php include_javascripts() ?>

before adding your conditional comments.

And the code of the helper, to be written in "lib/helper/IEHelper.php"

<?php
 
function get_opening_ie_conditional_comments($version = null, $operator = null) {
    return '<!--[if ' . ($version ? $operator.' ' : '') . 'IE' . ($version ? ' '.$version : '') . ']>';
}
 
function get_closing_ie_conditional_comments($version = null, $operator = 'lte') {
    return '<![endif]-->';
}
 
function get_ie_conditional_comments($version = null, $operator = null) {
    return array(
        get_opening_ie_conditional_comments($version, $operator),
        get_closing_ie_conditional_comments($version, $operator)
    );
}
 
function get_ie_metas($type = null) {
    if ($type == 'javascripts' || $type == 'stylesheets') {
        $metas = '';
        $already_seen = array();
        // Stylesheets
        $assets = sfContext::getInstance()->getResponse()->getParameter($type, array(), 'ie');
        foreach ($assets as $asset_info) {
            list($asset, $options) = $asset_info;
            $file = $type == 'stylesheets' ? stylesheet_path($asset) : javascript_path($asset);
            if (isset($already_seen[$file])) {
                continue;
            } else {
                $already_seen[$file] = true;
            }
            // IE version (default : none)
            $version = isset($options['version']) ? $options['version'] : null;
            unset($options['version']);
            // IE version comparison operator (default : "le")
            $operator = isset($options['operator']) ? $options['operator'] : null;
            unset($options['operator']);
            // get corresponding comments
            list($start_comment, $end_comment) = get_ie_conditional_comments($version, $operator);
            $include = $type == 'stylesheets' ? stylesheet_tag($file, $options) : javascript_include_tag($file, $options);
            $metas .= $start_comment . "\n" . $include . $end_comment . "\n";
        }
        return $metas;
    } elseif (is_null($type)) {
        return get_ie_metas('javascripts') . "\n" . get_ie_metas('stylesheets');
    }
}
 
function include_ie_metas() {
    echo get_ie_metas();
}
 
function use_ie_stylesheet($stylesheet, $options = array()) {
    $stylesheets = sfContext::getInstance()->getResponse()->getParameter('stylesheets', array(), 'ie');
    if (!is_array($options)) $options = sfToolkit::stringToArray($options);
    $stylesheets[] = array($stylesheet, $options);
    sfContext::getInstance()->getResponse()->setParameter('stylesheets', $stylesheets, 'ie');
}
 
function include_ie_stylesheets() {
    echo get_ie_stylesheets();
}
 
function get_ie_stylesheets() {
    return get_ie_metas('stylesheets');
}
 
function use_ie_javascript($js, $options = array()) {
    $javascripts = sfContext::getInstance()->getResponse()->getParameter('javascripts', array(), 'ie');
    if (!is_array($options)) $options = sfToolkit::stringToArray($options);
    $javascripts[] = array($javascript, $options);
    sfContext::getInstance()->getResponse()->setParameter('javascripts', $javascripts, 'ie');
}
 
function include_ie_javascripts() {
    echo get_ie_javascripts();
}
 
function get_ie_javascripts() {
    return get_ie_metas('javascripts');
}
by Nicolas Chambrier on 2007-07-27, tagged css  helper  ie  view 
(3 comments)

Complimentary Filters

A common modification to a CRUD module is filtering records on the list page. There are probably several nice ways to do this, including drop-downs, forms, etc. depending on the situation. While some of these approaches are nice, they are often not minimal... We like minimal!

Discussion

Say you have a book table, with a related author and genre. You want to filter the list of books by those related tables with links, paginate your list, and make it sortable via links in the header of the table.

To do this traditionally, you'll need lots of nasty little if statements in your template code, to check for all of these parameters (author_id, genre_id, title, num_pages, page, etc.) and combine them all together into a meaningful uri for your link_to function (something like "book/list?author_id=10&genre_id=5&sort=title&page=5"). We still want that link in the end, but generating it... How about a better way!

Followup Example

<!-- listSuccess.php -->
<?php use_helpers('Filter', 'Pagination') ?>
<h2>Books</h2>
<h3>Filter By</h3>
<table class="filters">
  <tbody>
    <tr>
      <th>Author:</th>
      <td><?php echo filter_navigation(objects_for_filter($authors), 'author_id', $author_id) ?></td>
    </tr>
    <tr>
      <th>Genre:</th>
      <td><?php echo filter_navigation(objects_for_filter($genres), 'genre_id', $genre_id) ?></td>
    </tr>
  </tbody>
</table>
<hr />
<table class="list">
  <thead>
    <tr>
      <th><?php link_to_unless($sort == 'id', 'Id', filter_url('sort', 'id')) ?></th>
      <th><?php link_to_unless($sort == 'title', 'Title', filter_url('sort', 'title')) ?></th>
      <th><?php link_to_unless($sort == 'author_id', 'Author', filter_url('sort', 'author_id')) ?></th>
      <th><?php link_to_unless($sort == 'genre_id', 'Genre', filter_url('sort', 'genre_id')) ?></th>
    </tr>
  </thead>
  <tbody>
    <?php foreach ($pager->getResults() as $book): ?>
      <tr>
        <td><?php echo $book->getId() ?></td>
        <td><?php echo $book->getTitle() ?></td>
        <td><?php echo $book->getAuthor()->getName() ?></td>
        <td><?php echo $book->getGenre()->getName() ?></td>
      </tr>
    <?php endforeach; ?>
  </tbody>
</table>
<?php echo pager_navigation($pager) ?>

NOTE: pager_navigation is covered at http://www.symfony-project.com/snippets/snippet/4, but needs to guess the uri, which is done at http://www.symfony-project.com/snippets/snippet/59.

NOTE: filter_navigation returns an unordered list, so you'll need css to display the <li> tags inline, and remove <ul> padding, margin, etc.

I'll leave it as an exercise to the reader to create the controller (action) code for this template, but it should be obvious.

The Code

<?php
 
/**
 * Generate a url using the current internal uri, but replaces a param with a new value.
 * If the param is not in the current uri's query string, it is added instead.
 *
 * This is useful for a page that uses several filters to record sets,
 * and needs all the filters to work together, instead of blasting each
 * other away when a new link is clicked.
 *
 * <strong>Examples:</strong>
 * <code>
 *  // with current uri => mymodule/myaction?author=10&genre=3
 *
 *  echo link_to('new author', filter_url('author', 5));
 *  // uri when clicked => mymodule/myaction?author=5&genre=3
 *
 *  echo link_to('new genre', filter_url('genre', 1));
 *  // uri when clicked => mymodule/myaction?author=10&genre=1
 *
 *  // with current uri => mymodule/myaction
 *
 *  echo link_to('an author', filter_url('author', 10));
 *  // uri when clicked => mymodule/myaction?author=10
 * </code>
 *
 * @param string the name of the parameter to replace
 * @param string the value to replace the current value with
 * @param boolean use route name
 * @return string the url with the parameter replaced
 * @see link_to
 */
function filter_url($param, $new_value, $with_route_name = false)
{
    // fetch params from query string
    $params = _get_params(_get_query_string());
 
    // replace param with new value
    $params[$param] = $new_value;
 
    return _get_uri($with_route_name) . '?' . _build_query_string($params);
}
 
/**
 * Removes a parameter from the current uri and returns the resulting url.
 *
 * @see filter_url
 */
function remove_filter_url($param, $with_route_name = false)
{
    // fetch params from query string
    $params = _get_params(_get_query_string());
 
    // remove param
    unset($params[$param]);
 
    return _get_uri($with_route_name) . '?' . _build_query_string($params);
}
 
/**
 * Generates an unordered list of links to filter the current record set by.
 * Multiple sets of filter_navigation links will work together, using the current uri.
 *
 * <strong>Examples:</strong>
 * <code>
 *  echo filter_navigation(array(10=>'Jones', 12=>'Smith, J.', 13=>'Darby'), 'author_id', 13);
 *  echo filter_navigation(objects_for_filter($authors), 'author_id', 13);
 * </code>
 *
 * @param array list of key=>value pairs of ids and strings
 * @param string the name of the parameter for this filter
 * @param string the selected id (or null, if none selected)
 * @param string the text to use for the "all" link
 * @see filter_url
 */
function filter_navigation($list, $param, $selected = null, $all_text = 'All')
{
    $html = '';
 
    $html .= content_tag('li', link_to_unless($selected === null, $all_text, remove_filter_url($param)));
    foreach ($list as $key => $value)
    {
        $html .= content_tag('li', link_to_unless($selected == $key, $value, filter_url($param, $key)));
    }
 
    return content_tag('ul', $html);
}
 
/**
 * Generates a simple list from a record set of propel objects.
 * Expects a getId function and a toString function.
 *
 * @param array objects to be converted to a list
 * @see filter_navigation
 */
function objects_for_filter($objects)
{
    $list = array();
 
    foreach ($objects as $object)
    {
        $list[$object->getId()] = $object->toString();
    }
 
    return $list;
}
 
function _get_uri($with_route_name = false)
{
    $internal_uri = sfRouting::getInstance()->getCurrentInternalUri($with_route_name);
    $ar = explode('?', $internal_uri);
 
    return ($with_route_name ? '@' : '') . $ar[0];
}
 
function _get_query_string()
{
    $internal_uri = sfRouting::getInstance()->getCurrentInternalUri();
    $ar = explode('?', $internal_uri);
 
    return isset($ar[1]) ? $ar[1] : '';
}
 
function _get_params($query_string)
{
    // parse query string into associative array
    $params = array();
    if ($query_string != '')
    {
        foreach (explode('&', $query_string) as $kvpair)
        {
            list($key, $value) = explode('=', $kvpair);
            $params[$key] = $value;
        }
    }
 
    return $params;
}
 
function _build_query_string($params)
{
    // build list of key=value strings
    $ar = array();
    foreach ($params as $key => $value)
    {
        $ar[] = $key . '=' . $value;
    }
 
    return implode('&', $ar);
}

NOTE: Place this in apps/myapp/lib/helper/FilterHelper.php.

Additional Usage Examples

<?php echo link_to('new author', filter_url('author_id', 10)) ?>
<?php echo url_for(filter_url('author_id', 10)) ?>
<?php echo link_to_if($condition, 'new author',  filter_url('author_id', 10)) ?>
<?php echo link_to_unless($condition, 'new author',  filter_url('author_id', 10)) ?>
<?php echo button_to('new author',  filter_url('author_id', 10)) ?>

Additional Helpers

<?php
 
/**
 * Shortcut combining link_to and filter_url into single function.
 *
 * @see link_to
 * @see filter_url
 */
function link_to_filter($name, $param, $new_value, $options = array())
{
    return link_to($name, filter_url($param, $new_value), $options);
}
 
/**
 * Shortcut combining url_for and filter_url into single function.
 *
 * @see url_for
 * @see filter_url
 */
function filter_url_for($param, $new_value)
{
    return url_for(filter_url($param, $new_value));
}
 
/**
 * Shortcut combining link_to_if and filter_url into single function.
 *
 * @see link_to_if
 * @see filter_url
 */
function link_to_filter_if($condition, $name, $param, $new_value, $options = array())
{
    return link_to_if($condition, $name, filter_url($param, $new_value), $options);
}
 
/**
 * Shortcut combining link_to_unless and filter_url into single function.
 *
 * @see link_to_unless
 * @see filter_url
 */
function link_to_filter_unless($condition, $name, $param, $new_value, $options = array())
{
    return link_to_unless($condition, $name, filter_url($param, $new_value), $options);
}
 
/**
 * Shortcut combining button_to and filter_url into single function.
 *
 * @see button_to
 * @see filter_url
 */
function button_to_filter($name, $param, $new_value, $options = array())
{
    return button_to($name, filter_url($param, $new_value), $options);
}
by Stephen Riesenberg on 2007-02-03, tagged css  filter  helper  pager  pagination  template  view 

Additional slot helpers

I recently had several template fragments which each had some conditional css that needed to be defined in the template itself. This is a perfect application for slots.

However, since there were several of these fragments/partials being executed, I would have to use several slots. That seemed unnecessary. Consider the following:

Example 1

/* _fragment.php */
<?php append_to_slot('style') ?>
<style>
.class {
  font-size: 12px;
}
</style>
<? end_slot() ?>

Example 2

/* _anotherfragment.php */
<?php append_to_slot('style') ?>
<style>
  .anotherclass {
    <?php if ($bool): ?>
      font-size: 24px;
    <?php else: ?>
      font-size: 26px;
    <?php endif; ?>
  }
</style>
<?php end_slot() ?>

Those were just an example, so don't laugh. They do illustrate, however, the use of a helper which continually appends your content to the existing slot, which may or may not already contain content; in this case the "style" slot.

Then of course, you can use the slot in your layout.php (in the head tag):

...
<head>
...
<?php if (has_slot('style')) echo get_slot('style') ?>
</head>

Note: while the append_to_slot function can be ended by either end_slot() or end_append_to_slot(), the prepend_to_slot function must be ended by end_prepend_to_slot()!

The code

<?php
 
/**
 * Begins the capturing of a slot,
 * but echoes the current value of a slot with the same name,
 * if one exists, before capturing the rest of the slot content.
 *
 * <strong>Example</strong>
 * <code>
 *  // first myslot
 *  slot('myslot');
 *  echo 'something to put in "myslot"';
 *  end_slot();
 *  // second myslot
 *  append_to_slot('myslot');
 *  echo 'some stuff to append to existing "myslot" slot';
 *  end_slot();
 * </code>
 *
 * @param   string slot name
 * @return  void
 * @see     end_append_to_slot, slot, end_slot
 */
function append_to_slot($name)
{
    // check for the existence of a slot with the same name
    $content = '';
    if (has_slot($name)) $content = get_slot($name);
 
    // regardless, begin capturing the slot
    slot($name);
 
    // echo either a blank string, or the previous value of the slot with the same name
    echo $content;
}
 
/**
 * Stops the content capture and save the content in the slot.
 * This function is interchangeable with end_slot, and is not necessary.
 * It is useful only for consistency's sake.
 *
 * @return  void
 * @see     append_to_slot, slot, end_slot
 */
function end_append_to_slot()
{
    // simply end the current slot
    end_slot();
}
 
/**
 * Begins the capturing of a slot,
 * but saves the current value of a slot with the same name,
 * if one exists, and then proceeds to capture the slot content.
 *
 * @param   string slot name
 * @return  void
 * @see     end_prepend_to_slot, slot, end_slot
 */
function prepend_to_slot($name)
{
    $context = sfContext::getInstance();
    $response = $context->getResponse();
 
    // check for the existence of a slot with the same name
    $content = '';
    if (has_slot($name)) $content = get_slot($name);
 
    // save the current slot value in a temporary location
    $response->setParameter('old_slot', $content, 'symfony/view/sfView/slot');
 
    // begin capturing the slot
    slot($name);
}
 
/**
 * Retrieves the old value of a slot which was backed up in prepend_to_slot,
 * if one exists, and echoes this value,
 * and then proceeds to stop the content capture and save the content in the slot.
 *
 * @return  void
 * @see     prepend_to_slot, slot, end_slot
 */
function end_prepend_to_slot()
{
    $context = sfContext::getInstance();
    $response = $context->getResponse();
 
    // retrieve the old value for this slot
    $content = $response->getParameter('old_slot', '', 'symfony/view/sfView/slot');
    // remove the old value
    $response->getParameterHolder()->remove('old_slot', 'symfony/view/sfView/slot');
 
    // echo the old value of the slot, or a blank string if no slot existed
    echo $content;
 
    // end the current slot
    end_slot();
}

Remember: you can place this in myproject/apps/myapp/lib/helper/PartialPlusHelper.php

Then you can use it with <?php use_helper('PartialPlus') ?>

by Stephen Riesenberg on 2006-12-16, tagged helper  template  view 

Template fragments, cont.

I'm not sure how useful these will be, but I came up with the idea, so I figured I'd post them as well.

Example

<table>
  <?php foreach ($vars as $key => $value): ?>
    <tr>
      <th><?php include_fragment('label', array('id' => $key, 'object' => $value))) ?></th>
      <td><?php include_fragment('input', array('id' => $key, 'object' => $value))) ?></td>
    </tr>
  <?php endforeach; ?>
</table>

These helpers use the fragment idea from http://www.symfony-project.com/snippets/snippet/124 and take it a step further. There's a difference here, however, that's important to note:

Note: Unlike partials, these fragments will have absolutely no variables available, unless explicitly passed in. Templating variables provided by symfony will not be available, due to php's scope handling when inside of functions. That's why my previous snippet used include(fragment_name('myfragment')) instead, which gave the fragment access to all currently in-scope variables.

The code

<?php
 
/**
 * Includes a template fragment.
 * This function is similar in nature to include_partial,
 * but does not use sfView, and therefore has virtually no overhead.
 * 
 * <strong>Example</strong>
 * <code>
 *  include_fragment('mypartial');
 * </code>
 * 
 * @param   string fragment name
 * @return  void
 * @see     include_partial, get_fragment, fragment_path, fragment_name
 */
function include_fragment($templateName, $vars = array())
{
    echo get_fragment($templateName, $vars);
}
 
/**
 * Evaluates and returns a template fragment.
 * The syntax is similar to that of include_fragment.
 * 
 * <strong>Example</strong>
 * <code>
 *  echo get_fragment('mypartial');
 * </code>
 * 
 * @param   string fragment name
 * @return  string result of a fragment include
 * @see     include_partial, include_fragment, fragment_path, fragment_name
 */
function get_fragment($templateName, $vars = array())
{
    // start the output buffer
    ob_start();
    ob_implicit_flush(0);
    // extract the variables in the scope of this function
    extract($vars);
    // include the file
    include(fragment_path($templateName));
    // clear the output buffer, and return the content
    return ob_get_clean();
}

Remember: you can place this in myproject/apps/myapp/lib/helper/PartialPlusHelper.php

Then you can use it with <?php use_helper('PartialPlus') ?>

by Stephen Riesenberg on 2006-12-16, tagged helper  template  view 

Template fragments - when even partials are too much

For the record, partials are wonderful. Self-sustained pieces of gui code, with access to all the symfony templating tools you need.

Sometimes however, even partials tend to be overkill. And sometimes, too, you have an annoying little piece of gui code that ought to go into your main template (indexSuccess.php or what have you), but you can't bear to put it there, because it would disturb the flow of an otherwise beautifully crafted template.

The truth is, every reuseable piece of gui code that isn't a full blown template really fits the partial paradigm, but sometimes it's just too much overhead, too much trouble, or just not necessary. Consider the following:

Example 1

<table>
  <?php foreach ($vars as $key => $value): ?>
    <tr>
      <th><?php include(fragment_name('label')) ?></th>
      <td><?php include(fragment_name('input')) ?></td>
    </tr>
  <?php endforeach; ?>
</table>

Example 2

<table>
  <?php foreach ($vars as $key => $value): ?>
    <tr>
      <th><?php include(fragment_path('somemodule/label')) ?></th>
      <td><?php include(fragment_path('anothermodule/input')) ?></td>
    </tr>
  <?php endforeach; ?>
</table>

The _label.php file could contain a few if statements... enough to generate the label for our input. It would look quite a bit uglier to place the code right in the th tag, but that's what we're really going for. _label.php would immediately have access to $key and $value, and any other variables already in scope, including typical templating variables provided by symfony. Additionally, there's no overhead to deal with, which helps if that foreach loop will execute 200 times (well, maybe that's a stretch). Finally, it just looks symfony-esque (at least to me)!

The code

<?php
 
/**
 * Get the path to a fragment.
 * The syntax is similar to that of include_partial.
 * To be used in conjunction with php's include function.
 * Variables in scope in the caller will be available in the execution of the fragment.
 * 
 * <strong>Example</strong>
 * <code>
 *  include(fragment_path('mypartial'))
 * </code>
 * 
 * <strong>Example</strong>
 * <code>
 *  include(fragment_path('mymodule/mypartial'))
 * </code>
 * 
 * @param   string fragment name
 * @return  string path to the fragment
 * @see     get_fragment, include_fragment, include_partial
 */
function fragment_path($templateName)
{
    // partial is in another module?
    if (false !== ($sep = strpos($templateName, '/')))
    {
        $moduleName   = substr($templateName, 0, $sep);
        $templateName = substr($templateName, $sep + 1);
    }
    else
    {
        $moduleName = sfContext::getInstance()->getActionStack()->getLastEntry()->getModuleName();
    }
    $fragmentName = fragment_name($templateName);
    // test for global fragment
    if ($moduleName === 'global')
    {
        $path = sfConfig::get('sf_app_template_dir');
    }
    // if not global, or the global fragment doesn't exist
    if ($moduleName !== 'global' || !is_readable($path.'/'.$fragmentName))
    {
        // get the full path to the file
        $path = sfConfig::get('sf_app_module_dir').'/'.$moduleName.'/'.sfConfig::get('sf_app_module_template_dir_name');
    }
    // return the full path to the partial
    return $path.'/'.$fragmentName;
}
 
/**
 * Translates a nice fragment name into the filename adhering to the naming convention for partials.
 * 
 * <strong>Example</strong>
 * <code>
 *  include(fragment_name('mypartial'))
 * </code>
 * 
 * @param   string fragment name
 * @return  string fragment filename
 * @see     fragment_path
 */
function fragment_name($templateName)
{
    return '_'.$templateName.'.php';
}

Remember: you can place this in myproject/apps/myapp/lib/helper/PartialPlusHelper.php

Then you can use it with <?php use_helper('PartialPlus') ?>

by Stephen Riesenberg on 2006-12-16, tagged helper  template  view 
(1 comment)

Displaying bitmapped headlines

This one is a port of the Imagetext plugin for Smarty. So as I won't include the credit in this snippet please check them in the Smarty plugin itself here.

Requirements

In order to make it work, this is what you need : - A 'fonts' folder in your projects' data directory, in which you copy the .ttf files you need - The following helper :

function imagetext($text,$styleName=null,$params=array())
    {
        if(!$styleName){
            $styleName='default';
        }
        if (empty($text)) { return; }
        // Which style is in use
        $style=sfConfig::get('app_imagetext_styles');
        $style=$style[$styleName];
        // Param values overwrite style values
        foreach ($params as $key=>$value) $style[$key] = $value;
        // Error handling
        if (empty($style['font'])) { throw new sfViewException("imagetext: missing 'font' parameter"); return; }
        if (empty($style['size'])) { throw new sfViewException("imagetext: missing 'size' parameter"); return; }
        if (empty($style['bgcolor'])) { $style['bgcolor'] = 'FFFFFF'; }
        if (empty($style['fgcolor'])) { $style['fgcolor'] = '000000';  }
 
    ### Preferences
    $cacheDir=sfConfig::get('sf_web_dir').DIRECTORY_SEPARATOR.'images'.DIRECTORY_SEPARATOR.'imagetext'.DIRECTORY_SEPARATOR;
    $cacheUrl='imagetext/';
    $fontDir=sfConfig::get('sf_data_dir').DIRECTORY_SEPARATOR.'fonts'.DIRECTORY_SEPARATOR;  
    $text = preg_replace("=<br( /)?>=i", "\n", $text);
 
    // Which font is in use
    $font = $fontDir.$style['font'];
    // Hash of text and all parameters for the cache function
    $hash = md5(implode('',$style).$text);
    // The url of the created image
    $imgFile    = $cacheDir.$styleName.'_'.$hash.'.'.$style['format'];
    $imgUrl = $cacheUrl.$styleName.'_'.$hash.'.'.$style['format'];
 
    ### Use cached image if available
    if (file_exists($imgFile) && @$style['dev'] != true && @$style['dynamic'] != true) 
          if (@$style['urlonly'] == 'true') return $imgUrl; else
        return @$style['prehtml'].image_tag($imgUrl,array('style'=>'border: 0 px solid #00ff00','alt'=>preg_replace("=\r\n|\r|\n|\t=", ' ', htmlspecialchars($text)))).@$style['posthtml'];
 
    ### otherwise create it
    // Function to get a color handler of hex values
    if (!function_exists('fromhex'))
        {
        function fromhex($image,$string) {
            sscanf($string, "%2x%2x%2x", $red, $green, $blue);
            return ImageColorAllocate($image,$red,$green,$blue);
            }
        }
 
    ### create a four times larger image to improve kerning
    // The multiplier. The bigger the better the kerning and the typeface, but the slower the creation
    $multi = 4;
 
    // If "pixelfont" donĀ“t use multiplier
    if ($style['pixelfont'] == 1) $multi = 1;
 
    // Calculate measures of image
    $bbox = imagettfbbox ($style['size']*$multi, 0, $font, $text);
    $xcorr = 0-$bbox[6]; // northwest X
    $ycorr = 0-$bbox[7]; // northwest Y
    $box['left']    = $bbox[6]+$xcorr+$style['x']*$multi;
    $box['height']  = abs($bbox[5])+abs($bbox[1]);
    $box['width']   = abs($bbox[2])+abs($bbox[0])+$style['x']*$multi;
    $box['top']     = abs($bbox[5]);
 
    // Create the big image
    $im = imagecreate ($box['width'], $box['height']);
    $bgcolor = fromhex($im,$style['bgcolor']);
    $fgcolor = fromhex($im,$style['fgcolor']);
    if ($style['pixelfont'] == 1) $fgcolor = -$fgcolor;
    imagettftext ($im, $style['size']*$multi, 0, $box['left'], $box['top'], $fgcolor, $font, $text);
 
    // Sample down the big image
    $width = $style['width']+$style['addx'];
    $height = $style['height']+$style['addy'];
 
    // Overwrite when height oder width is given
    if (empty($style['width'])) $width = $box['width']/$multi+$style['addx']+$style['x'];
    if (empty($style['height'])) $height = $box['height']/$multi+$style['addy']+$style['y'];
 
    $ds = imagecreatetruecolor ($width, $height);
    $bgcolor2 = fromhex($ds,$style['bgcolor']);
    imageFill($ds,0,0,$bgcolor2);
    imagecopyresampled($ds,$im,0,$style['y'],0,0,$box['width']/$multi, $box['height']/$multi,$box['width'], $box['height']);
    imagetruecolortopalette($ds,0,256);
    imagepalettecopy($ds,$im);
    ImageColorTransparent($ds,$bgcolor);
 
 
    // write whereto?
    if ($style['format'] == 'gif') ImageGIF ($ds,$imgFile); else ImagePNG ($ds,$imgFile);
    ImageDestroy ($im);
    ImageDestroy ($ds);
 
    // and display
    if ($style['dev']) $border = 1; else $border = 0;
  if ($style['urlonly'] == 'true') return $imgUrl;
        else
    return $style['prehtml'].image_tag($imgUrl,array('style'=>'border: '.$border.'px solid #00ff00','alt'=>preg_replace("=\r\n|\r|\n|\t=", ' ', htmlspecialchars($text)))).$style['posthtml'];
}

As you can see in this code styles can (at least 'default' MUST be) be configured in app.yml. This is an example style config :

    imagetext:
        styles:
            default:
                font:   hockey.ttf
                size:   18
                format: png

So now you can use the helper in your templates :

<?php use_helper('imageText') ?>
 
<? echo imagetext('This is my title') ?>
<p>Some blah blah</p>
<? echo imagetext('This is another headline style','anotherstyle') ?>

This helper uses GD so depending on which version you have installed, you might or might not handle PNG or GIF.

by whoknows on 2006-07-02, tagged helper  image  png  view 
(1 comment)

Helper: Embedding images in emails

Embedding images in emails as described in the symfony book has one drawback: it breaks the MVC paradigm by setting properties of the view (embedding the image) in the controller.

This is probably more obvious when embedding images such as company logos that are not part of a query result, but instead are 'static' visual enhancements of the view.

Besides being not good practice, this results in more code and potential mistakes when using several images, as you need to keep track of cid's both in the action and in the template.

This helper lets you treat images in emails similar to images in regular HTML views. There are no calls to $mail->addEmbeddedImage() needed in the action, embedding is done exclusively in the view, and you can forget about cid's.

Update: In the original version, an image used twice in the email would also be embedded twice, increasing email size. This bug is fixed.

Usage

Images are embedded like this:

<?php echo embedded_image($this, '/myImageDir/myImage.jpg' [, $altText [, $encoding [, $mimeType]]]); ?>

The parameters are:

$this: a reference to the current sfMailView

filename: the path to the image to be embedded

$altText: alternative name for attachment, default is ''

$encoding: encoding, default is base64

$mimeType: mime type, when omitted, mime type is determined using the file extension of the image

The Helper

function embedded_image(
    $mailView,
    $fileName,
    $altText = '',
    $encoding = 'base64',
    $mimeType = null
) {
 
    // get list of files already embedded
    $embeddedList = $mailView->getParameter('embedList', array(), 'email_helper');
 
    if (array_key_exists($fileName, $embeddedList)) {
 
        // if image was already embedded, use its old cid
        $embedString = $embeddedList[$fileName];
 
    } else {
 
        // find mime type
        if ($mimeType == null) {
            $mimeMap = array(
                'gif' => 'image/gif',
                'jpg' => 'image/jpeg',
                'png' => 'image/png'
            );
            $ext = strtolower(array_pop(explode('.', $fileName)));
            if (array_key_exists($ext, $mimeMap)) {
                $mimeType = $mimeMap[$ext];
            } else {
                $mimeType = 'image/jpeg';
            }
        }
 
        // increment embedded image count
        $imgID = $mailView->getParameter('embedIndex', 0, 'email_helper') + 1;
        $mailView->setParameter('embedIndex', $imgID, 'email_helper');
        $embedString = 'embID_' . $imgID;
 
        // add image to mail
        $mailView->getAttribute('mail')->addEmbeddedImage(
            $fileName,
            $embedString,
            $altText,
            $encoding,
            $mimeType
        );
 
        // remember embedded file
        $embeddedList[$fileName] = $embedString;
        $mailView->setParameter('embedList', $embeddedList, 'email_helper');
 
    }
 
    return '<img src="cid:' . $embedString . '" />';
 
}
by donharold on 2006-05-31, tagged email  helper  mvc  view 
(2 comments)