UrlHelper.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. <?php
  2. /*
  3. * This file is part of the symfony package.
  4. * (c) 2004-2006 Fabien Potencier <fabien.potencier@symfony-project.com>
  5. *
  6. * For the full copyright and license information, please view the LICENSE
  7. * file that was distributed with this source code.
  8. */
  9. /**
  10. * UrlHelper.
  11. *
  12. * @package symfony
  13. * @subpackage helper
  14. * @author Fabien Potencier <fabien.potencier@symfony-project.com>
  15. * @version SVN: $Id: UrlHelper.php 13408 2008-11-27 11:51:22Z fabien $
  16. */
  17. /**
  18. * Returns a routed URL based on the module/action passed as argument
  19. * and the routing configuration.
  20. *
  21. * <b>Examples:</b>
  22. * <code>
  23. * echo url_for('my_module/my_action');
  24. * => /path/to/my/action
  25. * echo url_for('@my_rule');
  26. * => /path/to/my/action
  27. * echo url_for('@my_rule', true);
  28. * => http://myapp.example.com/path/to/my/action
  29. * </code>
  30. *
  31. * @param string $internal_uri 'module/action' or '@rule' of the action
  32. * @param bool $absolute return absolute path?
  33. * @return string routed URL
  34. */
  35. function url_for($internal_uri, $absolute = false)
  36. {
  37. return sfContext::getInstance()->getController()->genUrl($internal_uri, $absolute);
  38. }
  39. /**
  40. * Creates a <a> link tag of the given name using a routed URL
  41. * based on the module/action passed as argument and the routing configuration.
  42. * It's also possible to pass a string instead of a module/action pair to
  43. * get a link tag that just points without consideration.
  44. * If null is passed as a name, the link itself will become the name.
  45. * If an object is passed as a name, the object string representation is used.
  46. * One of the options serves for for creating javascript confirm alerts where
  47. * if you pass 'confirm' => 'Are you sure?', the link will be guarded
  48. * with a JS popup asking that question. If the user accepts, the link is processed,
  49. * otherwise not.
  50. *
  51. * <b>Options:</b>
  52. * - 'absolute' - if set to true, the helper outputs an absolute URL
  53. * - 'query_string' - to append a query string (starting by ?) to the routed url
  54. * - 'anchor' - to append an anchor (starting by #) to the routed url
  55. * - 'confirm' - displays a javascript confirmation alert when the link is clicked
  56. * - 'popup' - if set to true, the link opens a new browser window
  57. * - 'post' - if set to true, the link submits a POST request instead of GET (caution: do not use inside a form)
  58. *
  59. * <b>Note:</b> The 'popup' and 'post' options are not compatible with each other.
  60. *
  61. * <b>Examples:</b>
  62. * <code>
  63. * echo link_to('Delete this page', 'my_module/my_action');
  64. * => <a href="/path/to/my/action">Delete this page</a>
  65. * echo link_to('Visit Hoogle', 'http://www.hoogle.com');
  66. * => <a href="http://www.hoogle.com">Visit Hoogle</a>
  67. * echo link_to('Delete this page', 'my_module/my_action', array('id' => 'myid', 'confirm' => 'Are you sure?', 'absolute' => true));
  68. * => <a href="http://myapp.example.com/path/to/my/action" id="myid" onclick="return confirm('Are you sure?');">Delete this page</a>
  69. * </code>
  70. *
  71. * @param string $name name of the link, i.e. string to appear between the <a> tags
  72. * @param string $internal_uri 'module/action' or '@rule' of the action
  73. * @param array $options additional HTML compliant <a> tag parameters
  74. * @return string XHTML compliant <a href> tag
  75. * @see url_for
  76. */
  77. function link_to($name = '', $internal_uri = '', $options = array())
  78. {
  79. $html_options = _parse_attributes($options);
  80. $html_options = _convert_options_to_javascript($html_options);
  81. $absolute = false;
  82. if (isset($html_options['absolute_url']))
  83. {
  84. $html_options['absolute'] = $html_options['absolute_url'];
  85. unset($html_options['absolute_url']);
  86. }
  87. if (isset($html_options['absolute']))
  88. {
  89. $absolute = (boolean) $html_options['absolute'];
  90. unset($html_options['absolute']);
  91. }
  92. $html_options['href'] = url_for($internal_uri, $absolute);
  93. if (isset($html_options['query_string']))
  94. {
  95. $html_options['href'] .= '?'.$html_options['query_string'];
  96. unset($html_options['query_string']);
  97. }
  98. if (isset($html_options['anchor']))
  99. {
  100. $html_options['href'] .= '#'.$html_options['anchor'];
  101. unset($html_options['anchor']);
  102. }
  103. if (is_object($name))
  104. {
  105. if (method_exists($name, '__toString'))
  106. {
  107. $name = $name->__toString();
  108. }
  109. else
  110. {
  111. throw new sfException(sprintf('Object of class "%s" cannot be converted to string (Please create a __toString() method).', get_class($name)));
  112. }
  113. }
  114. if (!strlen($name))
  115. {
  116. $name = $html_options['href'];
  117. }
  118. return content_tag('a', $name, $html_options);
  119. }
  120. /**
  121. * If the condition passed as first argument is true,
  122. * creates a <a> link tag of the given name using a routed URL
  123. * based on the module/action passed as argument and the routing configuration.
  124. * If the condition is false, the given name is returned between <span> tags
  125. *
  126. * <b>Options:</b>
  127. * - 'tag' - the HTML tag that must enclose the name if the condition is false, defaults to <span>
  128. * - 'absolute' - if set to true, the helper outputs an absolute URL
  129. * - 'query_string' - to append a query string (starting by ?) to the routed url
  130. * - 'anchor' - to append an anchor (starting by #) to the routed url
  131. * - 'confirm' - displays a javascript confirmation alert when the link is clicked
  132. * - 'popup' - if set to true, the link opens a new browser window
  133. * - 'post' - if set to true, the link submits a POST request instead of GET (caution: do not use inside a form)
  134. *
  135. * <b>Examples:</b>
  136. * <code>
  137. * echo link_to_if($user->isAdministrator(), 'Delete this page', 'my_module/my_action');
  138. * => <a href="/path/to/my/action">Delete this page</a>
  139. * echo link_to_if(!$user->isAdministrator(), 'Delete this page', 'my_module/my_action');
  140. * => <span>Delete this page</span>
  141. * </code>
  142. *
  143. * @param bool $condition condition
  144. * @param string $name name of the link, i.e. string to appear between the <a> tags
  145. * @param string $internal_uri 'module/action' or '@rule' of the action
  146. * @param array $options additional HTML compliant <a> tag parameters
  147. * @return string XHTML compliant <a href> tag or name
  148. * @see link_to
  149. */
  150. function link_to_if($condition, $name = '', $internal_uri = '', $options = array())
  151. {
  152. $html_options = _parse_attributes($options);
  153. if ($condition)
  154. {
  155. unset($html_options['tag']);
  156. return link_to($name, $internal_uri, $html_options);
  157. }
  158. else
  159. {
  160. unset($html_options['query_string']);
  161. unset($html_options['absolute_url']);
  162. unset($html_options['absolute']);
  163. $tag = _get_option($html_options, 'tag', 'span');
  164. return content_tag($tag, $name, $html_options);
  165. }
  166. }
  167. /**
  168. * If the condition passed as first argument is false,
  169. * creates a <a> link tag of the given name using a routed URL
  170. * based on the module/action passed as argument and the routing configuration.
  171. * If the condition is true, the given name is returned between <span> tags
  172. *
  173. * <b>Options:</b>
  174. * - 'tag' - the HTML tag that must enclose the name if the condition is true, defaults to <span>
  175. * - 'absolute' - if set to true, the helper outputs an absolute URL
  176. * - 'query_string' - to append a query string (starting by ?) to the routed url
  177. * - 'anchor' - to append an anchor (starting by #) to the routed url
  178. * - 'confirm' - displays a javascript confirmation alert when the link is clicked
  179. * - 'popup' - if set to true, the link opens a new browser window
  180. * - 'post' - if set to true, the link submits a POST request instead of GET (caution: do not use inside a form)
  181. *
  182. * <b>Examples:</b>
  183. * <code>
  184. * echo link_to_unless($user->isAdministrator(), 'Delete this page', 'my_module/my_action');
  185. * => <span>Delete this page</span>
  186. * echo link_to_unless(!$user->isAdministrator(), 'Delete this page', 'my_module/my_action');
  187. * => <a href="/path/to/my/action">Delete this page</a>
  188. * </code>
  189. *
  190. * @param bool $condition condition
  191. * @param string $name name of the link, i.e. string to appear between the <a> tags
  192. * @param string $internal_uri 'module/action' or '@rule' of the action
  193. * @param array $options additional HTML compliant <a> tag parameters
  194. * @return string XHTML compliant <a href> tag or name
  195. * @see link_to
  196. */
  197. function link_to_unless($condition, $name = '', $internal_uri = '', $options = array())
  198. {
  199. return link_to_if(!$condition, $name, $internal_uri, $options);
  200. }
  201. /**
  202. * Returns a URL rooted at the web root
  203. *
  204. * @param string $path The route to append
  205. * @param bool $absolute If true, an absolute path is returned (optional)
  206. * @return The web URL root
  207. */
  208. function public_path($path, $absolute = false)
  209. {
  210. $request = sfContext::getInstance()->getRequest();
  211. $root = $request->getRelativeUrlRoot();
  212. if ($absolute)
  213. {
  214. $source = 'http';
  215. if ($request->isSecure())
  216. {
  217. $source .= 's';
  218. }
  219. $source .='://'.$request->getHost().$root;
  220. }
  221. else
  222. {
  223. $source = $root;
  224. }
  225. if (substr($path, 0, 1) != '/')
  226. {
  227. $path = '/'.$path;
  228. }
  229. return $source.$path;
  230. }
  231. /**
  232. * Creates an <input> button tag of the given name pointing to a routed URL
  233. * based on the module/action passed as argument and the routing configuration.
  234. * The syntax is similar to the one of link_to.
  235. *
  236. * <b>Options:</b>
  237. * - 'absolute' - if set to true, the helper outputs an absolute URL
  238. * - 'query_string' - to append a query string (starting by ?) to the routed url
  239. * - 'anchor' - to append an anchor (starting by #) to the routed url
  240. * - 'confirm' - displays a javascript confirmation alert when the button is clicked
  241. * - 'popup' - if set to true, the button opens a new browser window
  242. * - 'post' - if set to true, the button submits a POST request instead of GET (caution: do not use inside a form)
  243. *
  244. * <b>Examples:</b>
  245. * <code>
  246. * echo button_to('Delete this page', 'my_module/my_action');
  247. * => <input value="Delete this page" type="button" onclick="document.location.href='/path/to/my/action';" />
  248. * </code>
  249. *
  250. * @param string $name name of the button
  251. * @param string $internal_uri 'module/action' or '@rule' of the action
  252. * @param array $options additional HTML compliant <input> tag parameters
  253. * @return string XHTML compliant <input> tag
  254. * @see url_for, link_to
  255. */
  256. function button_to($name, $internal_uri ='', $options = array())
  257. {
  258. $html_options = _parse_attributes($options);
  259. $html_options['value'] = $name;
  260. if (isset($html_options['post']) && $html_options['post'])
  261. {
  262. if (isset($html_options['popup']))
  263. {
  264. throw new sfConfigurationException('You can\'t use "popup" and "post" together.');
  265. }
  266. $html_options['type'] = 'submit';
  267. unset($html_options['post']);
  268. $html_options = _convert_options_to_javascript($html_options);
  269. return form_tag($internal_uri, array('method' => 'post', 'class' => 'button_to')).content_tag('div', tag('input', $html_options)).'</form>';
  270. }
  271. $url = url_for($internal_uri);
  272. if (isset($html_options['query_string']))
  273. {
  274. $url = $url.'?'.$html_options['query_string'];
  275. unset($html_options['query_string']);
  276. }
  277. if (isset($html_options['anchor']))
  278. {
  279. $url = $url.'#'.$html_options['anchor'];
  280. unset($html_options['anchor']);
  281. }
  282. $url = "'".$url."'";
  283. $html_options['type'] = 'button';
  284. if (isset($html_options['popup']))
  285. {
  286. $html_options = _convert_options_to_javascript($html_options, $url);
  287. unset($html_options['popup']);
  288. }
  289. else
  290. {
  291. $html_options['onclick'] = "document.location.href=".$url.";";
  292. $html_options = _convert_options_to_javascript($html_options);
  293. }
  294. return tag('input', $html_options);
  295. }
  296. /**
  297. * Creates a <a> link tag to the given email (with href="mailto:...").
  298. * If null is passed as a name, the email itself will become the name.
  299. *
  300. * <b>Options:</b>
  301. * - 'encode' - if set to true, the email address appears with various random encoding for each letter.
  302. * The mail link still works when encoded, but the address doesn't appear in clear
  303. * in the source. Use it to prevent spam (efficiency not guaranteed).
  304. *
  305. * <b>Examples:</b>
  306. * <code>
  307. * echo mail_to('webmaster@example.com');
  308. * => <a href="mailto:webmaster@example.com">webmaster@example.com</a>
  309. * echo mail_to('webmaster@example.com', 'send us an email');
  310. * => <a href="mailto:webmaster@example.com">send us an email</a>
  311. * echo mail_to('webmaster@example.com', 'send us an email', array('encode' => true));
  312. * => <a href="
  313. &#x6d;a&#x69;&#x6c;&#x74;&#111;&#58;&#x77;&#x65;b&#x6d;as&#116;&#x65;&#114;
  314. &#64;&#101;&#x78;&#x61;&#x6d;&#x70;&#108;&#x65;&#46;&#99;&#x6f;&#109;
  315. ">send us an email</a>
  316. * </code>
  317. *
  318. * @param string $email target email
  319. * @param string $name name of the link, i.e. string to appear between the <a> tags
  320. * @param array $options additional HTML compliant <a> tag parameters
  321. * @param array $default_value
  322. * @return string XHTML compliant <a href> tag
  323. * @see link_to
  324. */
  325. function mail_to($email, $name = '', $options = array(), $default_value = array())
  326. {
  327. $html_options = _parse_attributes($options);
  328. $html_options = _convert_options_to_javascript($html_options);
  329. $default_tmp = _parse_attributes($default_value);
  330. $default = array();
  331. foreach ($default_tmp as $key => $value)
  332. {
  333. $default[] = urlencode($key).'='.urlencode($value);
  334. }
  335. $options = count($default) ? '?'.implode('&', $default) : '';
  336. if (isset($html_options['encode']) && $html_options['encode'])
  337. {
  338. unset($html_options['encode']);
  339. $html_options['href'] = _encodeText('mailto:'.$email.$options);
  340. if (!$name)
  341. {
  342. $name = _encodeText($email);
  343. }
  344. }
  345. else
  346. {
  347. $html_options['href'] = 'mailto:'.$email.$options;
  348. if (!$name)
  349. {
  350. $name = $email;
  351. }
  352. }
  353. return content_tag('a', $name, $html_options);
  354. }
  355. function _convert_options_to_javascript($html_options, $url = 'this.href')
  356. {
  357. // confirm
  358. $confirm = isset($html_options['confirm']) ? $html_options['confirm'] : '';
  359. unset($html_options['confirm']);
  360. // popup
  361. $popup = isset($html_options['popup']) ? $html_options['popup'] : '';
  362. unset($html_options['popup']);
  363. // post
  364. $post = isset($html_options['post']) ? $html_options['post'] : '';
  365. unset($html_options['post']);
  366. $onclick = isset($html_options['onclick']) ? $html_options['onclick'] : '';
  367. if ($popup && $post)
  368. {
  369. throw new sfConfigurationException('You can\'t use "popup" and "post" in the same link.');
  370. }
  371. else if ($confirm && $popup)
  372. {
  373. $html_options['onclick'] = $onclick.'if ('._confirm_javascript_function($confirm).') { '._popup_javascript_function($popup, $url).' };return false;';
  374. }
  375. else if ($confirm && $post)
  376. {
  377. $html_options['onclick'] = $onclick.'if ('._confirm_javascript_function($confirm).') { '._post_javascript_function().' };return false;';
  378. }
  379. else if ($confirm)
  380. {
  381. if ($onclick)
  382. {
  383. $html_options['onclick'] = 'if ('._confirm_javascript_function($confirm).') { return '.$onclick.'} else return false;';
  384. }
  385. else
  386. {
  387. $html_options['onclick'] = 'return '._confirm_javascript_function($confirm).';';
  388. }
  389. }
  390. else if ($post)
  391. {
  392. $html_options['onclick'] = $onclick._post_javascript_function().'return false;';
  393. }
  394. else if ($popup)
  395. {
  396. $html_options['onclick'] = $onclick._popup_javascript_function($popup, $url).'return false;';
  397. }
  398. return $html_options;
  399. }
  400. function _confirm_javascript_function($confirm)
  401. {
  402. return "confirm('".escape_javascript($confirm)."')";
  403. }
  404. function _popup_javascript_function($popup, $url = '')
  405. {
  406. if (is_array($popup))
  407. {
  408. if (isset($popup[1]))
  409. {
  410. return "var w=window.open(".$url.",'".$popup[0]."','".$popup[1]."');w.focus();";
  411. }
  412. else
  413. {
  414. return "var w=window.open(".$url.",'".$popup[0]."');w.focus();";
  415. }
  416. }
  417. else
  418. {
  419. return "var w=window.open(".$url.");w.focus();";
  420. }
  421. }
  422. function _post_javascript_function()
  423. {
  424. return "f = document.createElement('form'); document.body.appendChild(f); f.method = 'POST'; f.action = this.href; f.submit();";
  425. }
  426. function _encodeText($text)
  427. {
  428. $encoded_text = '';
  429. for ($i = 0; $i < strlen($text); $i++)
  430. {
  431. $char = $text{$i};
  432. $r = rand(0, 100);
  433. # roughly 10% raw, 45% hex, 45% dec
  434. # '@' *must* be encoded. I insist.
  435. if ($r > 90 && $char != '@')
  436. {
  437. $encoded_text .= $char;
  438. }
  439. else if ($r < 45)
  440. {
  441. $encoded_text .= '&#x'.dechex(ord($char)).';';
  442. }
  443. else
  444. {
  445. $encoded_text .= '&#'.ord($char).';';
  446. }
  447. }
  448. return $encoded_text;
  449. }