1<?php
2
3/*
4 * This file is part of the symfony package.
5 * (c) 2004-2006 Fabien Potencier <fabien.potencier@symfony-project.com>
6 * (c) 2004 David Heinemeier Hansson
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * AssetHelper.
14 *
15 * @package    symfony
16 * @subpackage helper
17 * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
18 * @author     David Heinemeier Hansson
19 * @version    SVN: $Id$
20 */
21
22/**
23 * Returns a <link> tag that browsers and news readers
24 * can use to auto-detect a RSS or ATOM feed for the current page,
25 * to be included in the <head> section of a HTML document.
26 *
27 * <b>Options:</b>
28 * - rel - defaults to 'alternate'
29 * - type - defaults to 'application/rss+xml'
30 * - title - defaults to the feed type in upper case
31 *
32 * <b>Examples:</b>
33 * <code>
34 *  echo auto_discovery_link_tag('rss', 'module/feed');
35 *    => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.curenthost.com/module/feed" />
36 *  echo auto_discovery_link_tag('rss', 'module/feed', array('title' => 'My RSS'));
37 *    => <link rel="alternate" type="application/rss+xml" title="My RSS" href="http://www.curenthost.com/module/feed" />
38 * </code>
39 *
40 * @param string $type        feed type ('rss', 'atom')
41 * @param string $url         'module/action' or '@rule' of the feed
42 * @param array  $tag_options additional HTML compliant <link> tag parameters
43 *
44 * @return string XHTML compliant <link> tag
45 */
46function auto_discovery_link_tag($type = 'rss', $url = '', $tag_options = array())
47{
48  return tag('link', array(
49    'rel'   => isset($tag_options['rel']) ? $tag_options['rel'] : 'alternate',
50    'type'  => isset($tag_options['type']) ? $tag_options['type'] : 'application/'.$type.'+xml',
51    'title' => isset($tag_options['title']) ? $tag_options['title'] : ucfirst($type),
52    'href'  => url_for($url, true)
53  ));
54}
55
56/**
57 * Returns the path to a JavaScript asset.
58 *
59 * <b>Example:</b>
60 * <code>
61 *  echo javascript_path('myscript');
62 *    => /js/myscript.js
63 * </code>
64 *
65 * <b>Note:</b> The asset name can be supplied as a...
66 * - full path, like "/my_js/myscript.css"
67 * - file name, like "myscript.js", that gets expanded to "/js/myscript.js"
68 * - file name without extension, like "myscript", that gets expanded to "/js/myscript.js"
69 *
70 * @param string $source   asset name
71 * @param bool   $absolute return absolute path ?
72 *
73 * @return string file path to the JavaScript file
74 * @see    javascript_include_tag
75 */
76function javascript_path($source, $absolute = false)
77{
78  return _compute_public_path($source, sfConfig::get('sf_web_js_dir_name', 'js'), 'js', $absolute);
79}
80
81/**
82 * Returns a <script> include tag per source given as argument.
83 *
84 * <b>Examples:</b>
85 * <code>
86 *  echo javascript_include_tag('xmlhr');
87 *    => <script language="JavaScript" type="text/javascript" src="/js/xmlhr.js"></script>
88 *  echo javascript_include_tag('common.javascript', '/elsewhere/cools');
89 *    => <script language="JavaScript" type="text/javascript" src="/js/common.javascript"></script>
90 *       <script language="JavaScript" type="text/javascript" src="/elsewhere/cools.js"></script>
91 * </code>
92 *
93 * @param string asset names
94 * @param array additional HTML compliant <link> tag parameters
95 *
96 * @return string XHTML compliant <script> tag(s)
97 * @see    javascript_path
98 */
99function javascript_include_tag()
100{
101  $sources = func_get_args();
102  $sourceOptions = (func_num_args() > 1 && is_array($sources[func_num_args() - 1])) ? array_pop($sources) : array();
103
104  $html = '';
105  foreach ($sources as $source)
106  {
107    $absolute = false;
108    if (isset($sourceOptions['absolute']))
109    {
110      unset($sourceOptions['absolute']);
111      $absolute = true;
112    }
113
114    $condition = null;
115    if (isset($sourceOptions['condition']))
116    {
117      $condition = $sourceOptions['condition'];
118      unset($sourceOptions['condition']);
119    }
120
121    if (!isset($sourceOptions['raw_name']))
122    {
123      $source = javascript_path($source, $absolute);
124    }
125    else
126    {
127      unset($sourceOptions['raw_name']);
128    }
129
130    $options = array_merge(array('type' => 'text/javascript', 'src' => $source), $sourceOptions);
131    $tag = content_tag('script', '', $options);
132
133    if (null !== $condition)
134    {
135      $tag = comment_as_conditional($condition, $tag);
136    }
137
138    $html .= $tag."\n";
139  }
140
141  return $html;
142}
143
144/**
145 * Returns the path to a stylesheet asset.
146 *
147 * <b>Example:</b>
148 * <code>
149 *  echo stylesheet_path('style');
150 *    => /css/style.css
151 * </code>
152 *
153 * <b>Note:</b> The asset name can be supplied as a...
154 * - full path, like "/my_css/style.css"
155 * - file name, like "style.css", that gets expanded to "/css/style.css"
156 * - file name without extension, like "style", that gets expanded to "/css/style.css"
157 *
158 * @param string $source   asset name
159 * @param bool   $absolute return absolute path ?
160 *
161 * @return string file path to the stylesheet file
162 * @see    stylesheet_tag
163 */
164function stylesheet_path($source, $absolute = false)
165{
166  return _compute_public_path($source, sfConfig::get('sf_web_css_dir_name', 'css'), 'css', $absolute);
167}
168
169/**
170 * Returns a css <link> tag per source given as argument,
171 * to be included in the <head> section of a HTML document.
172 *
173 * <b>Options:</b>
174 * - rel - defaults to 'stylesheet'
175 * - type - defaults to 'text/css'
176 * - media - defaults to 'screen'
177 *
178 * <b>Examples:</b>
179 * <code>
180 *  echo stylesheet_tag('style');
181 *    => <link href="/stylesheets/style.css" media="screen" rel="stylesheet" type="text/css" />
182 *  echo stylesheet_tag('style', array('media' => 'all'));
183 *    => <link href="/stylesheets/style.css" media="all" rel="stylesheet" type="text/css" />
184 *  echo stylesheet_tag('style', array('raw_name' => true));
185 *    => <link href="style" media="all" rel="stylesheet" type="text/css" />
186 *  echo stylesheet_tag('random.styles', '/css/stylish');
187 *    => <link href="/stylesheets/random.styles" media="screen" rel="stylesheet" type="text/css" />
188 *       <link href="/css/stylish.css" media="screen" rel="stylesheet" type="text/css" />
189 * </code>
190 *
191 * @param string asset names
192 * @param array  additional HTML compliant <link> tag parameters
193 *
194 * @return string XHTML compliant <link> tag(s)
195 * @see    stylesheet_path
196 */
197function stylesheet_tag()
198{
199  $sources = func_get_args();
200  $sourceOptions = (func_num_args() > 1 && is_array($sources[func_num_args() - 1])) ? array_pop($sources) : array();
201
202  $html = '';
203  foreach ($sources as $source)
204  {
205    $absolute = false;
206    if (isset($sourceOptions['absolute']))
207    {
208      unset($sourceOptions['absolute']);
209      $absolute = true;
210    }
211
212    $condition = null;
213    if (isset($sourceOptions['condition']))
214    {
215      $condition = $sourceOptions['condition'];
216      unset($sourceOptions['condition']);
217    }
218
219    if (!isset($sourceOptions['raw_name']))
220    {
221      $source = stylesheet_path($source, $absolute);
222    }
223    else
224    {
225      unset($sourceOptions['raw_name']);
226    }
227
228    $options = array_merge(array('rel' => 'stylesheet', 'type' => 'text/css', 'media' => 'screen', 'href' => $source), $sourceOptions);
229    $tag = tag('link', $options);
230
231    if (null !== $condition)
232    {
233      $tag = comment_as_conditional($condition, $tag);
234    }
235
236    $html .= $tag."\n";
237  }
238
239  return $html;
240}
241
242/**
243 * Adds a stylesheet to the response object.
244 *
245 * @see sfResponse->addStylesheet()
246 */
247function use_stylesheet($css, $position = '', $options = array())
248{
249  sfContext::getInstance()->getResponse()->addStylesheet($css, $position, $options);
250}
251
252/**
253 * Adds a javascript to the response object.
254 *
255 * @see sfResponse->addJavascript()
256 */
257function use_javascript($js, $position = '', $options = array())
258{
259  sfContext::getInstance()->getResponse()->addJavascript($js, $position, $options);
260}
261
262/**
263 * Decorates the current template with a given layout.
264 *
265 * @param mixed $layout The layout name or path or false to disable the layout
266 */
267function decorate_with($layout)
268{
269  if (false === $layout)
270  {
271    sfContext::getInstance()->get('view_instance')->setDecorator(false);
272  }
273  else
274  {
275    sfContext::getInstance()->get('view_instance')->setDecoratorTemplate($layout);
276  }
277}
278
279/**
280 * Returns the path to an image asset.
281 *
282 * <b>Example:</b>
283 * <code>
284 *  echo image_path('foobar');
285 *    => /images/foobar.png
286 * </code>
287 *
288 * <b>Note:</b> The asset name can be supplied as a...
289 * - full path, like "/my_images/image.gif"
290 * - file name, like "rss.gif", that gets expanded to "/images/rss.gif"
291 * - file name without extension, like "logo", that gets expanded to "/images/logo.png"
292 *
293 * @param string $source   asset name
294 * @param bool   $absolute return absolute path ?
295 *
296 * @return string file path to the image file
297 * @see    image_tag
298 */
299function image_path($source, $absolute = false)
300{
301  return _compute_public_path($source, sfConfig::get('sf_web_images_dir_name', 'images'), 'png', $absolute);
302}
303
304/**
305 * Returns an <img> image tag for the asset given as argument.
306 *
307 * <b>Options:</b>
308 * - 'absolute' - to output absolute file paths, useful for embedded images in emails
309 * - 'alt'  - defaults to the file name part of the asset (capitalized and without the extension)
310 * - 'size' - Supplied as "XxY", so "30x45" becomes width="30" and height="45"
311 *
312 * <b>Examples:</b>
313 * <code>
314 *  echo image_tag('foobar');
315 *    => <img src="images/foobar.png" alt="Foobar" />
316 *  echo image_tag('/my_images/image.gif', array('alt' => 'Alternative text', 'size' => '100x200'));
317 *    => <img src="/my_images/image.gif" alt="Alternative text" width="100" height="200" />
318 * </code>
319 *
320 * @param string $source  image asset name
321 * @param array  $options additional HTML compliant <img> tag parameters
322 *
323 * @return string XHTML compliant <img> tag
324 * @see    image_path
325 */
326function image_tag($source, $options = array())
327{
328  if (!$source)
329  {
330    return '';
331  }
332
333  $options = _parse_attributes($options);
334
335  $absolute = false;
336  if (isset($options['absolute']))
337  {
338    unset($options['absolute']);
339    $absolute = true;
340  }
341
342  if (!isset($options['raw_name']))
343  {
344    $options['src'] = image_path($source, $absolute);
345  }
346  else
347  {
348    $options['src'] = $source;
349    unset($options['raw_name']);
350  }
351
352  if (isset($options['alt_title']))
353  {
354    // set as alt and title but do not overwrite explicitly set
355    if (!isset($options['alt']))
356    {
357      $options['alt'] = $options['alt_title'];
358    }
359    if (!isset($options['title']))
360    {
361      $options['title'] = $options['alt_title'];
362    }
363    unset($options['alt_title']);
364  }
365
366  if (isset($options['size']))
367  {
368    list($width, $height) = explode('x', $options['size'], 2);
369    $options['height'] = $height;
370    $options['width'] = $width;
371    unset($options['size']);
372  }
373
374  return tag('img', $options);
375}
376
377function _compute_public_path($source, $dir, $ext, $absolute = false)
378{
379  if (strpos($source, '://') || strpos($source, '//') === 0)
380  {
381    return $source;
382  }
383
384  $request = sfContext::getInstance()->getRequest();
385  $sf_relative_url_root = $request->getRelativeUrlRoot();
386  if (0 !== strpos($source, '/'))
387  {
388    $source = $sf_relative_url_root.'/'.$dir.'/'.$source;
389  }
390
391  $query_string = '';
392  if (false !== $pos = strpos($source, '?'))
393  {
394    $query_string = substr($source, $pos);
395    $source = substr($source, 0, $pos);
396  }
397
398  if (false === strpos(basename($source), '.'))
399  {
400    $source .= '.'.$ext;
401  }
402
403  if ($sf_relative_url_root && 0 !== strpos($source, $sf_relative_url_root))
404  {
405    $source = $sf_relative_url_root.$source;
406  }
407
408  if ($absolute)
409  {
410    $source = 'http'.($request->isSecure() ? 's' : '').'://'.$request->getHost().$source;
411  }
412
413  return $source.$query_string;
414}
415
416/**
417 * Prints a set of <meta> tags according to the response attributes,
418 * to be included in the <head> section of a HTML document.
419 *
420 * <b>Examples:</b>
421 * <code>
422 *  include_metas();
423 *    => <meta name="title" content="symfony - open-source PHP5 web framework" />
424 *       <meta name="robots" content="index, follow" />
425 *       <meta name="description" content="symfony - open-source PHP5 web framework" />
426 *       <meta name="keywords" content="symfony, project, framework, php, php5, open-source, mit, symphony" />
427 *       <meta name="language" content="en" /><link href="/stylesheets/style.css" media="screen" rel="stylesheet" type="text/css" />
428 * </code>
429 *
430 * <b>Note:</b> Modify the view.yml or use sfWebResponse::addMeta() to change, add or remove metas.
431 *
432 * @return string XHTML compliant <meta> tag(s)
433 * @see    include_http_metas
434 * @see    sfWebResponse::addMeta()
435 */
436function include_metas()
437{
438  $context = sfContext::getInstance();
439  $i18n = sfConfig::get('sf_i18n') ? $context->getI18N() : null;
440  foreach ($context->getResponse()->getMetas() as $name => $content)
441  {
442    echo tag('meta', array('name' => $name, 'content' => null === $i18n ? $content : $i18n->__($content)))."\n";
443  }
444}
445
446/**
447 * Returns a set of <meta http-equiv> tags according to the response attributes,
448 * to be included in the <head> section of a HTML document.
449 *
450 * <b>Examples:</b>
451 * <code>
452 *  include_http_metas();
453 *    => <meta http-equiv="content-type" content="text/html; charset=utf-8" />
454 * </code>
455 *
456 * <b>Note:</b> Modify the view.yml or use sfWebResponse::addHttpMeta() to change, add or remove HTTP metas.
457 *
458 * @return string XHTML compliant <meta> tag(s)
459 * @see    include_metas
460 * @see    sfWebResponse::addHttpMeta()
461 */
462function include_http_metas()
463{
464  foreach (sfContext::getInstance()->getResponse()->getHttpMetas() as $httpequiv => $value)
465  {
466    echo tag('meta', array('http-equiv' => $httpequiv, 'content' => $value))."\n";
467  }
468}
469
470/**
471 * Returns the title of the current page according to the response attributes,
472 * to be included in the <title> section of a HTML document.
473 *
474 * <b>Note:</b> Modify the sfResponse object or the view.yml to modify the title of a page.
475 *
476 * @return string page title
477 */
478function include_title()
479{
480  $title = sfContext::getInstance()->getResponse()->getTitle();
481
482  echo content_tag('title', $title)."\n";
483}
484
485/**
486 * Returns <script> tags for all javascripts configured in view.yml or added to the response object.
487 *
488 * You can use this helper to decide the location of javascripts in pages.
489 * By default, if you don't call this helper, symfony will automatically include javascripts before </head>.
490 * Calling this helper disables this behavior.
491 *
492 * @return string <script> tags
493 */
494function get_javascripts()
495{
496  $response = sfContext::getInstance()->getResponse();
497  sfConfig::set('symfony.asset.javascripts_included', true);
498
499  $html = '';
500  foreach ($response->getJavascripts() as $file => $options)
501  {
502    $html .= javascript_include_tag($file, $options);
503  }
504
505  return $html;
506}
507
508/**
509 * Prints <script> tags for all javascripts configured in view.yml or added to the response object.
510 *
511 * @see get_javascripts()
512 */
513function include_javascripts()
514{
515  echo get_javascripts();
516}
517
518/**
519 * Clear all javascripts of the response object.
520 *
521 * @see sfResponse->clearJavascripts()
522 */
523function clear_javascripts()
524{
525  sfContext::getInstance()->getResponse()->clearJavascripts();
526}
527
528/**
529 * Returns <link> tags for all stylesheets configured in view.yml or added to the response object.
530 *
531 * You can use this helper to decide the location of stylesheets in pages.
532 * By default, if you don't call this helper, symfony will automatically include stylesheets before </head>.
533 * Calling this helper disables this behavior.
534 *
535 * @return string <link> tags
536 */
537function get_stylesheets()
538{
539  $response = sfContext::getInstance()->getResponse();
540  sfConfig::set('symfony.asset.stylesheets_included', true);
541
542  $html = '';
543  foreach ($response->getStylesheets() as $file => $options)
544  {
545    $html .= stylesheet_tag($file, $options);
546  }
547
548  return $html;
549}
550
551/**
552 * Prints <link> tags for all stylesheets configured in view.yml or added to the response object.
553 *
554 * @see get_stylesheets()
555 */
556function include_stylesheets()
557{
558  echo get_stylesheets();
559}
560
561/* Clear all stylesheets of the response object.
562 *
563 * @see sfResponse->clearStylesheets()
564 */
565function clear_stylesheets()
566{
567  sfContext::getInstance()->getResponse()->clearStylesheets();
568}
569
570/**
571 * Returns a <script> include tag for the given internal URI.
572 *
573 * The helper automatically adds the sf_format to the internal URI, so you don't have to.
574 *
575 * @param string $uri      The internal URI for the dynamic javascript
576 * @param bool   $absolute Whether to generate an absolute URL
577 * @param array  $options  An array of options
578 *
579 * @return string XHTML compliant <script> tag(s)
580 * @see    javascript_include_tag
581 */
582function dynamic_javascript_include_tag($uri, $absolute = false, $options = array())
583{
584  $options['raw_name'] = true;
585
586  return javascript_include_tag(_dynamic_path($uri, 'js', $absolute), $options);
587}
588
589/**
590 * Adds a dynamic javascript to the response object.
591 *
592 * The first argument is an internal URI.
593 * The helper automatically adds the sf_format to the internal URI, so you don't have to.
594 *
595 * @see sfResponse->addJavascript()
596 */
597function use_dynamic_javascript($js, $position = '', $options = array())
598{
599  $options['raw_name'] = true;
600
601  return use_javascript(_dynamic_path($js, 'js'), $position, $options);
602}
603
604/**
605 * Adds a dynamic stylesheet to the response object.
606 *
607 * The first argument is an internal URI.
608 * The helper automatically adds the sf_format to the internal URI, so you don't have to.
609 *
610 * @see sfResponse->addStylesheet()
611 */
612function use_dynamic_stylesheet($css, $position = '', $options = array())
613{
614  $options['raw_name'] = true;
615
616  return use_stylesheet(_dynamic_path($css, 'css'), $position, $options);
617}
618
619function _dynamic_path($uri, $format, $absolute = false)
620{
621  return url_for($uri.(false === strpos($uri, '?') ? '?' : '&').'sf_format='.$format, $absolute);
622}
623
624/**
625 * Returns <script> tags for all javascripts associated with the given form.
626 *
627 * The scripts are set by implementing the getJavaScripts() method in the
628 * corresponding widget.
629 *
630 * <code>
631 * class MyWidget extends sfWidgetForm
632 * {
633 *   public function getJavaScripts()
634 *   {
635 *     return array('/path/to/a/file.js');
636 *   }
637 * }
638 * </code>
639 *
640 * @return string <script> tags
641 */
642function get_javascripts_for_form(sfForm $form)
643{
644  $html = '';
645  foreach ($form->getJavaScripts() as $file)
646  {
647    $html .= javascript_include_tag($file);
648  }
649
650  return $html;
651}
652
653/**
654 * Prints <script> tags for all javascripts associated with the given form.
655 *
656 * @see get_javascripts_for_form()
657 */
658function include_javascripts_for_form(sfForm $form)
659{
660  echo get_javascripts_for_form($form);
661}
662
663/**
664 * Adds javascripts from the supplied form to the response object.
665 *
666 * @param sfForm $form
667 */
668function use_javascripts_for_form(sfForm $form)
669{
670  $response = sfContext::getInstance()->getResponse();
671
672  foreach ($form->getJavaScripts() as $file)
673  {
674    $response->addJavascript($file);
675  }
676}
677
678/**
679 * Returns <link> tags for all stylesheets associated with the given form.
680 *
681 * The stylesheets are set by implementing the getStyleSheets() method in the
682 * corresponding widget.
683 *
684 * <code>
685 * class MyWidget extends sfWidgetForm
686 * {
687 *   public function getStyleSheets()
688 *   {
689 *     return array('/path/to/a/file.css');
690 *   }
691 * }
692 * </code>
693 *
694 * @return string <link> tags
695 */
696function get_stylesheets_for_form(sfForm $form)
697{
698  $html = '';
699  foreach ($form->getStylesheets() as $file => $media)
700  {
701    $html .= stylesheet_tag($file, array('media' => $media));
702  }
703
704  return $html;
705}
706
707/**
708 * Prints <link> tags for all stylesheets associated with the given form.
709 *
710 * @see get_stylesheets_for_form()
711 */
712function include_stylesheets_for_form(sfForm $form)
713{
714  echo get_stylesheets_for_form($form);
715}
716
717/**
718 * Adds stylesheets from the supplied form to the response object.
719 *
720 * @param sfForm $form
721 */
722function use_stylesheets_for_form(sfForm $form)
723{
724  $response = sfContext::getInstance()->getResponse();
725
726  foreach ($form->getStylesheets() as $file => $media)
727  {
728    $response->addStylesheet($file, '', array('media' => $media));
729  }
730}
731