Development

Changeset 8227

You must first sign up to be able to contribute.

Changeset 8227

Show
Ignore:
Timestamp:
04/03/08 12:47:59 (6 months ago)
Author:
fabien
Message:

added support for formats/mime types

  • added a formats parameter to the request factory (to associate format and mime types)
  • added sfWebRequest ->getMimeType(), ->getFormat() interfaces to the formats parameter
  • added a special sf_format request parameter to configure the request format from the routing

url: /:page.:sf_format

  • added sfWebRequest ->getRequestFormat(), and ->setRequestFormat() to manage the request format
  • getRequestFormat() try to guess the format if no format is configured (via the Accept HTTP header)
  • added format support to the view
    • by default, if a format is defined, the template extension is changed according to the format,
      and the layout is disabled (except for the html format, which is the default)

if the format is js, the default template will be indexSuccess.js.php

  • the format is supported by templates, layouts, components, and partials

if you include a partial named article in a js context, the loaded partial will be _article.js.php

  • added a view.configure_format event to override the default behavior (to add new formats for example...
    look at the functional test to see how to add iPhone support ;))
  • added unit and functional tests (we have now more than 8000 tests!)
Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • branches/1.1/lib/config/config/factories.yml

    r7695 r8227  
    55  request: 
    66    class: sfWebRequest 
     7    param: 
     8      formats: 
     9        txt:  text/plain 
     10        js:   [application/javascript, application/x-javascript, text/javascript] 
     11        css:  text/css 
     12        json: [application/json, application/x-json] 
     13        xml:  [text/xml, application/xml, application/x-xml] 
     14        rdf:  application/rdf+xml 
     15        atom: application/atom+xml 
    716 
    817  response: 
  • branches/1.1/lib/config/sfViewConfigHandler.class.php

    r7959 r8227  
    153153 
    154154  /** 
    155    * Adds a layour statement statement to the data. 
    156    * 
    157    * @param string The view name 
     155   * Adds a layout statement statement to the data. 
     156   * 
     157   * @param string The view name 
    158158   * 
    159159   * @return string The PHP statement 
     
    161161  protected function addLayout($viewName = '') 
    162162  { 
    163     $data = ''; 
    164     if ($this->getConfigValue('has_layout', $viewName) && false !== $layout = $this->getConfigValue('layout', $viewName)) 
    165     { 
    166       // decorator configuration 
    167       $data .= "  \$this->setDecoratorTemplate(sfConfig::get('symfony.view.'.\$this->moduleName.'_'.\$this->actionName.'_layout', '$layout'.\$this->getExtension()));\n"; 
    168     } 
    169  
    170     // For XMLHttpRequest, we want no layout by default 
    171     // So, we check if the user requested has_layout: true or if he gave a layout: name for this particular action 
    172     $localLayout = isset($this->yamlConfig[$viewName]['layout']) || isset($this->yamlConfig[$viewName]['has_layout']); 
    173     if (!$localLayout && isset($data)) 
    174     { 
    175       $data = "  if (!\$this->context->getRequest()->isXmlHttpRequest())\n  {\n  $data  }\n"; 
     163    // true if the user set 'has_layout' to true or set a 'layout' name for this specific action 
     164    $hasLocalLayout = isset($this->yamlConfig[$viewName]['layout']) || (isset($this->yamlConfig[$viewName]) && array_key_exists('has_layout', $this->yamlConfig[$viewName])); 
     165 
     166    // the layout value 
     167    $layout = $this->getConfigValue('has_layout', $viewName) ? $this->getConfigValue('layout', $viewName) : false; 
     168 
     169    // the user set a decorator in the action 
     170    $data = <<<EOF 
     171  if (!is_null(\$layout = sfConfig::get('symfony.view.'.\$this->moduleName.'_'.\$this->actionName.'_layout'))) 
     172  { 
     173    \$this->setDecoratorTemplate(false === \$layout ? false : \$layout.\$this->getExtension()); 
     174  } 
     175EOF; 
     176 
     177    if ($hasLocalLayout) 
     178    { 
     179      // the user set a decorator in view.yml for this action 
     180      $data .= <<<EOF 
     181 
     182  else 
     183  { 
     184    \$this->setDecoratorTemplate('' == '$layout' ? false : '$layout'.\$this->getExtension()); 
     185  } 
     186 
     187EOF; 
     188    } 
     189    else 
     190    { 
     191      // no specific configuration 
     192      // set the layout to the 'all' view.yml value except if: 
     193      //   * the decorator template has already been set by "someone" (via view.configure_format for example) 
     194      //   * the request is an XMLHttpRequest request 
     195      $data .= <<<EOF 
     196 
     197  else if (is_null(\$this->getDecoratorTemplate()) && !\$this->context->getRequest()->isXmlHttpRequest()) 
     198  { 
     199    \$this->setDecoratorTemplate('' == '$layout' ? false : '$layout'.\$this->getExtension()); 
     200  } 
     201 
     202EOF; 
    176203    } 
    177204 
  • branches/1.1/lib/request/sfWebRequest.class.php

    r7792 r8227  
    3131    $getParameters          = null, 
    3232    $postParameters         = null, 
    33     $requestParameters      = null; 
     33    $requestParameters      = null, 
     34    $formats                = array(), 
     35    $format                 = null; 
    3436 
    3537  /** 
     
    8082      // set the default method 
    8183      $this->setMethod(self::GET); 
     84    } 
     85 
     86    foreach ($this->getAttribute('formats', array()) as $format => $mimeTypes) 
     87    { 
     88      $this->setFormat($format, $mimeTypes); 
    8289    } 
    8390 
     
    900907 
    901908  /** 
     909   * Gets the mime type associated with the format. 
     910   * 
     911   * @param  string The format 
     912   * 
     913   * @return string The associated mime type (null if not found) 
     914   */ 
     915  public function getMimeType($format) 
     916  { 
     917    return isset($this->formats[$format]) ? $this->formats[$format][0] : null; 
     918  } 
     919 
     920  /** 
     921   * Gets the format associated with the mime type. 
     922   * 
     923   * @param  string The associated mime type 
     924   * 
     925   * @return string The format (null if not found) 
     926   */ 
     927  public function getFormat($mimeType) 
     928  { 
     929    foreach ($this->formats as $format => $mimeTypes) 
     930    { 
     931      if (in_array($mimeType, $mimeTypes)) 
     932      { 
     933        return $format; 
     934      } 
     935    } 
     936 
     937    return null; 
     938  } 
     939 
     940  /** 
     941   * Associates a format with mime types. 
     942   * 
     943   * @param string       The format 
     944   * @param string|array The associated mime types (the preferred one must be the first as it will be used as the content type) 
     945   */ 
     946  public function setFormat($format, $mimeTypes) 
     947  { 
     948    $this->formats[$format] = is_array($mimeTypes) ? $mimeTypes : array($mimeTypes); 
     949  } 
     950 
     951  /** 
     952   * Sets the request format. 
     953   * 
     954   * @param string The request format 
     955   */ 
     956  public function setRequestFormat($format) 
     957  { 
     958    $this->format = $format; 
     959  } 
     960 
     961  /** 
     962   * Gets the request format. 
     963   * 
     964   * If no format is defined by the user, it defaults to the sf_format request parameter if available. 
     965   * 
     966   * @return string The request format 
     967   */ 
     968  public function getRequestFormat() 
     969  { 
     970    if (is_null($this->format)) 
     971    { 
     972      if ($this->getParameter('sf_format')) 
     973      { 
     974        $this->setRequestFormat($this->getParameter('sf_format')); 
     975      } 
     976      else 
     977      { 
     978        $acceptableContentTypes = $this->getAcceptableContentTypes(); 
     979 
     980        // skip if no acceptable content types or browsers 
     981        if (isset($acceptableContentTypes[0]) && 'text/xml' != $acceptableContentTypes[0]) 
     982        { 
     983          $this->setRequestFormat($this->getFormat($acceptableContentTypes[0])); 
     984        } 
     985      } 
     986    } 
     987 
     988    return $this->format; 
     989  } 
     990 
     991  /** 
    902992   * Parses the request parameters. 
    903993   * 
  • branches/1.1/lib/view/sfView.class.php

    r8138 r8227  
    125125    $this->parameterHolder->add(sfConfig::get('mod_'.strtolower($moduleName).'_view_param', array())); 
    126126 
     127    $request = $context->getRequest(); 
     128    if (!is_null($format = $request->getRequestFormat())) 
     129    { 
     130      if ('html' != $format) 
     131      { 
     132        $this->setExtension('.'.$format.$this->getExtension()); 
     133      } 
     134 
     135      if ($mimeType = $request->getMimeType($format)) 
     136      { 
     137        $this->context->getResponse()->setContentType($mimeType); 
     138        $this->setDecorator(false); 
     139      } 
     140 
     141      $this->dispatcher->notify(new sfEvent($this, 'view.configure_format', array('format' => $format, 'response' => $context->getResponse(), 'request' => $context->getRequest()))); 
     142    } 
     143 
    127144    // include view configuration 
    128145    $this->configure(); 
     
    311328  { 
    312329    $this->decorator = (boolean) $boolean; 
     330 
     331    if (false === $boolean) 
     332    { 
     333      $this->decoratorTemplate = false; 
     334    } 
    313335  } 
    314336 
     
    368390    if (false === $template) 
    369391    { 
    370       $this->decorator = false
     392      $this->setDecorator(false)
    371393 
    372394      return; 
  • branches/1.1/test/functional/fixtures/project/apps/frontend/config/routing.yml

    r2069 r8227  
     1format: 
     2  url:   /format_test.:sf_format 
     3  param: { module: format, action: index, sf_format: html } 
     4 
    15# default rules 
    26homepage: 
  • branches/1.1/test/unit/request/sfWebRequestTest.php

    r6690 r8227  
    1111require_once(dirname(__FILE__).'/../../bootstrap/unit.php'); 
    1212 
    13 $t = new lime_test(16, new lime_output_color()); 
     13$t = new lime_test(25, new lime_output_color()); 
    1414 
    1515class myRequest extends sfWebRequest 
     
    8383$t->is($request->splitHttpAcceptHeader('a,b;q=0.7,c;q=0.3'), array('a', 'b', 'c'), '->splitHttpAcceptHeader() strips the q value'); 
    8484$t->is($request->splitHttpAcceptHeader('a;q=0.1,b,c;q=0.3'), array('b', 'c', 'a'), '->splitHttpAcceptHeader() sorts values by the q value'); 
     85 
     86// ->getRequestFormat() ->setRequestFormat() 
     87$t->diag('->getRequestFormat() ->setRequestFormat()'); 
     88 
     89$t->ok(is_null($request->getRequestFormat()), '->getRequestFormat() returns null if the format is not defined in the request'); 
     90$request->setParameter('sf_format', 'js'); 
     91$t->is($request->getRequestFormat(), 'js', '->getRequestFormat() returns the request format'); 
     92 
     93$request->setRequestFormat('css'); 
     94$t->is($request->getRequestFormat(), 'css', '->setRequestFormat() sets the request format'); 
     95 
     96$request->acceptableContentTypes = null; 
     97$_SERVER['HTTP_ACCEPT'] = 'application/json'; 
     98$request->setFormat('json', 'application/json'); 
     99$request->setParameter('sf_format', null); 
     100$request->setRequestFormat(null); 
     101$t->is($request->getRequestFormat(), 'json', '->getRequestFormat() uses the Accept HTTP header to guess the format'); 
     102 
     103// ->getFormat() ->setFormat() 
     104$t->diag('->getFormat() ->setFormat()'); 
     105$request->setFormat('js', 'application/x-javascript'); 
     106$t->is($request->getFormat('application/x-javascript'), 'js', '->getFormat() returns the format for the given mime type'); 
     107$request->setFormat('js', array('application/x-javascript', 'text/js')); 
     108$t->is($request->getFormat('text/js'), 'js', '->setFormat() can take an array of mime types'); 
     109$t->is($request->getFormat('foo/bar'), null, '->getFormat() returns null if the mime type does not exist'); 
     110 
     111// ->getMimeType() 
     112$t->diag('->getMimeType()'); 
     113$t->is($request->getMimeType('js'), 'application/x-javascript', '->getMimeType() returns the first mime type for the given format'); 
     114$t->is($request->getMimeType('foo'), null, '->getMimeType() returns null if the format does not exist'); 
  • branches/1.1/test/unit/sfContextMock.class.php

    r7616 r8227  
    2525    $sessionPath = ''; 
    2626 
    27   static public function getInstance($factories = array()
     27  static public function getInstance($factories = array(), $force = false
    2828  { 
    29     if (!isset(self::$instance)
     29    if (!isset(self::$instance) || $force
    3030    { 
    3131      self::$instance = new sfContext(); 
  • branches/1.1/test/unit/view/sfViewTest.php

    r8206 r8227  
    1212require_once($_test_dir.'/unit/sfContextMock.class.php'); 
    1313 
    14 $t = new lime_test(14, new lime_output_color()); 
     14$t = new lime_test(19, new lime_output_color()); 
    1515 
    1616class myView extends sfView 
    1717{ 
    18   function execute () {} 
    19   function configure () {} 
    20   function getEngine () {} 
    21   function render () {} 
     18  function execute() {} 
     19  function configure() {} 
     20  function getEngine() {} 
     21  function render() {} 
    2222} 
    2323 
    24 $context = sfContext::getInstance(); 
     24class configuredView extends myView 
     25
     26  static public $isDecorated = false; 
     27 
     28  function initialize($context, $moduleName, $actionName, $viewName) 
     29  { 
     30    $this->setDecorator(self::$isDecorated); 
     31 
     32    parent::initialize($context, $moduleName, $actionName, $viewName); 
     33  } 
     34
     35 
     36$context = sfContext::getInstance(array('request' => 'sfWebRequest', 'response' => 'sfWebResponse')); 
     37 
    2538$view = new myView($context, '', '', ''); 
    2639 
     
    3043$view->setDecorator(true); 
    3144$t->is($view->isDecorator(), true, '->setDecorator() sets the decorator status for the view'); 
     45 
     46// format 
     47$t->diag('format'); 
     48$context = sfContext::getInstance(array('request' => 'sfWebRequest', 'response' => 'sfWebResponse'), true); 
     49$context->getRequest()->setFormat('js', 'application/x-javascript'); 
     50$context->getRequest()->setRequestFormat('js'); 
     51configuredView::$isDecorated = true; 
     52$view = new configuredView($context, '', '', ''); 
     53$t->is($view->isDecorator(), false, '->initialize() uses the format to configure the view'); 
     54$t->is($context->getResponse()->getContentType(), 'application/x-javascript', '->initialize() uses the format to configure the view'); 
     55$t->is($view->getExtension(), '.js.php', '->initialize() uses the format to configure the view'); 
     56$context = sfContext::getInstance(array('request' => 'sfWebRequest', 'response' => 'sfWebResponse'), true); 
     57$context->getEventDispatcher()->connect('view.configure_format', 'configure_format'); 
     58 
     59$context->getRequest()->setRequestFormat('js'); 
     60configuredView::$isDecorated = true; 
     61$view = new configuredView($context, '', '', ''); 
     62$t->is($view->isDecorator(), true, '->initialize() uses the format to configure the view'); 
     63$t->is($context->getResponse()->getContentType(), 'application/javascript', '->initialize() uses the format to configure the view'); 
    3264 
    3365// parameter holder proxy 
     
    4072$dispatcherTest = new sfEventDispatcherTest($t); 
    4173$dispatcherTest->launchTests($context->getEventDispatcher(), $view, 'view'); 
     74 
     75function configure_format(sfEvent $event) 
     76{ 
     77  $event->getSubject()->setDecorator(true); 
     78  $event['response']->setContentType('application/javascript'); 
     79 
     80  return true; 
     81}