Source for file list.php

Documentation is available at list.php

  1. <?php
  2. /**
  3.  * @package     Joomla.Legacy
  4.  * @subpackage  Model
  5.  *
  6.  * @copyright   Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
  7.  * @license     GNU General Public License version 2 or later; see LICENSE
  8.  */
  9.  
  10. defined('JPATH_PLATFORM'or die;
  11.  
  12. /**
  13.  * Model class for handling lists of items.
  14.  *
  15.  * @package     Joomla.Legacy
  16.  * @subpackage  Model
  17.  * @since       12.2
  18.  */
  19. class JModelList extends JModelLegacy
  20. {
  21.     /**
  22.      * Internal memory based cache array of data.
  23.      *
  24.      * @var    array 
  25.      * @since  12.2
  26.      */
  27.     protected $cache = array();
  28.  
  29.     /**
  30.      * Context string for the model type.  This is used to handle uniqueness
  31.      * when dealing with the getStoreId() method and caching data structures.
  32.      *
  33.      * @var    string 
  34.      * @since  12.2
  35.      */
  36.     protected $context = null;
  37.  
  38.     /**
  39.      * Valid filter fields or ordering.
  40.      *
  41.      * @var    array 
  42.      * @since  12.2
  43.      */
  44.     protected $filter_fields = array();
  45.  
  46.     /**
  47.      * An internal cache for the last query used.
  48.      *
  49.      * @var    JDatabaseQuery 
  50.      * @since  12.2
  51.      */
  52.     protected $query = array();
  53.  
  54.     /**
  55.      * Name of the filter form to load
  56.      *
  57.      * @var    string 
  58.      * @since  3.2
  59.      */
  60.     protected $filterFormName = null;
  61.  
  62.     /**
  63.      * Associated HTML form
  64.      *
  65.      * @var  string 
  66.      */
  67.     protected $htmlFormName = 'adminForm';
  68.  
  69.     /**
  70.      * Constructor.
  71.      *
  72.      * @param   array  $config  An optional associative array of configuration settings.
  73.      *
  74.      * @see     JModelLegacy
  75.      * @since   12.2
  76.      */
  77.     public function __construct($config array())
  78.     {
  79.         parent::__construct($config);
  80.  
  81.         // Add the ordering filtering fields white list.
  82.         if (isset($config['filter_fields']))
  83.         {
  84.             $this->filter_fields = $config['filter_fields'];
  85.         }
  86.  
  87.         // Guess the context as Option.ModelName.
  88.         if (empty($this->context))
  89.         {
  90.             $this->context = strtolower($this->option . '.' $this->getName());
  91.         }
  92.     }
  93.  
  94.     /**
  95.      * Method to cache the last query constructed.
  96.      *
  97.      * This method ensures that the query is constructed only once for a given state of the model.
  98.      *
  99.      * @return  JDatabaseQuery  A JDatabaseQuery object
  100.      *
  101.      * @since   12.2
  102.      */
  103.     protected function _getListQuery()
  104.     {
  105.         // Capture the last store id used.
  106.         static $lastStoreId;
  107.  
  108.         // Compute the current store id.
  109.         $currentStoreId $this->getStoreId();
  110.  
  111.         // If the last store id is different from the current, refresh the query.
  112.         if ($lastStoreId != $currentStoreId || empty($this->query))
  113.         {
  114.             $lastStoreId $currentStoreId;
  115.             $this->query = $this->getListQuery();
  116.         }
  117.  
  118.         return $this->query;
  119.     }
  120.  
  121.     /**
  122.      * Function to get the active filters
  123.      *
  124.      * @return  array  Associative array in the format: array('filter_published' => 0)
  125.      *
  126.      * @since   3.2
  127.      */
  128.     public function getActiveFilters()
  129.     {
  130.         $activeFilters array();
  131.  
  132.         if (!empty($this->filter_fields))
  133.         {
  134.             foreach ($this->filter_fields as $filter)
  135.             {
  136.                 $filterName 'filter.' $filter;
  137.  
  138.                 if (property_exists($this->state$filterName&& (!empty($this->state->{$filterName}|| is_numeric($this->state->{$filterName})))
  139.                 {
  140.                     $activeFilters[$filter$this->state->get($filterName);
  141.                 }
  142.             }
  143.         }
  144.  
  145.         return $activeFilters;
  146.     }
  147.  
  148.     /**
  149.      * Method to get an array of data items.
  150.      *
  151.      * @return  mixed  An array of data items on success, false on failure.
  152.      *
  153.      * @since   12.2
  154.      */
  155.     public function getItems()
  156.     {
  157.         // Get a storage key.
  158.         $store $this->getStoreId();
  159.  
  160.         // Try to load the data from internal storage.
  161.         if (isset($this->cache[$store]))
  162.         {
  163.             return $this->cache[$store];
  164.         }
  165.  
  166.         // Load the list items.
  167.         $query $this->_getListQuery();
  168.  
  169.         try
  170.         {
  171.             $items $this->_getList($query$this->getStart()$this->getState('list.limit'));
  172.         }
  173.         catch (RuntimeException $e)
  174.         {
  175.             $this->setError($e->getMessage());
  176.  
  177.             return false;
  178.         }
  179.  
  180.         // Add the items to the internal cache.
  181.         $this->cache[$store$items;
  182.  
  183.         return $this->cache[$store];
  184.     }
  185.  
  186.     /**
  187.      * Method to get a JDatabaseQuery object for retrieving the data set from a database.
  188.      *
  189.      * @return  JDatabaseQuery   A JDatabaseQuery object to retrieve the data set.
  190.      *
  191.      * @since   12.2
  192.      */
  193.     protected function getListQuery()
  194.     {
  195.         $db $this->getDbo();
  196.         $query $db->getQuery(true);
  197.  
  198.         return $query;
  199.     }
  200.  
  201.     /**
  202.      * Method to get a JPagination object for the data set.
  203.      *
  204.      * @return  JPagination  A JPagination object for the data set.
  205.      *
  206.      * @since   12.2
  207.      */
  208.     public function getPagination()
  209.     {
  210.         // Get a storage key.
  211.         $store $this->getStoreId('getPagination');
  212.  
  213.         // Try to load the data from internal storage.
  214.         if (isset($this->cache[$store]))
  215.         {
  216.             return $this->cache[$store];
  217.         }
  218.  
  219.         // Create the pagination object.
  220.         $limit = (int) $this->getState('list.limit'- (int) $this->getState('list.links');
  221.         $page new JPagination($this->getTotal()$this->getStart()$limit);
  222.  
  223.         // Add the object to the internal cache.
  224.         $this->cache[$store$page;
  225.  
  226.         return $this->cache[$store];
  227.     }
  228.  
  229.     /**
  230.      * Method to get a store id based on the model configuration state.
  231.      *
  232.      * This is necessary because the model is used by the component and
  233.      * different modules that might need different sets of data or different
  234.      * ordering requirements.
  235.      *
  236.      * @param   string  $id  An identifier string to generate the store id.
  237.      *
  238.      * @return  string  A store id.
  239.      *
  240.      * @since   12.2
  241.      */
  242.     protected function getStoreId($id '')
  243.     {
  244.         // Add the list state to the store id.
  245.         $id .= ':' $this->getState('list.start');
  246.         $id .= ':' $this->getState('list.limit');
  247.         $id .= ':' $this->getState('list.ordering');
  248.         $id .= ':' $this->getState('list.direction');
  249.  
  250.         return md5($this->context ':' $id);
  251.     }
  252.  
  253.     /**
  254.      * Method to get the total number of items for the data set.
  255.      *
  256.      * @return  integer  The total number of items available in the data set.
  257.      *
  258.      * @since   12.2
  259.      */
  260.     public function getTotal()
  261.     {
  262.         // Get a storage key.
  263.         $store $this->getStoreId('getTotal');
  264.  
  265.         // Try to load the data from internal storage.
  266.         if (isset($this->cache[$store]))
  267.         {
  268.             return $this->cache[$store];
  269.         }
  270.  
  271.         // Load the total.
  272.         $query $this->_getListQuery();
  273.  
  274.         try
  275.         {
  276.             $total = (int) $this->_getListCount($query);
  277.         }
  278.         catch (RuntimeException $e)
  279.         {
  280.             $this->setError($e->getMessage());
  281.  
  282.             return false;
  283.         }
  284.  
  285.         // Add the total to the internal cache.
  286.         $this->cache[$store$total;
  287.  
  288.         return $this->cache[$store];
  289.     }
  290.  
  291.     /**
  292.      * Method to get the starting number of items for the data set.
  293.      *
  294.      * @return  integer  The starting number of items available in the data set.
  295.      *
  296.      * @since   12.2
  297.      */
  298.     public function getStart()
  299.     {
  300.         $store $this->getStoreId('getstart');
  301.  
  302.         // Try to load the data from internal storage.
  303.         if (isset($this->cache[$store]))
  304.         {
  305.             return $this->cache[$store];
  306.         }
  307.  
  308.         $start $this->getState('list.start');
  309.         $limit $this->getState('list.limit');
  310.         $total $this->getTotal();
  311.  
  312.         if ($start $total $limit)
  313.         {
  314.             $start max(0(int) (ceil($total $limit1$limit);
  315.         }
  316.  
  317.         // Add the total to the internal cache.
  318.         $this->cache[$store$start;
  319.  
  320.         return $this->cache[$store];
  321.     }
  322.  
  323.     /**
  324.      * Get the filter form
  325.      *
  326.      * @param   array    $data      data
  327.      * @param   boolean  $loadData  load current data
  328.      *
  329.      * @return  JForm/false  the JForm object or false
  330.      *
  331.      * @since   3.2
  332.      */
  333.     public function getFilterForm($data array()$loadData true)
  334.     {
  335.         $form null;
  336.  
  337.         // Try to locate the filter form automatically. Example: ContentModelArticles => "filter_articles"
  338.         if (empty($this->filterFormName))
  339.         {
  340.             $classNameParts explode('Model'get_called_class());
  341.  
  342.             if (count($classNameParts== 2)
  343.             {
  344.                 $this->filterFormName 'filter_' strtolower($classNameParts[1]);
  345.             }
  346.         }
  347.  
  348.         if (!empty($this->filterFormName))
  349.         {
  350.             // Get the form.
  351.             $form $this->loadForm($this->context '.filter'$this->filterFormNamearray('control' => '''load_data' => $loadData));
  352.         }
  353.  
  354.         return $form;
  355.     }
  356.  
  357.     /**
  358.      * Method to get a form object.
  359.      *
  360.      * @param   string   $name     The name of the form.
  361.      * @param   string   $source   The form source. Can be XML string if file flag is set to false.
  362.      * @param   array    $options  Optional array of options for the form creation.
  363.      * @param   boolean  $clear    Optional argument to force load a new form.
  364.      * @param   string   $xpath    An optional xpath to search for the fields.
  365.      *
  366.      * @return  mixed  JForm object on success, False on error.
  367.      *
  368.      * @see     JForm
  369.      * @since   3.2
  370.      */
  371.     protected function loadForm($name$source null$options array()$clear false$xpath false)
  372.     {
  373.         // Handle the optional arguments.
  374.         $options['control'JArrayHelper::getValue($options'control'false);
  375.  
  376.         // Create a signature hash.
  377.         $hash md5($source serialize($options));
  378.  
  379.         // Check if we can use a previously loaded form.
  380.         if (isset($this->_forms[$hash]&& !$clear)
  381.         {
  382.             return $this->_forms[$hash];
  383.         }
  384.  
  385.         // Get the form.
  386.         JForm::addFormPath(JPATH_COMPONENT '/models/forms');
  387.         JForm::addFieldPath(JPATH_COMPONENT '/models/fields');
  388.  
  389.         try
  390.         {
  391.             $form JForm::getInstance($name$source$optionsfalse$xpath);
  392.  
  393.             if (isset($options['load_data']&& $options['load_data'])
  394.             {
  395.                 // Get the data for the form.
  396.                 $data $this->loadFormData();
  397.             }
  398.             else
  399.             {
  400.                 $data array();
  401.             }
  402.  
  403.             // Allow for additional modification of the form, and events to be triggered.
  404.             // We pass the data because plugins may require it.
  405.             $this->preprocessForm($form$data);
  406.  
  407.             // Load the data into the form after the plugins have operated.
  408.             $form->bind($data);
  409.         }
  410.         catch (Exception $e)
  411.         {
  412.             $this->setError($e->getMessage());
  413.  
  414.             return false;
  415.         }
  416.  
  417.         // Store the form for later.
  418.         $this->_forms[$hash$form;
  419.  
  420.         return $form;
  421.     }
  422.  
  423.     /**
  424.      * Method to get the data that should be injected in the form.
  425.      *
  426.      * @return    mixed    The data for the form.
  427.      *
  428.      * @since    3.2
  429.      */
  430.     protected function loadFormData()
  431.     {
  432.         // Check the session for previously entered form data.
  433.         $data JFactory::getApplication()->getUserState($this->contextnew stdClass);
  434.  
  435.         // Pre-fill the list options
  436.         if (!property_exists($data'list'))
  437.         {
  438.             $data->list array(
  439.                 'direction' => $this->state->{'list.direction'},
  440.                 'limit'     => $this->state->{'list.limit'},
  441.                 'ordering'  => $this->state->{'list.ordering'},
  442.                 'start'     => $this->state->{'list.start'}
  443.             );
  444.         }
  445.  
  446.         return $data;
  447.     }
  448.  
  449.     /**
  450.      * Method to auto-populate the model state.
  451.      *
  452.      * This method should only be called once per instantiation and is designed
  453.      * to be called on the first call to the getState() method unless the model
  454.      * configuration flag to ignore the request is set.
  455.      *
  456.      * Note. Calling getState in this method will result in recursion.
  457.      *
  458.      * @param   string  $ordering   An optional ordering field.
  459.      * @param   string  $direction  An optional direction (asc|desc).
  460.      *
  461.      * @return  void 
  462.      *
  463.      * @since   12.2
  464.      */
  465.     protected function populateState($ordering null$direction null)
  466.     {
  467.         // If the context is set, assume that stateful lists are used.
  468.         if ($this->context)
  469.         {
  470.             $app JFactory::getApplication();
  471.  
  472.             // Receive & set filters
  473.             if ($filters $app->getUserStateFromRequest($this->context '.filter''filter'array()'array'))
  474.             {
  475.                 foreach ($filters as $name => $value)
  476.                 {
  477.                     $this->setState('filter.' $name$value);
  478.                 }
  479.             }
  480.  
  481.             $limit 0;
  482.  
  483.             // Receive & set list options
  484.             if ($list $app->getUserStateFromRequest($this->context '.list''list'array()'array'))
  485.             {
  486.                 foreach ($list as $name => $value)
  487.                 {
  488.                     // Extra validations
  489.                     switch ($name)
  490.                     {
  491.                         case 'fullordering':
  492.                             $orderingParts explode(' '$value);
  493.  
  494.                             if (count($orderingParts>= 2)
  495.                             {
  496.                                 // Latest part will be considered the direction
  497.                                 $fullDirection end($orderingParts);
  498.  
  499.                                 if (in_array(strtoupper($fullDirection)array('ASC''DESC''')))
  500.                                 {
  501.                                     $this->setState('list.direction'$fullDirection);
  502.                                 }
  503.  
  504.                                 unset($orderingParts[count($orderingParts1]);
  505.  
  506.                                 // The rest will be the ordering
  507.                                 $fullOrdering implode(' '$orderingParts);
  508.  
  509.                                 if (in_array($fullOrdering$this->filter_fields))
  510.                                 {
  511.                                     $this->setState('list.ordering'$fullOrdering);
  512.                                 }
  513.                             }
  514.                             else
  515.                             {
  516.                                 $this->setState('list.ordering'$ordering);
  517.                                 $this->setState('list.direction'$direction);
  518.                             }
  519.                             break;
  520.  
  521.                         case 'ordering':
  522.                             if (!in_array($value$this->filter_fields))
  523.                             {
  524.                                 $value $ordering;
  525.                             }
  526.                             break;
  527.  
  528.                         case 'direction':
  529.                             if (!in_array(strtoupper($value)array('ASC''DESC''')))
  530.                             {
  531.                                 $value $direction;
  532.                             }
  533.                             break;
  534.  
  535.                         case 'limit':
  536.                             $limit $value;
  537.                             break;
  538.  
  539.                         // Just to keep the default case
  540.                         default:
  541.                             $value $value;
  542.                             break;
  543.                     }
  544.  
  545.                     $this->setState('list.' $name$value);
  546.                 }
  547.             }
  548.             else
  549.             // Keep B/C for components previous to jform forms for filters
  550.             {
  551.                 // Pre-fill the limits
  552.                 $limit $app->getUserStateFromRequest('global.list.limit''limit'$app->getCfg('list_limit')'uint');
  553.                 $this->setState('list.limit'$limit);
  554.  
  555.                 // Check if the ordering field is in the white list, otherwise use the incoming value.
  556.                 $value $app->getUserStateFromRequest($this->context '.ordercol''filter_order'$ordering);
  557.  
  558.                 if (!in_array($value$this->filter_fields))
  559.                 {
  560.                     $value $ordering;
  561.                     $app->setUserState($this->context '.ordercol'$value);
  562.                 }
  563.  
  564.                 $this->setState('list.ordering'$value);
  565.  
  566.                 // Check if the ordering direction is valid, otherwise use the incoming value.
  567.                 $value $app->getUserStateFromRequest($this->context '.orderdirn''filter_order_Dir'$direction);
  568.  
  569.                 if (!in_array(strtoupper($value)array('ASC''DESC''')))
  570.                 {
  571.                     $value $direction;
  572.                     $app->setUserState($this->context '.orderdirn'$value);
  573.                 }
  574.  
  575.                 $this->setState('list.direction'$value);
  576.             }
  577.  
  578.             // Support old ordering field
  579.             $oldOrdering $app->input->get('filter_order');
  580.  
  581.             if (!empty($oldOrdering&& in_array($value$this->filter_fields))
  582.             {
  583.                 $this->setState('list.ordering'$oldOrdering);
  584.             }
  585.  
  586.             // Support old direction field
  587.             $oldDirection $app->input->get('filter_order_Dir');
  588.  
  589.             if (!empty($oldDirection&& in_array(strtoupper($oldDirection)array('ASC''DESC''')))
  590.             {
  591.                 $this->setState('list.direction'$oldDirection);
  592.             }
  593.  
  594.             $value $app->getUserStateFromRequest($this->context '.limitstart''limitstart'0);
  595.             $limitstart ($limit != (floor($value $limit$limit0);
  596.             $this->setState('list.start'$limitstart);
  597.         }
  598.         else
  599.         {
  600.             $this->setState('list.start'0);
  601.             $this->setState('list.limit'0);
  602.         }
  603.     }
  604.  
  605.     /**
  606.      * Method to allow derived classes to preprocess the form.
  607.      *
  608.      * @param   JForm   $form   A JForm object.
  609.      * @param   mixed   $data   The data expected for the form.
  610.      * @param   string  $group  The name of the plugin group to import (defaults to "content").
  611.      *
  612.      * @return  void 
  613.      *
  614.      * @since   3.2
  615.      * @throws  Exception if there is an error in the form event.
  616.      */
  617.     protected function preprocessForm(JForm $form$data$group 'content')
  618.     {
  619.         // Import the appropriate plugin group.
  620.         JPluginHelper::importPlugin($group);
  621.  
  622.         // Get the dispatcher.
  623.         $dispatcher JDispatcher::getInstance();
  624.  
  625.         // Trigger the form preparation event.
  626.         $results $dispatcher->trigger('onContentPrepareForm'array($form$data));
  627.  
  628.         // Check for errors encountered while preparing the form.
  629.         if (count($results&& in_array(false$resultstrue))
  630.         {
  631.             // Get the last error.
  632.             $error $dispatcher->getError();
  633.  
  634.             if (!($error instanceof Exception))
  635.             {
  636.                 throw new Exception($error);
  637.             }
  638.         }
  639.     }
  640.  
  641.     /**
  642.      * Gets the value of a user state variable and sets it in the session
  643.      *
  644.      * This is the same as the method in JApplication except that this also can optionally
  645.      * force you back to the first page when a filter has changed
  646.      *
  647.      * @param   string   $key        The key of the user state variable.
  648.      * @param   string   $request    The name of the variable passed in a request.
  649.      * @param   string   $default    The default value for the variable if not found. Optional.
  650.      * @param   string   $type       Filter for the variable, for valid values see {@link JFilterInput::clean()}. Optional.
  651.      * @param   boolean  $resetPage  If true, the limitstart in request is set to zero
  652.      *
  653.      * @return  The request user state.
  654.      *
  655.      * @since   12.2
  656.      */
  657.     public function getUserStateFromRequest($key$request$default null$type 'none'$resetPage true)
  658.     {
  659.         $app JFactory::getApplication();
  660.         $input     $app->input;
  661.         $old_state $app->getUserState($key);
  662.         $cur_state (!is_null($old_state)) $old_state $default;
  663.         $new_state $input->get($requestnull$type);
  664.  
  665.         if (($cur_state != $new_state&& ($resetPage))
  666.         {
  667.             $input->set('limitstart'0);
  668.         }
  669.  
  670.         // Save the new value only if it is set in this request.
  671.         if ($new_state !== null)
  672.         {
  673.             $app->setUserState($key$new_state);
  674.         }
  675.         else
  676.         {
  677.             $new_state $cur_state;
  678.         }
  679.  
  680.         return $new_state;
  681.     }
  682. }

Documentation generated on Tue, 19 Nov 2013 15:07:17 +0100 by phpDocumentor 1.4.3