Source for file table.php

Documentation is available at table.php

  1. <?php
  2. /**
  3.  * @package     FrameworkOnFramework
  4.  * @subpackage  table
  5.  * @copyright   Copyright (C) 2010 - 2012 Akeeba Ltd. All rights reserved.
  6.  * @license     GNU General Public License version 2 or later; see LICENSE.txt
  7.  */
  8. // Protect from unauthorized access
  9. defined('_JEXEC'or die;
  10.  
  11. /**
  12.  * Normally this shouldn't be required. Some PHP versions, however, seem to
  13.  * require this. Why? No idea whatsoever. If I remove it, FOF crashes on some
  14.  * hosts. Same PHP version on another host and no problem occurs. Any takers?
  15.  */
  16. if (class_exists('FOFTable'false))
  17. {
  18.     return;
  19. }
  20.  
  21. if (!interface_exists('JTableInterface'true))
  22. {
  23.     interface JTableInterface {}
  24. }
  25.  
  26. /**
  27.  * FrameworkOnFramework Table class. The Table is one part controller, one part
  28.  * model and one part data adapter. It's supposed to handle operations for single
  29.  * records.
  30.  *
  31.  * @package  FrameworkOnFramework
  32.  * @since    1.0
  33.  */
  34. class FOFTable extends JObject implements JTableInterface
  35. {
  36.     /**
  37.      * Cache array for instances
  38.      *
  39.      * @var    array 
  40.      */
  41.     private static $instances array();
  42.  
  43.     /**
  44.      * Include paths for searching for FOFTable classes.
  45.      *
  46.      * @var    array 
  47.      */
  48.     private static $_includePaths array();
  49.  
  50.     /**
  51.      * The configuration parameters array
  52.      *
  53.      * @var  array 
  54.      */
  55.     protected $config = array();
  56.  
  57.     /**
  58.      * Name of the database table to model.
  59.      *
  60.      * @var    string 
  61.      */
  62.     protected $_tbl = '';
  63.  
  64.     /**
  65.      * Name of the primary key field in the table.
  66.      *
  67.      * @var    string 
  68.      */
  69.     protected $_tbl_key = '';
  70.  
  71.     /**
  72.      * JDatabaseDriver object.
  73.      *
  74.      * @var    JDatabaseDriver 
  75.      */
  76.     protected $_db;
  77.  
  78.     /**
  79.      * Should rows be tracked as ACL assets?
  80.      *
  81.      * @var    boolean 
  82.      */
  83.     protected $_trackAssets = false;
  84.  
  85.     /**
  86.      * Does the resource support joomla tags?
  87.      *
  88.      * @var    boolean 
  89.      */
  90.     protected $_has_tags = false;
  91.  
  92.     /**
  93.      * The rules associated with this record.
  94.      *
  95.      * @var    JAccessRules  A JAccessRules object.
  96.      */
  97.     protected $_rules;
  98.  
  99.     /**
  100.      * Indicator that the tables have been locked.
  101.      *
  102.      * @var    boolean 
  103.      */
  104.     protected $_locked = false;
  105.  
  106.     /**
  107.      * If this is set to true, it triggers automatically plugin events for
  108.      * table actions
  109.      *
  110.      * @var    boolean 
  111.      */
  112.     protected $_trigger_events = false;
  113.  
  114.     /**
  115.      * Table alias used in queries
  116.      *
  117.      * @var    string 
  118.      */
  119.     protected $_tableAlias = false;
  120.  
  121.     /**
  122.      * Array with alias for "special" columns such as ordering, hits etc etc
  123.      *
  124.      * @var    array 
  125.      */
  126.     protected $_columnAlias = array();
  127.  
  128.     /**
  129.      * If set to true, it enabled automatic checks on fields based on columns properties
  130.      *
  131.      * @var    boolean 
  132.      */
  133.     protected $_autoChecks = false;
  134.  
  135.     /**
  136.      * Array with fields that should be skipped by automatic checks
  137.      *
  138.      * @var    array 
  139.      */
  140.     protected $_skipChecks = array();
  141.  
  142.     /**
  143.      * Does the table actually exist? We need that to avoid PHP notices on
  144.      * table-less views.
  145.      *
  146.      * @var    boolean 
  147.      */
  148.     protected $_tableExists = true;
  149.  
  150.     /**
  151.      * The asset key for items in this table. It's usually something in the
  152.      * com_example.viewname format. They asset name will be this key appended
  153.      * with the item's ID, e.g. com_example.viewname.123
  154.      *
  155.      * @var type 
  156.      */
  157.     protected $_assetKey = '';
  158.  
  159.     /**
  160.      * The input data
  161.      *
  162.      * @var    FOFInput 
  163.      */
  164.     protected $input = null;
  165.  
  166.     /**
  167.      * Extended query including joins with other tables
  168.      *
  169.      * @var    JDatabaseQuery 
  170.      */
  171.     protected $_queryJoin = null;
  172.  
  173.     /**
  174.      * The prefix for the table class
  175.      *
  176.      * @var        string 
  177.      */
  178.     protected $_tablePrefix = '';
  179.  
  180.     /**
  181.      * The known fields for this table
  182.      *
  183.      * @var        array 
  184.      */
  185.     protected $knownFields = array();
  186.  
  187.     /**
  188.      * A list of table fields, keyed per table
  189.      *
  190.      * @var array 
  191.      */
  192.     protected static $tableFieldCache array();
  193.  
  194.     /**
  195.      * A list of tables in the database
  196.      *
  197.      * @var array 
  198.      */
  199.     protected static $tableCache array();
  200.  
  201.     /**
  202.      * An instance of FOFConfigProvider to provision configuration overrides
  203.      *
  204.      * @var    FOFConfigProvider 
  205.      */
  206.     protected $configProvider = null;
  207.  
  208.     /**
  209.      * FOFTableDispatcherBehavior for dealing with extra behaviors
  210.      *
  211.      * @var    FOFTableDispatcherBehavior 
  212.      */
  213.     protected $tableDispatcher = null;
  214.  
  215.     /**
  216.      * List of default behaviors to apply to the table
  217.      *
  218.      * @var    array 
  219.      */
  220.     protected $default_behaviors = array('tags''assets');
  221.  
  222.     /**
  223.      * Returns a static object instance of a particular table type
  224.      *
  225.      * @param   string  $type    The table name
  226.      * @param   string  $prefix  The prefix of the table class
  227.      * @param   array   $config  Optional configuration variables
  228.      *
  229.      * @return FOFTable 
  230.      */
  231.     public static function getInstance($type$prefix 'JTable'$config array())
  232.     {
  233.         return self::getAnInstance($type$prefix$config);
  234.     }
  235.  
  236.     /**
  237.      * Returns a static object instance of a particular table type
  238.      *
  239.      * @param   string  $type    The table name
  240.      * @param   string  $prefix  The prefix of the table class
  241.      * @param   array   $config  Optional configuration variables
  242.      *
  243.      * @return FOFTable 
  244.      */
  245.     public static function &getAnInstance($type null$prefix 'JTable'$config array())
  246.     {
  247.         // Make sure $config is an array
  248.         if (is_object($config))
  249.         {
  250.             $config = (array) $config;
  251.         }
  252.         elseif (!is_array($config))
  253.         {
  254.             $config array();
  255.         }
  256.  
  257.         // Guess the component name
  258.         if (!array_key_exists('input'$config))
  259.         {
  260.             $config['input'new FOFInput;
  261.         }
  262.  
  263.         if ($config['input'instanceof FOFInput)
  264.         {
  265.             $tmpInput $config['input'];
  266.         }
  267.         else
  268.         {
  269.             $tmpInput new FOFInput($config['input']);
  270.         }
  271.  
  272.         $option $tmpInput->getCmd('option''');
  273.         $tmpInput->set('option'$option);
  274.         $config['input'$tmpInput;
  275.  
  276.         if (!in_array($prefixarray('Table''JTable')))
  277.         {
  278.             preg_match('/(.*)Table$/'$prefix$m);
  279.             $option 'com_' strtolower($m[1]);
  280.         }
  281.  
  282.         if (array_key_exists('option'$config))
  283.         {
  284.             $option $config['option'];
  285.         }
  286.  
  287.         $config['option'$option;
  288.  
  289.         if (!array_key_exists('view'$config))
  290.         {
  291.             $config['view'$config['input']->getCmd('view''cpanel');
  292.         }
  293.  
  294.         if (is_null($type))
  295.         {
  296.             if ($prefix == 'JTable')
  297.             {
  298.                 $prefix 'Table';
  299.             }
  300.  
  301.             $type $config['view'];
  302.         }
  303.  
  304.         $type       preg_replace('/[^A-Z0-9_\.-]/i'''$type);
  305.         $tableClass $prefix ucfirst($type);
  306.  
  307.         $configProvider new FOFConfigProvider;
  308.         $configProviderKey $option '.views.' FOFInflector::singularize($type'.config.';
  309.  
  310.         if (!array_key_exists($tableClassself::$instances))
  311.         {
  312.             if (!class_exists($tableClass))
  313.             {
  314.                 $componentPaths FOFPlatform::getInstance()->getComponentBaseDirs($config['option']);
  315.  
  316.                 $searchPaths array(
  317.                     $componentPaths['main''/tables',
  318.                     $componentPaths['admin''/tables'
  319.                 );
  320.  
  321.                 if (array_key_exists('tablepath'$config))
  322.                 {
  323.                     array_unshift($searchPaths$config['tablepath']);
  324.                 }
  325.  
  326.                 $altPath $configProvider->get($configProviderKey 'table_path'null);
  327.  
  328.                 if ($altPath)
  329.                 {
  330.                     array_unshift($searchPaths$componentPaths['admin''/' $altPath);
  331.                 }
  332.  
  333.                 JLoader::import('joomla.filesystem.path');
  334.                 $path JPath::find(
  335.                     $searchPathsstrtolower($type'.php'
  336.                 );
  337.  
  338.                 if ($path)
  339.                 {
  340.                     require_once $path;
  341.                 }
  342.             }
  343.  
  344.             if (!class_exists($tableClass))
  345.             {
  346.                 $tableClass 'FOFTable';
  347.             }
  348.  
  349.             $component str_replace('com_'''$config['option']);
  350.             $tbl_common $component '_';
  351.  
  352.             if (!array_key_exists('tbl'$config))
  353.             {
  354.                 $config['tbl'strtolower('#__' $tbl_common strtolower(FOFInflector::pluralize($type)));
  355.             }
  356.  
  357.             $altTbl $configProvider->get($configProviderKey 'tbl'null);
  358.  
  359.             if ($altTbl)
  360.             {
  361.                 $config['tbl'$altTbl;
  362.             }
  363.  
  364.             if (!array_key_exists('tbl_key'$config))
  365.             {
  366.                 $keyName           FOFInflector::singularize($type);
  367.                 $config['tbl_key'strtolower($tbl_common $keyName '_id');
  368.             }
  369.  
  370.             $altTblKey $configProvider->get($configProviderKey 'tbl_key'null);
  371.  
  372.             if ($altTblKey)
  373.             {
  374.                 $config['tbl_key'$altTblKey;
  375.             }
  376.  
  377.             if (!array_key_exists('db'$config))
  378.             {
  379.                 $config['db'JFactory::getDBO();
  380.             }
  381.  
  382.             // Assign the correct table alias
  383.             if (array_key_exists('table_alias'$config))
  384.             {
  385.                 $table_alias $config['table_alias'];
  386.             }
  387.             else
  388.             {
  389.                 $configProviderTableAliasKey $option '.tables.' FOFInflector::singularize($type'.tablealias';
  390.                 $table_alias $configProvider->get($configProviderTableAliasKeyfalse    );
  391.             }
  392.  
  393.             // Can we use the FOF cache?
  394.             if (!array_key_exists('use_table_cache'$config))
  395.             {
  396.                 $config['use_table_cache'FOFPlatform::getInstance()->isGlobalFOFCacheEnabled();
  397.             }
  398.  
  399.             $alt_use_table_cache $configProvider->get($configProviderKey 'use_table_cache'null);
  400.  
  401.             if (!is_null($alt_use_table_cache))
  402.             {
  403.                 $config['use_table_cache'$alt_use_table_cache;
  404.             }
  405.  
  406.             // Create a new table instance
  407.             $instance new $tableClass($config['tbl']$config['tbl_key']$config['db']$config);
  408.             $instance->setInput($tmpInput);
  409.             $instance->setTablePrefix($prefix);
  410.             $instance->setTableAlias($table_alias);
  411.  
  412.             // Determine and set the asset key for this table
  413.             $assetKey 'com_' $component '.' strtolower(FOFInflector::singularize($type));
  414.             $assetKey $configProvider->get($configProviderKey 'asset_key'$assetKey);
  415.             $instance->setAssetKey($assetKey);
  416.  
  417.             if (array_key_exists('trigger_events'$config))
  418.             {
  419.                 $instance->setTriggerEvents($config['trigger_events']);
  420.             }
  421.  
  422.             if (FOFPlatform::getInstance()->checkVersion(JVERSION'3.1''ge'))
  423.             {
  424.                 if (array_key_exists('has_tags'$config))
  425.                 {
  426.                     $instance->setHasTags($config['has_tags']);
  427.                 }
  428.  
  429.                 $altHasTags $configProvider->get($configProviderKey 'has_tags'null);
  430.  
  431.                 if ($altHasTags)
  432.                 {
  433.                     $instance->setHasTags($altHasTags);
  434.                 }
  435.             }
  436.             else
  437.             {
  438.                 $instance->setHasTags(false);
  439.             }
  440.  
  441.             $configProviderFieldmapKey $option '.tables.' FOFInflector::singularize($type'.field';
  442.             $aliases $configProvider->get($configProviderFieldmapKey$instance->_columnAlias);
  443.             $instance->_columnAlias array_merge($instance->_columnAlias$aliases);
  444.  
  445.             self::$instances[$tableClass$instance;
  446.         }
  447.  
  448.         return self::$instances[$tableClass];
  449.     }
  450.  
  451.     /**
  452.      * Force an instance inside class cache. Setting arguments to null nukes all or part of the cache
  453.      *
  454.      * @param    string|null      $key        TableClass to replace. Set it to null to nuke the entire cache
  455.      * @param    FOFTable|null    $instance   Instance to replace. Set it to null to nuke $key instances
  456.      *
  457.      * @return   bool              Did I correctly switch the instance?
  458.      */
  459.     public static function forceInstance($key null$instance null)
  460.     {
  461.         if(is_null($key))
  462.         {
  463.             self::$instances array();
  464.  
  465.             return true;
  466.         }
  467.         elseif($key && isset(self::$instances[$key]))
  468.         {
  469.             // I'm forcing an instance, but it's not a FOFTable, abort! abort!
  470.             if(!$instance || ($instance && $instance instanceof FOFTable))
  471.             {
  472.                 self::$instances[$key$instance;
  473.  
  474.                 return true;
  475.             }
  476.         }
  477.  
  478.         return false;
  479.     }
  480.  
  481.     /**
  482.      * Class Constructor.
  483.      *
  484.      * @param   string           $table   Name of the database table to model.
  485.      * @param   string           $key     Name of the primary key field in the table.
  486.      * @param   JDatabaseDriver  &$db     Database driver
  487.      * @param   array            $config  The configuration parameters array
  488.      */
  489.     public function __construct($table$key&$db$config array())
  490.     {
  491.         $this->_tbl     = $table;
  492.         $this->_tbl_key = $key;
  493.         $this->_db      = $db;
  494.  
  495.         // Make sure the use FOF cache information is in the config
  496.         if (!array_key_exists('use_table_cache'$config))
  497.         {
  498.             $config['use_table_cache'FOFPlatform::getInstance()->isGlobalFOFCacheEnabled();
  499.         }
  500.         $this->config   = $config;
  501.  
  502.         // Load the configuration provider
  503.         $this->configProvider = new FOFConfigProvider;
  504.  
  505.         // Load the behavior dispatcher
  506.         $this->tableDispatcher = new FOFTableDispatcherBehavior;
  507.  
  508.         // Initialise the table properties.
  509.  
  510.         if ($fields $this->getTableFields())
  511.         {
  512.             // Do I have anything joined?
  513.             $j_fields $this->getQueryJoinFields();
  514.  
  515.             if ($j_fields)
  516.             {
  517.                 $fields array_merge($fields$j_fields);
  518.             }
  519.  
  520.             $this->setKnownFields(array_keys($fields)true);
  521.             $this->reset();
  522.         }
  523.         else
  524.         {
  525.             $this->_tableExists = false;
  526.         }
  527.  
  528.         // Get the input
  529.         if (array_key_exists('input'$config))
  530.         {
  531.             if ($config['input'instanceof FOFInput)
  532.             {
  533.                 $this->input = $config['input'];
  534.             }
  535.             else
  536.             {
  537.                 $this->input = new FOFInput($config['input']);
  538.             }
  539.         }
  540.         else
  541.         {
  542.             $this->input = new FOFInput;
  543.         }
  544.  
  545.         // Set the $name/$_name variable
  546.         $component $this->input->getCmd('option''com_foobar');
  547.  
  548.         if (array_key_exists('option'$config))
  549.         {
  550.             $component $config['option'];
  551.         }
  552.  
  553.         $this->input->set('option'$component);
  554.  
  555.         // Apply table behaviors
  556.         $type explode("_"$this->_tbl);
  557.         $type $type[count($type1];
  558.  
  559.         $configKey $component '.tables.' FOFInflector::singularize($type'.behaviors';
  560.  
  561.         if (isset($config['behaviors']))
  562.         {
  563.             $behaviors = (array) $config['behaviors'];
  564.         }
  565.         elseif ($behaviors $this->configProvider->get($configKeynull))
  566.         {
  567.             $behaviors explode(','$behaviors);
  568.         }
  569.         else
  570.         {
  571.             $behaviors $this->default_behaviors;
  572.         }
  573.  
  574.         if (is_array($behaviors&& count($behaviors))
  575.         {
  576.             foreach ($behaviors as $behavior)
  577.             {
  578.                 $this->addBehavior($behavior);
  579.             }
  580.         }
  581.  
  582.         // If we are tracking assets, make sure an access field exists and initially set the default.
  583.         $asset_id_field    $this->getColumnAlias('asset_id');
  584.         $access_field    $this->getColumnAlias('access');
  585.  
  586.         if (in_array($asset_id_field$this->getKnownFields()))
  587.         {
  588.             JLoader::import('joomla.access.rules');
  589.             $this->_trackAssets = true;
  590.         }
  591.  
  592.         // If the acess property exists, set the default.
  593.         if (in_array($access_field$this->getKnownFields()))
  594.         {
  595.             $this->$access_field = (int) JFactory::getConfig()->get('access');
  596.         }
  597.     }
  598.  
  599.     /**
  600.      * Replace the entire known fields array
  601.      *
  602.      * @param   array    $fields      A simple array of known field names
  603.      * @param   boolean  $initialise  Should we initialise variables to null?
  604.      *
  605.      * @return  void 
  606.      */
  607.     public function setKnownFields($fields$initialise false)
  608.     {
  609.         $this->knownFields = $fields;
  610.  
  611.         if ($initialise)
  612.         {
  613.             foreach ($this->knownFields as $field)
  614.             {
  615.                 $this->$field null;
  616.             }
  617.         }
  618.     }
  619.  
  620.     /**
  621.      * Get the known fields array
  622.      *
  623.      * @return  array 
  624.      */
  625.     public function getKnownFields()
  626.     {
  627.         return $this->knownFields;
  628.     }
  629.  
  630.     /**
  631.      * Add a field to the known fields array
  632.      *
  633.      * @param   string   $field       The name of the field to add
  634.      * @param   boolean  $initialise  Should we initialise the variable to null?
  635.      *
  636.      * @return  void 
  637.      */
  638.     public function addKnownField($field$initialise false)
  639.     {
  640.         if (!in_array($field$this->knownFields))
  641.         {
  642.             $this->knownFields[$field;
  643.  
  644.             if ($initialise)
  645.             {
  646.                 $this->$field null;
  647.             }
  648.         }
  649.     }
  650.  
  651.     /**
  652.      * Remove a field from the known fields array
  653.      *
  654.      * @param   string  $field  The name of the field to remove
  655.      *
  656.      * @return  void 
  657.      */
  658.     public function removeKnownField($field)
  659.     {
  660.         if (in_array($field$this->knownFields))
  661.         {
  662.             $pos array_search($field$this->knownFields);
  663.             unset($this->knownFields[$pos]);
  664.         }
  665.     }
  666.  
  667.     /**
  668.      * Adds a behavior to the table
  669.      *
  670.      * @param   string  $name    The name of the behavior
  671.      * @param   array   $config  Optional Behavior configuration
  672.      *
  673.      * @return  boolean 
  674.      */
  675.     public function addBehavior($name$config array())
  676.     {
  677.  
  678.         $behaviorClass 'FOFTableBehavior' ucfirst(strtolower($name));
  679.  
  680.         if (class_exists($behaviorClass&& $this->tableDispatcher)
  681.         {
  682.             $behavior new $behaviorClass($this->tableDispatcher$config);
  683.  
  684.             return true;
  685.         }
  686.  
  687.         return false;
  688.     }
  689.  
  690.     /**
  691.      * Sets the events trigger switch state
  692.      *
  693.      * @param   boolean  $newState  The new state of the switch (what else could it be?)
  694.      *
  695.      * @return  void 
  696.      */
  697.     public function setTriggerEvents($newState false)
  698.     {
  699.         $this->_trigger_events = $newState true false;
  700.     }
  701.  
  702.     /**
  703.      * Gets the events trigger switch state
  704.      *
  705.      * @return  boolean 
  706.      */
  707.     public function getTriggerEvents()
  708.     {
  709.         return $this->_trigger_events;
  710.     }
  711.  
  712.     /**
  713.      * Gets the has tags switch state
  714.      *
  715.      * @return bool 
  716.      */
  717.     public function hasTags()
  718.     {
  719.         return $this->_has_tags;
  720.     }
  721.  
  722.     /**
  723.      * Sets the has tags switch state
  724.      *
  725.      * @param   bool  $newState 
  726.      */
  727.     public function setHasTags($newState false)
  728.     {
  729.         $this->_has_tags = false;
  730.  
  731.         // Tags are available only in 3.1+
  732.         if (FOFPlatform::getInstance()->checkVersion(JVERSION'3.1''ge'))
  733.         {
  734.             $this->_has_tags = $newState true false;
  735.         }
  736.     }
  737.  
  738.     /**
  739.      * Set the class prefix
  740.      *
  741.      * @param string $prefix The prefix
  742.      */
  743.     public function setTablePrefix($prefix)
  744.     {
  745.         $this->_tablePrefix = $prefix;
  746.     }
  747.  
  748.     /**
  749.      * Sets fields to be skipped from automatic checks.
  750.      *
  751.      * @param   array/string  $skip  Fields to be skipped by automatic checks
  752.      *
  753.      * @return void 
  754.      */
  755.     public function setSkipChecks($skip)
  756.     {
  757.         $this->_skipChecks = (array) $skip;
  758.     }
  759.  
  760.     /**
  761.      * Method to load a row from the database by primary key and bind the fields
  762.      * to the FOFTable instance properties.
  763.      *
  764.      * @param   mixed    $keys   An optional primary key value to load the row by, or an array of fields to match.  If not
  765.      *                            set the instance property value is used.
  766.      * @param   boolean  $reset  True to reset the default values before loading the new row.
  767.      *
  768.      * @return  boolean  True if successful. False if row not found.
  769.      *
  770.      * @throws  RuntimeException
  771.      * @throws  UnexpectedValueException
  772.      */
  773.     public function load($keys null$reset true)
  774.     {
  775.         if (!$this->_tableExists)
  776.         {
  777.             $result false;
  778.  
  779.             return $this->onAfterLoad($result);
  780.         }
  781.  
  782.         if (empty($keys))
  783.         {
  784.             // If empty, use the value of the current key
  785.             $keyName $this->_tbl_key;
  786.  
  787.             if (isset($this->$keyName))
  788.             {
  789.                 $keyValue $this->$keyName;
  790.             }
  791.             else
  792.             {
  793.                 $keyValue null;
  794.             }
  795.  
  796.             // If empty primary key there's is no need to load anything
  797.  
  798.             if (empty($keyValue))
  799.             {
  800.                 $result true;
  801.  
  802.                 return $this->onAfterLoad($result);
  803.             }
  804.  
  805.             $keys array($keyName => $keyValue);
  806.         }
  807.         elseif (!is_array($keys))
  808.         {
  809.             // Load by primary key.
  810.             $keys array($this->_tbl_key => $keys);
  811.         }
  812.  
  813.         if ($reset)
  814.         {
  815.             $this->reset();
  816.         }
  817.  
  818.         // Initialise the query.
  819.         $query $this->_db->getQuery(true);
  820.         $query->select($this->_tbl . '.*');
  821.         $query->from($this->_tbl);
  822.  
  823.         // Joined fields are ok, since I initialized them in the constructor
  824.         $fields $this->getKnownFields();
  825.  
  826.         foreach ($keys as $field => $value)
  827.         {
  828.             // Check that $field is in the table.
  829.  
  830.             if (!in_array($field$fields))
  831.             {
  832.                 throw new UnexpectedValueException(sprintf('Missing field in table %s : %s.'$this->_tbl$field));
  833.             }
  834.  
  835.             // Add the search tuple to the query.
  836.             $query->where($this->_db->qn($this->_tbl . '.' $field' = ' $this->_db->q($value));
  837.         }
  838.  
  839.         // Do I have any joined table?
  840.         $j_query $this->getQueryJoin();
  841.  
  842.         if ($j_query)
  843.         {
  844.             if ($j_query->select && $j_query->select->getElements())
  845.             {
  846.                 //$query->select($this->normalizeSelectFields($j_query->select->getElements(), true));
  847.                 $query->select($j_query->select->getElements());
  848.             }
  849.  
  850.             if ($j_query->join)
  851.             {
  852.                 foreach ($j_query->join as $join)
  853.                 {
  854.                     $t = (string) $join;
  855.  
  856.                     // Joomla doesn't provide any access to the "name" variable, so I have to work with strings...
  857.                     if (stripos($t'inner'!== false)
  858.                     {
  859.                         $query->innerJoin($join->getElements());
  860.                     }
  861.                     elseif (stripos($t'left'!== false)
  862.                     {
  863.                         $query->leftJoin($join->getElements());
  864.                     }
  865.                     elseif (stripos($t'right'!== false)
  866.                     {
  867.                         $query->rightJoin($join->getElements());
  868.                     }
  869.                     elseif (stripos($t'outer'!== false)
  870.                     {
  871.                         $query->outerJoin($join->getElements());
  872.                     }
  873.                 }
  874.             }
  875.         }
  876.  
  877.         $this->_db->setQuery($query);
  878.  
  879.         $row $this->_db->loadAssoc();
  880.  
  881.         // Check that we have a result.
  882.         if (empty($row))
  883.         {
  884.             $result true;
  885.  
  886.             return $this->onAfterLoad($result);
  887.         }
  888.  
  889.         // Bind the object with the row and return.
  890.         $result $this->bind($row);
  891.  
  892.         $this->onAfterLoad($result);
  893.  
  894.         return $result;
  895.     }
  896.  
  897.     /**
  898.      * Based on fields properties (nullable column), checks if the field is required or not
  899.      *
  900.      * @return boolean 
  901.      */
  902.     public function check()
  903.     {
  904.         if (!$this->_autoChecks)
  905.         {
  906.             return true;
  907.         }
  908.  
  909.         $fields $this->getTableFields();
  910.  
  911.         // No fields? Why in the hell am I here?
  912.         if(!$fields)
  913.         {
  914.             return false;
  915.         }
  916.  
  917.         $result       true;
  918.         $known        $this->getKnownFields();
  919.         $skipFields[$this->_tbl_key;
  920.  
  921.         if(in_array($this->getColumnAlias('created_on')$known))   $skipFields[$this->getColumnAlias('created_on');
  922.         if(in_array($this->getColumnAlias('created_by')$known))   $skipFields[$this->getColumnAlias('created_by');
  923.         if(in_array($this->getColumnAlias('modified_on')$known))  $skipFields[$this->getColumnAlias('modified_on');
  924.         if(in_array($this->getColumnAlias('modified_by')$known))  $skipFields[$this->getColumnAlias('modified_by');
  925.         if(in_array($this->getColumnAlias('locked_by')$known))    $skipFields[$this->getColumnAlias('locked_by');
  926.         if(in_array($this->getColumnAlias('locked_on')$known))    $skipFields[$this->getColumnAlias('locked_on');
  927.  
  928.         // Let's merge it with custom skips
  929.         $skipFields array_merge($skipFields$this->_skipChecks);
  930.  
  931.         foreach ($fields as $field)
  932.         {
  933.             $fieldName $field->Field;
  934.  
  935.             if (empty($fieldName))
  936.             {
  937.                 $fieldName $field->column_name;
  938.             }
  939.  
  940.             // Field is not nullable but it's null, set error
  941.  
  942.             if ($field->Null == 'NO' && $this->$fieldName == '' && !in_array($fieldName$skipFields))
  943.             {
  944.                 $text str_replace('#__''COM_'$this->getTableName()) '_ERR_' $fieldName;
  945.                 $this->setError(JText::_(strtoupper($text)));
  946.                 $result false;
  947.             }
  948.         }
  949.  
  950.         return $result;
  951.     }
  952.  
  953.     /**
  954.      * Method to reset class properties to the defaults set in the class
  955.      * definition. It will ignore the primary key as well as any private class
  956.      * properties.
  957.      *
  958.      * @return void 
  959.      */
  960.     public function reset()
  961.     {
  962.         if (!$this->onBeforeReset())
  963.         {
  964.             return false;
  965.         }
  966.  
  967.         // Get the default values for the class from the table.
  968.         $fields   $this->getTableFields();
  969.         $j_fields $this->getQueryJoinFields();
  970.  
  971.         if ($j_fields)
  972.         {
  973.             $fields array_merge($fields$j_fields);
  974.         }
  975.  
  976.         foreach ($fields as $k => $v)
  977.         {
  978.             // If the property is not the primary key or private, reset it.
  979.  
  980.             if ($k != $this->_tbl_key && (strpos($k'_'!== 0))
  981.             {
  982.                 $this->$k $v->Default;
  983.             }
  984.         }
  985.  
  986.         if (!$this->onAfterReset())
  987.         {
  988.             return false;
  989.         }
  990.     }
  991.  
  992.     /**
  993.      * Generic check for whether dependancies exist for this object in the db schema
  994.      *
  995.      * @param   integer  $oid    The primary key of the record to delete
  996.      * @param   array    $joins  Any joins to foreign table, used to determine if dependent records exist
  997.      *
  998.      * @return  boolean  True if the record can be deleted
  999.      */
  1000.     public function canDelete($oid null$joins null)
  1001.     {
  1002.         $k $this->_tbl_key;
  1003.  
  1004.         if ($oid)
  1005.         {
  1006.             $this->$k intval($oid);
  1007.         }
  1008.  
  1009.         if (is_array($joins))
  1010.         {
  1011.             $db      $this->_db;
  1012.             $query   $db->getQuery(true)
  1013.                 ->select($db->qn('master''.' $db->qn($k))
  1014.                 ->from($db->qn($this->_tbl' AS ' $db->qn('master'));
  1015.             $tableNo 0;
  1016.  
  1017.             foreach ($joins as $table)
  1018.             {
  1019.                 $tableNo++;
  1020.                 $query->select(
  1021.                     array(
  1022.                         'COUNT(DISTINCT ' $db->qn('t' $tableNo'.' $db->qn($table['idfield']') AS ' $db->qn($table['idalias'])
  1023.                     )
  1024.                 );
  1025.                 $query->join('LEFT'$db->qn($table['name'].
  1026.                     ' AS ' $db->qn('t' $tableNo.
  1027.                     ' ON ' $db->qn('t' $tableNo'.' $db->qn($table['joinfield'].
  1028.                     ' = ' $db->qn('master''.' $db->qn($k)
  1029.                 );
  1030.             }
  1031.  
  1032.             $query->where($db->qn('master''.' $db->qn($k' = ' $db->q($this->$k));
  1033.             $query->group($db->qn('master''.' $db->qn($k));
  1034.             $this->_db->setQuery((string) $query);
  1035.  
  1036.             if (FOFPlatform::getInstance()->checkVersion(JVERSION'3.0''ge'))
  1037.             {
  1038.                 try
  1039.                 {
  1040.                     $obj $this->_db->loadObject();
  1041.                 }
  1042.                 catch (JDatabaseException $e)
  1043.                 {
  1044.                     $this->setError($e->getMessage());
  1045.                 }
  1046.             }
  1047.             else
  1048.             {
  1049.                 if (!$obj $this->_db->loadObject())
  1050.                 {
  1051.                     $this->setError($this->_db->getErrorMsg());
  1052.  
  1053.                     return false;
  1054.                 }
  1055.             }
  1056.  
  1057.             $msg array();
  1058.             $i   0;
  1059.  
  1060.             foreach ($joins as $table)
  1061.             {
  1062.                 $k $table['idalias'];
  1063.  
  1064.                 if ($obj->$k 0)
  1065.                 {
  1066.                     $msg[JText::_($table['label']);
  1067.                 }
  1068.  
  1069.                 $i++;
  1070.             }
  1071.  
  1072.             if (count($msg))
  1073.             {
  1074.                 $option  $this->input->getCmd('option''com_foobar');
  1075.                 $comName str_replace('com_'''$option);
  1076.                 $tview   str_replace('#__' $comName '_'''$this->_tbl);
  1077.                 $prefix  $option '_' $tview '_NODELETE_';
  1078.  
  1079.                 foreach ($msg as $key)
  1080.                 {
  1081.                     $this->setError(JText::_($prefix $key));
  1082.                 }
  1083.  
  1084.                 return false;
  1085.             }
  1086.             else
  1087.             {
  1088.                 return true;
  1089.             }
  1090.         }
  1091.  
  1092.         return true;
  1093.     }
  1094.  
  1095.     /**
  1096.      * Method to bind an associative array or object to the FOFTable instance.This
  1097.      * method only binds properties that are publicly accessible and optionally
  1098.      * takes an array of properties to ignore when binding.
  1099.      *
  1100.      * @param   mixed  $src     An associative array or object to bind to the FOFTable instance.
  1101.      * @param   mixed  $ignore  An optional array or space separated list of properties to ignore while binding.
  1102.      *
  1103.      * @return  boolean  True on success.
  1104.      *
  1105.      * @throws  InvalidArgumentException
  1106.      */
  1107.     public function bind($src$ignore array())
  1108.     {
  1109.         if (!$this->onBeforeBind($src))
  1110.         {
  1111.             return false;
  1112.         }
  1113.  
  1114.         // If the source value is not an array or object return false.
  1115.         if (!is_object($src&& !is_array($src))
  1116.         {
  1117.             throw new InvalidArgumentException(sprintf('%s::bind(*%s*)'get_class($this)gettype($src)));
  1118.         }
  1119.  
  1120.         // If the source value is an object, get its accessible properties.
  1121.         if (is_object($src))
  1122.         {
  1123.             $src get_object_vars($src);
  1124.         }
  1125.  
  1126.         // If the ignore value is a string, explode it over spaces.
  1127.         if (!is_array($ignore))
  1128.         {
  1129.             $ignore explode(' '$ignore);
  1130.         }
  1131.  
  1132.         // Bind the source value, excluding the ignored fields.
  1133.         foreach ($this->getKnownFields(as $k)
  1134.         {
  1135.             // Only process fields not in the ignore array.
  1136.             if (!in_array($k$ignore))
  1137.             {
  1138.                 if (isset($src[$k]))
  1139.                 {
  1140.                     $this->$k $src[$k];
  1141.                 }
  1142.             }
  1143.         }
  1144.  
  1145.         $result $this->onAfterBind($src);
  1146.  
  1147.         return $result;
  1148.     }
  1149.  
  1150.     /**
  1151.      * Method to store a row in the database from the FOFTable instance properties.
  1152.      * If a primary key value is set the row with that primary key value will be
  1153.      * updated with the instance property values.  If no primary key value is set
  1154.      * a new row will be inserted into the database with the properties from the
  1155.      * FOFTable instance.
  1156.      *
  1157.      * @param   boolean  $updateNulls  True to update fields even if they are null.
  1158.      *
  1159.      * @return  boolean  True on success.
  1160.      */
  1161.     public function store($updateNulls false)
  1162.     {
  1163.         if (!$this->onBeforeStore($updateNulls))
  1164.         {
  1165.             return false;
  1166.         }
  1167.  
  1168.         $k $this->_tbl_key;
  1169.  
  1170.         if ($this->$k == 0)
  1171.         {
  1172.             $this->$k null;
  1173.         }
  1174.  
  1175.         // Create the object used for inserting/udpating data to the database
  1176.         $fields     $this->getTableFields();
  1177.         $properties $this->getKnownFields();
  1178.         $keys       array();
  1179.  
  1180.         foreach ($properties as $property)
  1181.         {
  1182.             // 'input' property is a reserved name
  1183.  
  1184.             if (isset($fields[$property]))
  1185.             {
  1186.                 $keys[$property;
  1187.             }
  1188.         }
  1189.  
  1190.         $updateObject array();
  1191.         foreach ($keys as $key)
  1192.         {
  1193.             $updateObject[$key$this->$key;
  1194.         }
  1195.         $updateObject = (object)$updateObject;
  1196.  
  1197.         // If a primary key exists update the object, otherwise insert it.
  1198.         if ($this->$k)
  1199.         {
  1200.             $result $this->_db->updateObject($this->_tbl$updateObject$this->_tbl_key$updateNulls);
  1201.         }
  1202.         else
  1203.         {
  1204.             $result $this->_db->insertObject($this->_tbl$updateObject$this->_tbl_key);
  1205.         }
  1206.  
  1207.         if ($result !== true)
  1208.         {
  1209.             $this->setError($this->_db->getErrorMsg());
  1210.             return false;
  1211.         }
  1212.  
  1213.         $this->bind($updateObject);
  1214.  
  1215.         if ($this->_locked)
  1216.         {
  1217.             $this->_unlock();
  1218.         }
  1219.  
  1220.         $result $this->onAfterStore();
  1221.  
  1222.         return $result;
  1223.     }
  1224.  
  1225.     /**
  1226.      * Method to move a row in the ordering sequence of a group of rows defined by an SQL WHERE clause.
  1227.      * Negative numbers move the row up in the sequence and positive numbers move it down.
  1228.      *
  1229.      * @param   integer  $delta  The direction and magnitude to move the row in the ordering sequence.
  1230.      * @param   string   $where  WHERE clause to use for limiting the selection of rows to compact the
  1231.      *                            ordering values.
  1232.      *
  1233.      * @return  mixed    Boolean  True on success.
  1234.      *
  1235.      * @throws  UnexpectedValueException
  1236.      */
  1237.     public function move($delta$where '')
  1238.     {
  1239.         if (!$this->onBeforeMove($delta$where))
  1240.         {
  1241.             return false;
  1242.         }
  1243.  
  1244.         // If there is no ordering field set an error and return false.
  1245.         $ordering_field $this->getColumnAlias('ordering');
  1246.  
  1247.         if (!in_array($ordering_field$this->getKnownFields()))
  1248.         {
  1249.             throw new UnexpectedValueException(sprintf('%s does not support ordering.'$this->_tbl));
  1250.         }
  1251.  
  1252.         // If the change is none, do nothing.
  1253.         if (empty($delta))
  1254.         {
  1255.             $result $this->onAfterMove();
  1256.  
  1257.             return $result;
  1258.         }
  1259.  
  1260.         $k     $this->_tbl_key;
  1261.         $row   null;
  1262.         $query $this->_db->getQuery(true);
  1263.  
  1264.         // If the table is not loaded, return false
  1265.         if (empty($this->$k))
  1266.         {
  1267.             return false;
  1268.         }
  1269.  
  1270.         // Select the primary key and ordering values from the table.
  1271.         $query->select(array($this->_db->qn($this->_tbl_key)$this->_db->qn($ordering_field)));
  1272.         $query->from($this->_tbl);
  1273.  
  1274.         // If the movement delta is negative move the row up.
  1275.  
  1276.         if ($delta 0)
  1277.         {
  1278.             $query->where($this->_db->qn($ordering_field' < ' $this->_db->q((int) $this->$ordering_field));
  1279.             $query->order($this->_db->qn($ordering_field' DESC');
  1280.         }
  1281.  
  1282.         // If the movement delta is positive move the row down.
  1283.  
  1284.         elseif ($delta 0)
  1285.         {
  1286.             $query->where($this->_db->qn($ordering_field' > ' $this->_db->q((int) $this->$ordering_field));
  1287.             $query->order($this->_db->qn($ordering_field' ASC');
  1288.         }
  1289.  
  1290.         // Add the custom WHERE clause if set.
  1291.  
  1292.         if ($where)
  1293.         {
  1294.             $query->where($where);
  1295.         }
  1296.  
  1297.         // Select the first row with the criteria.
  1298.         $this->_db->setQuery($query01);
  1299.         $row $this->_db->loadObject();
  1300.  
  1301.         // If a row is found, move the item.
  1302.  
  1303.         if (!empty($row))
  1304.         {
  1305.             // Update the ordering field for this instance to the row's ordering value.
  1306.             $query $this->_db->getQuery(true);
  1307.             $query->update($this->_tbl);
  1308.             $query->set($this->_db->qn($ordering_field' = ' $this->_db->q((int) $row->$ordering_field));
  1309.             $query->where($this->_tbl_key . ' = ' $this->_db->q($this->$k));
  1310.             $this->_db->setQuery($query);
  1311.             $this->_db->execute();
  1312.  
  1313.             // Update the ordering field for the row to this instance's ordering value.
  1314.             $query $this->_db->getQuery(true);
  1315.             $query->update($this->_tbl);
  1316.             $query->set($this->_db->qn($ordering_field' = ' $this->_db->q((int) $this->$ordering_field));
  1317.             $query->where($this->_tbl_key . ' = ' $this->_db->q($row->$k));
  1318.             $this->_db->setQuery($query);
  1319.             $this->_db->execute();
  1320.  
  1321.             // Update the instance value.
  1322.             $this->$ordering_field $row->$ordering_field;
  1323.         }
  1324.         else
  1325.         {
  1326.             // Update the ordering field for this instance.
  1327.             $query $this->_db->getQuery(true);
  1328.             $query->update($this->_tbl);
  1329.             $query->set($this->_db->qn($ordering_field' = ' $this->_db->q((int) $this->$ordering_field));
  1330.             $query->where($this->_tbl_key . ' = ' $this->_db->q($this->$k));
  1331.             $this->_db->setQuery($query);
  1332.             $this->_db->execute();
  1333.         }
  1334.  
  1335.         $result $this->onAfterMove();
  1336.  
  1337.         return $result;
  1338.     }
  1339.  
  1340.     /**
  1341.      * Change the ordering of the records of the table
  1342.      *
  1343.      * @param   string   $where  The WHERE clause of the SQL used to fetch the order
  1344.      *
  1345.      * @return  boolean  True is successful
  1346.      *
  1347.      * @throws  UnexpectedValueException
  1348.      */
  1349.     public function reorder($where '')
  1350.     {
  1351.         if (!$this->onBeforeReorder($where))
  1352.         {
  1353.             return false;
  1354.         }
  1355.  
  1356.         // If there is no ordering field set an error and return false.
  1357.  
  1358.         $order_field $this->getColumnAlias('ordering');
  1359.  
  1360.         if (!in_array($order_field$this->getKnownFields()))
  1361.         {
  1362.             throw new UnexpectedValueException(sprintf('%s does not support ordering.'$this->_tbl_key));
  1363.         }
  1364.  
  1365.         $k $this->_tbl_key;
  1366.  
  1367.         // Get the primary keys and ordering values for the selection.
  1368.         $query $this->_db->getQuery(true);
  1369.         $query->select($this->_tbl_key . ', ' $this->_db->qn($order_field));
  1370.         $query->from($this->_tbl);
  1371.         $query->where($this->_db->qn($order_field' >= ' $this->_db->q(0));
  1372.         $query->order($this->_db->qn($order_field));
  1373.  
  1374.         // Setup the extra where and ordering clause data.
  1375.  
  1376.         if ($where)
  1377.         {
  1378.             $query->where($where);
  1379.         }
  1380.  
  1381.         $this->_db->setQuery($query);
  1382.         $rows $this->_db->loadObjectList();
  1383.  
  1384.         // Compact the ordering values.
  1385.  
  1386.         foreach ($rows as $i => $row)
  1387.         {
  1388.             // Make sure the ordering is a positive integer.
  1389.  
  1390.             if ($row->$order_field >= 0)
  1391.             {
  1392.                 // Only update rows that are necessary.
  1393.  
  1394.                 if ($row->$order_field != $i 1)
  1395.                 {
  1396.                     // Update the row ordering field.
  1397.                     $query $this->_db->getQuery(true);
  1398.                     $query->update($this->_tbl);
  1399.                     $query->set($this->_db->qn($order_field' = ' $this->_db->q($i 1));
  1400.                     $query->where($this->_tbl_key . ' = ' $this->_db->q($row->$k));
  1401.                     $this->_db->setQuery($query);
  1402.                     $this->_db->execute();
  1403.                 }
  1404.             }
  1405.         }
  1406.  
  1407.         $result $this->onAfterReorder();
  1408.  
  1409.         return $result;
  1410.     }
  1411.  
  1412.     /**
  1413.      * Check out (lock) a record
  1414.      *
  1415.      * @param   integer  $userId  The locking user's ID
  1416.      * @param   integer  $oid     The primary key value of the record to lock
  1417.      *
  1418.      * @return  boolean  True on success
  1419.      */
  1420.     public function checkout($userId$oid null)
  1421.     {
  1422.         $fldLockedBy $this->getColumnAlias('locked_by');
  1423.         $fldLockedOn $this->getColumnAlias('locked_on');
  1424.  
  1425.         if (!(in_array($fldLockedBy$this->getKnownFields())
  1426.             || in_array($fldLockedOn$this->getKnownFields())))
  1427.         {
  1428.             return true;
  1429.         }
  1430.  
  1431.         $k $this->_tbl_key;
  1432.  
  1433.         if ($oid !== null)
  1434.         {
  1435.             $this->$k $oid;
  1436.         }
  1437.  
  1438.         // No primary key defined, stop here
  1439.         if (!$this->$k)
  1440.         {
  1441.             return false;
  1442.         }
  1443.  
  1444.         $date JFactory::getDate();
  1445.         $time $date->toSql();
  1446.  
  1447.         $query $this->_db->getQuery(true)
  1448.             ->update($this->_db->qn($this->_tbl))
  1449.             ->set(
  1450.                 array(
  1451.                     $this->_db->qn($fldLockedBy' = ' $this->_db->q((int) $userId),
  1452.                     $this->_db->qn($fldLockedOn' = ' $this->_db->q($time)
  1453.                 )
  1454.             )
  1455.             ->where($this->_db->qn($this->_tbl_key' = ' $this->_db->q($this->$k));
  1456.         $this->_db->setQuery((string) $query);
  1457.  
  1458.         $this->$fldLockedBy $userId;
  1459.         $this->$fldLockedOn $time;
  1460.  
  1461.         return $this->_db->execute();
  1462.     }
  1463.  
  1464.     /**
  1465.      * Check in (unlock) a record
  1466.      *
  1467.      * @param   integer  $oid  The primary key value of the record to unlock
  1468.      *
  1469.      * @return  boolean  True on success
  1470.      */
  1471.     public function checkin($oid null)
  1472.     {
  1473.         $fldLockedBy $this->getColumnAlias('locked_by');
  1474.         $fldLockedOn $this->getColumnAlias('locked_on');
  1475.  
  1476.         if (!(in_array($fldLockedBy$this->getKnownFields())
  1477.             || in_array($fldLockedOn$this->getKnownFields())))
  1478.         {
  1479.             return true;
  1480.         }
  1481.  
  1482.         $k $this->_tbl_key;
  1483.  
  1484.         if ($oid !== null)
  1485.         {
  1486.             $this->$k $oid;
  1487.         }
  1488.  
  1489.         if ($this->$k == null)
  1490.         {
  1491.             return false;
  1492.         }
  1493.  
  1494.         $query $this->_db->getQuery(true)
  1495.             ->update($this->_db->qn($this->_tbl))
  1496.             ->set(
  1497.                 array(
  1498.                     $this->_db->qn($fldLockedBy' = 0',
  1499.                     $this->_db->qn($fldLockedOn' = ' $this->_db->q($this->_db->getNullDate())
  1500.                 )
  1501.             )
  1502.             ->where($this->_db->qn($this->_tbl_key' = ' $this->_db->q($this->$k));
  1503.         $this->_db->setQuery((string) $query);
  1504.  
  1505.         $this->$fldLockedBy 0;
  1506.         $this->$fldLockedOn '';
  1507.  
  1508.         return $this->_db->execute();
  1509.     }
  1510.  
  1511.     /**
  1512.      * Is a record locked?
  1513.      *
  1514.      * @param   integer $with            The userid to preform the match with. If an item is checked
  1515.      *                                    out by this user the function will return false.
  1516.      * @param   integer $unused_against  Junk inherited from JTable; ignore
  1517.      *
  1518.      * @throws  UnexpectedValueException
  1519.      *
  1520.      * @return  boolean  True if the record is locked by another user
  1521.      */
  1522.     public function isCheckedOut($with 0$unused_against null)
  1523.     {
  1524.         $against     null;
  1525.         $fldLockedBy $this->getColumnAlias('locked_by');
  1526.  
  1527.         $k  $this->_tbl_key;
  1528.  
  1529.         // If no primary key is given, return false.
  1530.  
  1531.         if ($this->$k === null)
  1532.         {
  1533.             throw new UnexpectedValueException('Null primary key not allowed.');
  1534.         }
  1535.  
  1536.         if (isset($this&& is_a($this'FOFTable'&& !$against)
  1537.         {
  1538.             $against $this->get($fldLockedBy);
  1539.         }
  1540.  
  1541.         // Item is not checked out, or being checked out by the same user
  1542.  
  1543.         if (!$against || $against == $with)
  1544.         {
  1545.             return false;
  1546.         }
  1547.  
  1548.         $session JTable::getInstance('session');
  1549.  
  1550.         return $session->exists($against);
  1551.     }
  1552.  
  1553.     /**
  1554.      * Copy (duplicate) one or more records
  1555.      *
  1556.      * @param   integer|array $cid  The primary key value (or values) or the record(s) to copy
  1557.      *
  1558.      * @return  boolean  True on success
  1559.      */
  1560.     public function copy($cid null)
  1561.     {
  1562.         //We have to cast the id as array, or the helper function will return an empty set
  1563.         if($cid)
  1564.         {
  1565.             $cid = (array) $cid;
  1566.         }
  1567.  
  1568.         JArrayHelper::toInteger($cid);
  1569.         $k $this->_tbl_key;
  1570.  
  1571.         if (count($cid1)
  1572.         {
  1573.             if ($this->$k)
  1574.             {
  1575.                 $cid array($this->$k);
  1576.             }
  1577.             else
  1578.             {
  1579.                 $this->setError("No items selected.");
  1580.  
  1581.                 return false;
  1582.             }
  1583.         }
  1584.  
  1585.         $created_by  $this->getColumnAlias('created_by');
  1586.         $created_on  $this->getColumnAlias('created_on');
  1587.         $modified_by $this->getColumnAlias('modified_by');
  1588.         $modified_on $this->getColumnAlias('modified_on');
  1589.  
  1590.         $locked_byName $this->getColumnAlias('locked_by');
  1591.         $checkin       in_array($locked_byName$this->getKnownFields());
  1592.  
  1593.         foreach ($cid as $item)
  1594.         {
  1595.             // Prevent load with id = 0
  1596.  
  1597.             if (!$item)
  1598.             {
  1599.                 continue;
  1600.             }
  1601.  
  1602.             $this->load($item);
  1603.  
  1604.             if ($checkin)
  1605.             {
  1606.                 // We're using the checkin and the record is used by someone else
  1607.  
  1608.                 if ($this->isCheckedOut($item))
  1609.                 {
  1610.                     continue;
  1611.                 }
  1612.             }
  1613.  
  1614.             // TODO Should we notify the user that we had a problem with this record?
  1615.             if (!$this->onBeforeCopy($item))
  1616.             {
  1617.                 continue;
  1618.             }
  1619.  
  1620.             $this->$k           null;
  1621.             $this->$created_by  null;
  1622.             $this->$created_on  null;
  1623.             $this->$modified_on null;
  1624.             $this->$modified_by null;
  1625.  
  1626.             // Let's fire the event only if everything is ok
  1627.             // TODO Should we notify the user that we had a problem with this record?
  1628.             if ($this->store())
  1629.             {
  1630.                 // TODO Should we notify the user that we had a problem with this record?
  1631.                 $this->onAfterCopy($item);
  1632.             }
  1633.  
  1634.             $this->reset();
  1635.         }
  1636.  
  1637.         return true;
  1638.     }
  1639.  
  1640.     /**
  1641.      * Publish or unpublish records
  1642.      *
  1643.      * @param   integer|array $cid      The primary key value(s) of the item(s) to publish/unpublish
  1644.      * @param   integer        $publish  1 to publish an item, 0 to unpublish
  1645.      * @param   integer        $user_id  The user ID of the user (un)publishing the item.
  1646.      *
  1647.      * @return  boolean  True on success, false on failure (e.g. record is locked)
  1648.      */
  1649.     public function publish($cid null$publish 1$user_id 0)
  1650.     {
  1651.         $enabledName   $this->getColumnAlias('enabled');
  1652.         $locked_byName $this->getColumnAlias('locked_by');
  1653.  
  1654.         // Mhm... you called the publish method on a table without publish support...
  1655.         if(!in_array($enabledName$this->getKnownFields()))
  1656.         {
  1657.             return false;
  1658.         }
  1659.  
  1660.         //We have to cast the id as array, or the helper function will return an empty set
  1661.         if($cid)
  1662.         {
  1663.             $cid = (array) $cid;
  1664.         }
  1665.  
  1666.         JArrayHelper::toInteger($cid);
  1667.         $user_id = (int) $user_id;
  1668.         $publish = (int) $publish;
  1669.         $k       $this->_tbl_key;
  1670.  
  1671.         if (count($cid1)
  1672.         {
  1673.             if ($this->$k)
  1674.             {
  1675.                 $cid array($this->$k);
  1676.             }
  1677.             else
  1678.             {
  1679.                 $this->setError("No items selected.");
  1680.  
  1681.                 return false;
  1682.             }
  1683.         }
  1684.  
  1685.         if (!$this->onBeforePublish($cid$publish))
  1686.         {
  1687.             return false;
  1688.         }
  1689.  
  1690.         $query $this->_db->getQuery(true)
  1691.             ->update($this->_db->qn($this->_tbl))
  1692.             ->set($this->_db->qn($enabledName' = ' . (int) $publish);
  1693.  
  1694.         $checkin in_array($locked_byName$this->getKnownFields());
  1695.  
  1696.         if ($checkin)
  1697.         {
  1698.             $query->where(
  1699.                 ' (' $this->_db->qn($locked_byName.
  1700.                     ' = 0 OR ' $this->_db->qn($locked_byName' = ' . (int) $user_id ')''AND'
  1701.             );
  1702.         }
  1703.  
  1704.         //Why this crazy statement?
  1705.         // TODO Rewrite this statment using IN. Check if it work in SQLServer and PostgreSQL
  1706.         $cids $this->_db->qn($k' = ' implode(' OR ' $this->_db->qn($k' = '$cid);
  1707.  
  1708.         $query->where('(' $cids ')');
  1709.  
  1710.         $this->_db->setQuery((string) $query);
  1711.  
  1712.         if (FOFPlatform::getInstance()->checkVersion(JVERSION'3.0''ge'))
  1713.         {
  1714.             try
  1715.             {
  1716.                 $this->_db->execute();
  1717.             }
  1718.             catch (JDatabaseException $e)
  1719.             {
  1720.                 $this->setError($e->getMessage());
  1721.             }
  1722.         }
  1723.         else
  1724.         {
  1725.             if (!$this->_db->execute())
  1726.             {
  1727.                 $this->setError($this->_db->getErrorMsg());
  1728.  
  1729.                 return false;
  1730.             }
  1731.         }
  1732.  
  1733.         if (count($cid== && $checkin)
  1734.         {
  1735.             if ($this->_db->getAffectedRows(== 1)
  1736.             {
  1737.                 // TODO should we check for its return value?
  1738.                 $this->checkin($cid[0]);
  1739.  
  1740.                 if ($this->$k == $cid[0])
  1741.                 {
  1742.                     $this->$enabledName $publish;
  1743.                 }
  1744.             }
  1745.         }
  1746.  
  1747.         $this->setError('');
  1748.  
  1749.         return true;
  1750.     }
  1751.  
  1752.     /**
  1753.      * Delete a record
  1754.      *
  1755.      * @param   integer $oid  The primary key value of the item to delete
  1756.      *
  1757.      * @throws  UnexpectedValueException
  1758.      *
  1759.      * @return  boolean  True on success
  1760.      */
  1761.     public function delete($oid null)
  1762.     {
  1763.         if ($oid)
  1764.         {
  1765.             $this->load($oid);
  1766.         }
  1767.  
  1768.         $k  $this->_tbl_key;
  1769.         $pk (!$oid$this->$k $oid;
  1770.  
  1771.         // If no primary key is given, return false.
  1772.         if (!$pk)
  1773.         {
  1774.             throw new UnexpectedValueException('Null primary key not allowed.');
  1775.         }
  1776.  
  1777.         // Execute the logic only if I have a primary key, otherwise I could have weird results
  1778.         if (!$this->onBeforeDelete($oid))
  1779.         {
  1780.             return false;
  1781.         }
  1782.  
  1783.         // Delete the row by primary key.
  1784.         $query $this->_db->getQuery(true);
  1785.         $query->delete();
  1786.         $query->from($this->_tbl);
  1787.         $query->where($this->_tbl_key . ' = ' $this->_db->q($pk));
  1788.         $this->_db->setQuery($query);
  1789.  
  1790.         // @TODO Check for a database error.
  1791.         $this->_db->execute();
  1792.  
  1793.         $result $this->onAfterDelete($oid);
  1794.  
  1795.         return $result;
  1796.     }
  1797.  
  1798.     /**
  1799.      * Register a hit on a record
  1800.      *
  1801.      * @param   integer  $oid  The primary key value of the record
  1802.      * @param   boolean  $log  Should I log the hit?
  1803.      *
  1804.      * @return  boolean  True on success
  1805.      */
  1806.     public function hit($oid null$log false)
  1807.     {
  1808.         if (!$this->onBeforeHit($oid$log))
  1809.         {
  1810.             return false;
  1811.         }
  1812.  
  1813.         // If there is no hits field, just return true.
  1814.         $hits_field $this->getColumnAlias('hits');
  1815.  
  1816.         if (!in_array($hits_field$this->getKnownFields()))
  1817.         {
  1818.             return true;
  1819.         }
  1820.  
  1821.         $k  $this->_tbl_key;
  1822.         $pk (is_null($oid)) $this->$k $oid;
  1823.  
  1824.         // If no primary key is given, return false.
  1825.  
  1826.         if ($pk === null)
  1827.         {
  1828.             $result false;
  1829.         }
  1830.         else
  1831.         {
  1832.             // Check the row in by primary key.
  1833.             $query $this->_db->getQuery(true);
  1834.             $query->update($this->_tbl);
  1835.             $query->set($this->_db->qn($hits_field' = (' $this->_db->qn($hits_field' + 1)');
  1836.             $query->where($this->_tbl_key . ' = ' $this->_db->q($pk));
  1837.             $this->_db->setQuery($query);
  1838.             $this->_db->execute();
  1839.  
  1840.             // Set table values in the object.
  1841.             $this->hits++;
  1842.  
  1843.             $result true;
  1844.         }
  1845.  
  1846.         if ($result)
  1847.         {
  1848.             $result $this->onAfterHit($oid);
  1849.         }
  1850.  
  1851.         return $result;
  1852.     }
  1853.  
  1854.     /**
  1855.      * Export the item as a CSV line
  1856.      *
  1857.      * @param   string  $separator  CSV separator. Tip: use "\t" to get a TSV file instead.
  1858.      *
  1859.      * @return  string  The CSV line
  1860.      */
  1861.     public function toCSV($separator ',')
  1862.     {
  1863.         $csv array();
  1864.  
  1865.         foreach (get_object_vars($thisas $k => $v)
  1866.         {
  1867.             if (is_array($vor is_object($vor $v === null)
  1868.             {
  1869.                 continue;
  1870.             }
  1871.  
  1872.             if ($k[0== '_')
  1873.             {
  1874.                 // Internal field
  1875.                 continue;
  1876.             }
  1877.  
  1878.             $csv['"' str_replace('"''""'$v'"';
  1879.         }
  1880.  
  1881.         $csv implode($separator$csv);
  1882.  
  1883.         return $csv;
  1884.     }
  1885.  
  1886.     /**
  1887.      * Exports the table in array format
  1888.      *
  1889.      * @return  array 
  1890.      */
  1891.     public function getData()
  1892.     {
  1893.         $ret array();
  1894.  
  1895.         foreach (get_object_vars($thisas $k => $v)
  1896.         {
  1897.             // Special internal fields
  1898.             if (in_array($karray('config''input''knownFields')))
  1899.             {
  1900.                 continue;
  1901.             }
  1902.  
  1903.             if (($k[0== '_'|| ($k[0== '*'))
  1904.             {
  1905.                 // Internal field
  1906.                 continue;
  1907.             }
  1908.  
  1909.             $ret[$k$v;
  1910.         }
  1911.  
  1912.         return $ret;
  1913.     }
  1914.  
  1915.     /**
  1916.      * Get the header for exporting item list to CSV
  1917.      *
  1918.      * @param   string  $separator  CSV separator. Tip: use "\t" to get a TSV file instead.
  1919.      *
  1920.      * @return  string  The CSV file's header
  1921.      */
  1922.     public function getCSVHeader($separator ',')
  1923.     {
  1924.         $csv array();
  1925.  
  1926.         foreach (get_object_vars($thisas $k => $v)
  1927.         {
  1928.             if (is_array($vor is_object($vor $v === null)
  1929.             {
  1930.                 continue;
  1931.             }
  1932.  
  1933.             if ($k[0== '_')
  1934.             {
  1935.                 // Internal field
  1936.                 continue;
  1937.             }
  1938.  
  1939.             $csv['"' str_replace('"''\"'$k'"';
  1940.         }
  1941.  
  1942.         $csv implode($separator$csv);
  1943.  
  1944.         return $csv;
  1945.     }
  1946.  
  1947.     /**
  1948.      * Get the columns from a database table.
  1949.      *
  1950.      * @param   string  $tableName  Table name. If null current table is used
  1951.      *
  1952.      * @return  mixed  An array of the field names, or false if an error occurs.
  1953.      */
  1954.     public function getTableFields($tableName null)
  1955.     {
  1956.         // Should I load the cached data?
  1957.         $useCache array_key_exists('use_table_cache'$this->config$this->config['use_table_cache'false;
  1958.  
  1959.         // Make sure we have a list of tables in this db
  1960.  
  1961.         if (empty(self::$tableCache))
  1962.         {
  1963.             if ($useCache)
  1964.             {
  1965.                 // Try to load table cache from a cache file
  1966.                 $cacheData FOFPlatform::getInstance()->getCache('tables'null);
  1967.  
  1968.                 // Unserialise the cached data, or set the table cache to empty
  1969.                 // if the cache data wasn't loaded.
  1970.                 if (!is_null($cacheData))
  1971.                 {
  1972.                     self::$tableCache json_decode($cacheDatatrue);
  1973.                 }
  1974.                 else
  1975.                 {
  1976.                     self::$tableCache array();
  1977.                 }
  1978.             }
  1979.  
  1980.             // This check is true if the cache data doesn't exist / is not loaded
  1981.             if (empty(self::$tableCache))
  1982.             {
  1983.                 self::$tableCache $this->_db->getTableList();
  1984.  
  1985.                 if ($useCache)
  1986.                 {
  1987.                     FOFPlatform::getInstance()->setCache('tables'json_encode(self::$tableCache));
  1988.                 }
  1989.             }
  1990.         }
  1991.  
  1992.         // Make sure the cached table fields cache is loaded
  1993.         if (empty(self::$tableFieldCache))
  1994.         {
  1995.             if ($useCache)
  1996.             {
  1997.                 // Try to load table cache from a cache file
  1998.                 $cacheData FOFPlatform::getInstance()->getCache('tablefields'null);
  1999.  
  2000.                 // Unserialise the cached data, or set to empty if the cache
  2001.                 // data wasn't loaded.
  2002.                 if (!is_null($cacheData))
  2003.                 {
  2004.                     $decoded json_decode($cacheDatatrue);
  2005.                     $tableCache array();
  2006.  
  2007.                     if (count($decoded))
  2008.                     {
  2009.                         foreach ($decoded as $myTableName => $tableFields)
  2010.                         {
  2011.                             $temp array();
  2012.  
  2013.                             if (is_array($tableFields))
  2014.                             {
  2015.                                 foreach($tableFields as $field => $def)
  2016.                                 {
  2017.                                     $temp[$field= (object)$def;
  2018.                                 }
  2019.                                 $tableCache[$myTableName$temp;
  2020.                             }
  2021.                             elseif (is_object($tableFields|| is_bool($tableFields))
  2022.                             {
  2023.                                 $tableCache[$myTableName$tableFields;
  2024.                             }
  2025.                         }
  2026.                     }
  2027.  
  2028.                     self::$tableFieldCache $tableCache;
  2029.                 }
  2030.                 else
  2031.                 {
  2032.                     self::$tableFieldCache array();
  2033.                 }
  2034.             }
  2035.         }
  2036.  
  2037.         if (!$tableName)
  2038.         {
  2039.             $tableName $this->_tbl;
  2040.         }
  2041.  
  2042.         // Try to load again column specifications if the table is not loaded OR if it's loaded and
  2043.         // the previous call returned an error
  2044.         if (!array_key_exists($tableNameself::$tableFieldCache||
  2045.             (isset(self::$tableFieldCache[$tableName]&& !self::$tableFieldCache[$tableName]))
  2046.         {
  2047.             // Lookup the fields for this table only once.
  2048.             $name $tableName;
  2049.  
  2050.             $prefix $this->_db->getPrefix();
  2051.  
  2052.             if (substr($name03== '#__')
  2053.             {
  2054.                 $checkName $prefix substr($name3);
  2055.             }
  2056.             else
  2057.             {
  2058.                 $checkName $name;
  2059.             }
  2060.  
  2061.             if (!in_array($checkNameself::$tableCache))
  2062.             {
  2063.                 // The table doesn't exist. Return false.
  2064.                 self::$tableFieldCache[$tableNamefalse;
  2065.             }
  2066.             elseif (FOFPlatform::getInstance()->checkVersion(JVERSION'3.0''ge'))
  2067.             {
  2068.                 $fields $this->_db->getTableColumns($namefalse);
  2069.  
  2070.                 if (empty($fields))
  2071.                 {
  2072.                     $fields false;
  2073.                 }
  2074.  
  2075.                 self::$tableFieldCache[$tableName$fields;
  2076.             }
  2077.             else
  2078.             {
  2079.                 $fields $this->_db->getTableFields($namefalse);
  2080.  
  2081.                 if (!isset($fields[$name]))
  2082.                 {
  2083.                     $fields false;
  2084.                 }
  2085.  
  2086.                 self::$tableFieldCache[$tableName$fields[$name];
  2087.             }
  2088.  
  2089.             // PostgreSQL date type compatibility
  2090.             if (($this->_db->name == 'postgresql'&& (self::$tableFieldCache[$tableName!= false))
  2091.             {
  2092.                 foreach (self::$tableFieldCache[$tableNameas $field)
  2093.                 {
  2094.                     if (strtolower($field->type== 'timestamp without time zone')
  2095.                     {
  2096.                         if (stristr($field->Default'\'::timestamp without time zone'))
  2097.                         {
  2098.                             list ($date$junkexplode('::'$field->Default2);
  2099.                             $field->Default trim($date"'");
  2100.                         }
  2101.                     }
  2102.                 }
  2103.             }
  2104.  
  2105.             // Save the data for this table into the cache
  2106.             if ($useCache)
  2107.             {
  2108.                 $cacheData FOFPlatform::getInstance()->setCache('tablefields'json_encode(self::$tableFieldCache));
  2109.             }
  2110.         }
  2111.  
  2112.         return self::$tableFieldCache[$tableName];
  2113.     }
  2114.  
  2115.     public function getTableAlias()
  2116.     {
  2117.         return $this->_tableAlias;
  2118.     }
  2119.  
  2120.     public function setTableAlias($string)
  2121.     {
  2122.         $string preg_replace('#[^A-Z0-9_]#i'''$string);
  2123.         $this->_tableAlias = $string;
  2124.     }
  2125.  
  2126.     /**
  2127.      * Method to return the real name of a "special" column such as ordering, hits, published
  2128.      * etc etc. In this way you are free to follow your db naming convention and use the
  2129.      * built in Joomla functions.
  2130.      *
  2131.      * @param   string  $column  Name of the "special" column (ie ordering, hits etc etc)
  2132.      *
  2133.      * @return  string  The string that identify the special
  2134.      */
  2135.     public function getColumnAlias($column)
  2136.     {
  2137.         if (isset($this->_columnAlias[$column]))
  2138.         {
  2139.             $return $this->_columnAlias[$column];
  2140.         }
  2141.         else
  2142.         {
  2143.             $return $column;
  2144.         }
  2145.  
  2146.         $return preg_replace('#[^A-Z0-9_]#i'''$return);
  2147.  
  2148.         return $return;
  2149.     }
  2150.  
  2151.     /**
  2152.      * Method to register a column alias for a "special" column.
  2153.      *
  2154.      * @param   string  $column       The "special" column (ie ordering)
  2155.      * @param   string  $columnAlias  The real column name (ie foo_ordering)
  2156.      *
  2157.      * @return  void 
  2158.      */
  2159.     public function setColumnAlias($column$columnAlias)
  2160.     {
  2161.         $column strtolower($column);
  2162.  
  2163.         $column                      preg_replace('#[^A-Z0-9_]#i'''$column);
  2164.         $this->_columnAlias[$column$columnAlias;
  2165.     }
  2166.  
  2167.     /**
  2168.      * Get a JOIN query, used to join other tables
  2169.      *
  2170.      * @param   boolean  $asReference  Return an object reference instead of a copy
  2171.      *
  2172.      * @return  JDatabaseQuery  Query used to join other tables
  2173.      */
  2174.     public function getQueryJoin($asReference false)
  2175.     {
  2176.         if ($asReference)
  2177.         {
  2178.             return $this->_queryJoin;
  2179.         }
  2180.         else
  2181.         {
  2182.             if ($this->_queryJoin)
  2183.             {
  2184.                 return clone $this->_queryJoin;
  2185.             }
  2186.             else
  2187.             {
  2188.                 return null;
  2189.             }
  2190.         }
  2191.     }
  2192.  
  2193.     /**
  2194.      * Sets the query with joins to other tables
  2195.      *
  2196.      * @param   JDatabaseQuery  $query  The JOIN query to use
  2197.      *
  2198.      * @return  void 
  2199.      */
  2200.     public function setQueryJoin(JDatabaseQuery $query)
  2201.     {
  2202.         $this->_queryJoin = $query;
  2203.     }
  2204.  
  2205.     /**
  2206.      * Extracts the fields from the join query
  2207.      *
  2208.      * @return   array    Fields contained in the join query
  2209.      */
  2210.     protected function getQueryJoinFields()
  2211.     {
  2212.         $query $this->getQueryJoin();
  2213.  
  2214.         if (!$query)
  2215.         {
  2216.             return array();
  2217.         }
  2218.  
  2219.         $tables   array();
  2220.         $j_tables array();
  2221.         $j_fields array();
  2222.  
  2223.         // Get joined tables. Ignore FROM clause, since it should not be used (the starting point is the table "table")
  2224.         $joins    $query->join;
  2225.  
  2226.         foreach ($joins as $join)
  2227.         {
  2228.             $tables array_merge($tables$join->getElements());
  2229.         }
  2230.  
  2231.         // Clean up table names
  2232.         foreach($tables as $table)
  2233.         {
  2234.             preg_match('#(.*)((\w)*(on|using))(.*)#i'$table$matches);
  2235.  
  2236.             if($matches && isset($matches[1]))
  2237.             {
  2238.                 // I always want the first part, no matter what
  2239.                 $parts explode(' '$matches[1]);
  2240.                 $t_table $parts[0];
  2241.  
  2242.                 if($this->isQuoted($t_table))
  2243.                 {
  2244.                     $t_table substr($t_table1strlen($t_table2);
  2245.                 }
  2246.  
  2247.                 if(!in_array($t_table$j_tables))
  2248.                 {
  2249.                     $j_tables[=  $t_table;
  2250.                 }
  2251.             }
  2252.         }
  2253.  
  2254.         // Do I have the current table inside the query join? Remove it (its fields are already ok)
  2255.         $find array_search($this->getTableName()$j_tables);
  2256.         if($find !== false)
  2257.         {
  2258.             unset($j_tables[$find]);
  2259.         }
  2260.  
  2261.         // Get table fields
  2262.         $fields array();
  2263.  
  2264.         foreach ($j_tables as $table)
  2265.         {
  2266.             $t_fields $this->getTableFields($table);
  2267.  
  2268.             if ($t_fields)
  2269.             {
  2270.                 $fields array_merge($fields$t_fields);
  2271.             }
  2272.         }
  2273.  
  2274.         // Remove any fields that aren't in the joined select
  2275.         $j_select $query->select;
  2276.  
  2277.         if ($j_select && $j_select->getElements())
  2278.         {
  2279.             $j_fields $this->normalizeSelectFields($j_select->getElements());
  2280.         }
  2281.  
  2282.         // I can intesect the keys
  2283.         $fields   array_intersect_key($fields$j_fields);
  2284.  
  2285.         // Now I walk again the array to change the key of columns that have an alias
  2286.         foreach ($j_fields as $column => $alias)
  2287.         {
  2288.             if ($column != $alias)
  2289.             {
  2290.                 $fields[$alias$fields[$column];
  2291.                 unset($fields[$column]);
  2292.             }
  2293.         }
  2294.  
  2295.         return $fields;
  2296.     }
  2297.  
  2298.     /**
  2299.      * Normalizes the fields, returning an associative array with all the fields.
  2300.      * Ie array('foobar as foo, bar') becomes array('foobar' => 'foo', 'bar' => 'bar')
  2301.      *
  2302.      * @param   array $fields    Array with column fields
  2303.      *
  2304.      * @return  array  Normalized array
  2305.      */
  2306.     protected function normalizeSelectFields($fields)
  2307.     {
  2308.         $db     JFactory::getDbo();
  2309.         $return array();
  2310.  
  2311.         foreach ($fields as $field)
  2312.         {
  2313.             $t_fields explode(','$field);
  2314.  
  2315.             foreach ($t_fields as $t_field)
  2316.             {
  2317.                 // Is there any alias?
  2318.                 $parts  preg_split('#\sas\s#i'$t_field);
  2319.  
  2320.                 // Do I have a table.column situation? Let's get the field name
  2321.                 $tableField  explode('.'$parts[0]);
  2322.  
  2323.                 if(isset($tableField[1]))
  2324.                 {
  2325.                     $column trim($tableField[1]);
  2326.                 }
  2327.                 else
  2328.                 {
  2329.                     $column trim($tableField[0]);
  2330.                 }
  2331.  
  2332.                 // Is this field quoted? If so, remove the quotes
  2333.                 if($this->isQuoted($column))
  2334.                 {
  2335.                     $column substr($column1strlen($column2);
  2336.                 }
  2337.  
  2338.                 if(isset($parts[1]))
  2339.                 {
  2340.                     $alias trim($parts[1]);
  2341.  
  2342.                     // Is this field quoted? If so, remove the quotes
  2343.                     if($this->isQuoted($alias))
  2344.                     {
  2345.                         $alias substr($alias1strlen($alias2);
  2346.                     }
  2347.                 }
  2348.                 else
  2349.                 {
  2350.                     $alias $column;
  2351.                 }
  2352.  
  2353.                 $return[$column$alias;
  2354.             }
  2355.         }
  2356.  
  2357.         return $return;
  2358.     }
  2359.  
  2360.     /**
  2361.      * Is the field quoted?
  2362.      *
  2363.      * @param   string  $column     Column, passed by reference, so in later version of Joomla
  2364.      *                               I can always quote them
  2365.      *
  2366.      * @return  bool    Is the field quoted?
  2367.      */
  2368.     protected function isQuoted(&$column)
  2369.     {
  2370.         // Under Joomla 3.2 I can safely quote the column again, then return true
  2371.         if(FOFPlatform::getInstance()->checkVersion(JVERSION'3.2''ge'))
  2372.         {
  2373.             $column JFactory::getDbo()->qn($column);
  2374.             return true;
  2375.         }
  2376.  
  2377.         // On previous version I need some "magic". If the first char is not a letter, a number
  2378.         // an underscore or # (needed for table), then most likely the field is quoted
  2379.         preg_match_all('/^[a-z0-9_#]/i'$column$matches);
  2380.  
  2381.         if(!$matches[0])
  2382.         {
  2383.             return true;
  2384.         }
  2385.  
  2386.         return false;
  2387.     }
  2388.  
  2389.     /**
  2390.      * The event which runs before binding data to the table
  2391.      *
  2392.      * NOTE TO 3RD PARTY DEVELOPERS:
  2393.      *
  2394.      * When you override the following methods in your child classes,
  2395.      * be sure to call parent::method *AFTER* your code, otherwise the
  2396.      * plugin events do NOT get triggered
  2397.      *
  2398.      * Example:
  2399.      * protected function onAfterStore(){
  2400.      *       // Your code here
  2401.      *     return parent::onAfterStore() && $your_result;
  2402.      * }
  2403.      *
  2404.      * Do not do it the other way around, e.g. return $your_result && parent::onAfterStore()
  2405.      * Due to  PHP short-circuit boolean evaluation the parent::onAfterStore()
  2406.      * will not be called if $your_result is false.
  2407.      *
  2408.      * @param   object|array &$from  The data to bind
  2409.      *
  2410.      * @return  boolean  True on success
  2411.      */
  2412.     protected function onBeforeBind(&$from)
  2413.     {
  2414.         // Call the behaviors
  2415.         $result $this->tableDispatcher->trigger('onBeforeBind'array(&$this&$from));
  2416.  
  2417.         if (in_array(false$resulttrue))
  2418.         {
  2419.             // Behavior failed, return false
  2420.             return false;
  2421.         }
  2422.  
  2423.         if ($this->_trigger_events)
  2424.         {
  2425.             $name FOFInflector::pluralize($this->getKeyName());
  2426.  
  2427.             $result     FOFPlatform::getInstance()->runPlugins('onBeforeBind' ucfirst($name)array(&$this&$from));
  2428.  
  2429.             if (in_array(false$resulttrue))
  2430.             {
  2431.                 return false;
  2432.             }
  2433.             else
  2434.             {
  2435.                 return true;
  2436.             }
  2437.         }
  2438.  
  2439.         return true;
  2440.     }
  2441.  
  2442.     /**
  2443.      * The event which runs after loading a record from the database
  2444.      *
  2445.      * @param   boolean  &$result  Did the load succeeded?
  2446.      *
  2447.      * @return  void 
  2448.      */
  2449.     protected function onAfterLoad(&$result)
  2450.     {
  2451.         // Call the behaviors
  2452.         $eventRistult $this->tableDispatcher->trigger('onAfterLoad'array(&$this&$result));
  2453.  
  2454.         if (in_array(false$eventRistulttrue))
  2455.         {
  2456.             // Behavior failed, return false
  2457.             $result false;
  2458.             return false;
  2459.         }
  2460.  
  2461.         if ($this->_trigger_events)
  2462.         {
  2463.             $name FOFInflector::pluralize($this->getKeyName());
  2464.  
  2465.             FOFPlatform::getInstance()->runPlugins('onAfterLoad' ucfirst($name)array(&$this&$result));
  2466.         }
  2467.     }
  2468.  
  2469.     /**
  2470.      * The event which runs before storing (saving) data to the database
  2471.      *
  2472.      * @param   boolean  $updateNulls  Should nulls be saved as nulls (true) or just skipped over (false)?
  2473.      *
  2474.      * @return  boolean  True to allow saving
  2475.      */
  2476.     protected function onBeforeStore($updateNulls)
  2477.     {
  2478.         // Do we have a "Created" set of fields?
  2479.         $created_on  $this->getColumnAlias('created_on');
  2480.         $created_by  $this->getColumnAlias('created_by');
  2481.         $modified_on $this->getColumnAlias('modified_on');
  2482.         $modified_by $this->getColumnAlias('modified_by');
  2483.         $locked_on   $this->getColumnAlias('locked_on');
  2484.         $locked_by   $this->getColumnAlias('locked_by');
  2485.         $title       $this->getColumnAlias('title');
  2486.         $slug        $this->getColumnAlias('slug');
  2487.  
  2488.         $hasCreatedOn in_array($created_on$this->getKnownFields());
  2489.         $hasCreatedBy in_array($created_by$this->getKnownFields());
  2490.  
  2491.         if ($hasCreatedOn && $hasCreatedBy)
  2492.         {
  2493.             $hasModifiedOn in_array($modified_on$this->getKnownFields());
  2494.             $hasModifiedBy in_array($modified_by$this->getKnownFields());
  2495.  
  2496.             $nullDate $this->_db->getNullDate();
  2497.  
  2498.             if (empty($this->$created_by|| ($this->$created_on == $nullDate|| empty($this->$created_on))
  2499.             {
  2500.                 $uid FOFPlatform::getInstance()->getUser()->id;
  2501.  
  2502.                 if ($uid)
  2503.                 {
  2504.                     $this->$created_by FOFPlatform::getInstance()->getUser()->id;
  2505.                 }
  2506.                 JLoader::import('joomla.utilities.date');
  2507.                 $date new JDate();
  2508.  
  2509.                 $this->$created_on $date->toSql();
  2510.             }
  2511.             elseif ($hasModifiedOn && $hasModifiedBy)
  2512.             {
  2513.                 $uid FOFPlatform::getInstance()->getUser()->id;
  2514.  
  2515.                 if ($uid)
  2516.                 {
  2517.                     $this->$modified_by FOFPlatform::getInstance()->getUser()->id;
  2518.                 }
  2519.                 JLoader::import('joomla.utilities.date');
  2520.                 $date new JDate();
  2521.  
  2522.                 $this->$modified_on $date->toSql();
  2523.             }
  2524.         }
  2525.  
  2526.         // Do we have a set of title and slug fields?
  2527.         $hasTitle in_array($title$this->getKnownFields());
  2528.         $hasSlug  in_array($slug$this->getKnownFields());
  2529.  
  2530.         if ($hasTitle && $hasSlug)
  2531.         {
  2532.             if (empty($this->$slug))
  2533.             {
  2534.                 // Create a slug from the title
  2535.                 $this->$slug FOFStringUtils::toSlug($this->$title);
  2536.             }
  2537.             else
  2538.             {
  2539.                 // Filter the slug for invalid characters
  2540.                 $this->$slug FOFStringUtils::toSlug($this->$slug);
  2541.             }
  2542.  
  2543.             // Make sure we don't have a duplicate slug on this table
  2544.             $db    $this->getDbo();
  2545.             $query $db->getQuery(true)
  2546.                 ->select($db->qn($slug))
  2547.                 ->from($this->_tbl)
  2548.                 ->where($db->qn($slug' = ' $db->q($this->$slug))
  2549.                 ->where('NOT ' $db->qn($this->_tbl_key' = ' $db->q($this->{$this->_tbl_key}));
  2550.             $db->setQuery($query);
  2551.             $existingItems $db->loadAssocList();
  2552.  
  2553.             $count   0;
  2554.             $newSlug $this->$slug;
  2555.  
  2556.             while (!empty($existingItems))
  2557.             {
  2558.                 $count++;
  2559.                 $newSlug $this->$slug '-' $count;
  2560.                 $query   $db->getQuery(true)
  2561.                     ->select($db->qn($slug))
  2562.                     ->from($this->_tbl)
  2563.                     ->where($db->qn($slug' = ' $db->q($newSlug))
  2564.                     ->where($db->qn($this->_tbl_key' = ' $db->q($this->{$this->_tbl_key})'AND NOT');
  2565.                 $db->setQuery($query);
  2566.                 $existingItems $db->loadAssocList();
  2567.             }
  2568.  
  2569.             $this->$slug $newSlug;
  2570.         }
  2571.  
  2572.         // Call the behaviors
  2573.         $result $this->tableDispatcher->trigger('onBeforeStore'array(&$this$updateNulls));
  2574.  
  2575.         if (in_array(false$resulttrue))
  2576.         {
  2577.             // Behavior failed, return false
  2578.             return false;
  2579.         }
  2580.  
  2581.         // Execute onBeforeStore<tablename> events in loaded plugins
  2582.         if ($this->_trigger_events)
  2583.         {
  2584.             $name       FOFInflector::pluralize($this->getKeyName());
  2585.             $result     FOFPlatform::getInstance()->runPlugins('onBeforeStore' ucfirst($name)array(&$this$updateNulls));
  2586.  
  2587.             if (in_array(false$resulttrue))
  2588.             {
  2589.                 return false;
  2590.             }
  2591.             else
  2592.             {
  2593.                 return true;
  2594.             }
  2595.         }
  2596.  
  2597.         return true;
  2598.     }
  2599.  
  2600.     /**
  2601.      * The event which runs after binding data to the class
  2602.      *
  2603.      * @param   object|array &$src  The data to bind
  2604.      *
  2605.      * @return  boolean  True to allow binding without an error
  2606.      */
  2607.     protected function onAfterBind(&$src)
  2608.     {
  2609.         // Call the behaviors
  2610.         $options array(
  2611.             'component'     => $this->input->get('option'),
  2612.             'view'            => $this->input->get('view'),
  2613.             'table_prefix'    => $this->_tablePrefix
  2614.         );
  2615.  
  2616.         $result $this->tableDispatcher->trigger('onAfterBind'array(&$this&$src$options));
  2617.  
  2618.         if (in_array(false$resulttrue))
  2619.         {
  2620.             // Behavior failed, return false
  2621.             return false;
  2622.         }
  2623.  
  2624.         if ($this->_trigger_events)
  2625.         {
  2626.             $name FOFInflector::pluralize($this->getKeyName());
  2627.  
  2628.             $result     FOFPlatform::getInstance()->runPlugins('onAfterBind' ucfirst($name)array(&$this&$src));
  2629.  
  2630.             if (in_array(false$resulttrue))
  2631.             {
  2632.                 return false;
  2633.             }
  2634.             else
  2635.             {
  2636.                 return true;
  2637.             }
  2638.         }
  2639.  
  2640.         return true;
  2641.     }
  2642.  
  2643.     /**
  2644.      * The event which runs after storing (saving) data to the database
  2645.      *
  2646.      * @return  boolean  True to allow saving without an error
  2647.      */
  2648.     protected function onAfterStore()
  2649.     {
  2650.         // Call the behaviors
  2651.         $result $this->tableDispatcher->trigger('onAfterStore'array(&$this));
  2652.  
  2653.         if (in_array(false$resulttrue))
  2654.         {
  2655.             // Behavior failed, return false
  2656.             return false;
  2657.         }
  2658.  
  2659.         if ($this->_trigger_events)
  2660.         {
  2661.             $name FOFInflector::pluralize($this->getKeyName());
  2662.  
  2663.             $result     FOFPlatform::getInstance()->runPlugins('onAfterStore' ucfirst($name)array(&$this));
  2664.  
  2665.             if (in_array(false$resulttrue))
  2666.             {
  2667.                 return false;
  2668.             }
  2669.             else
  2670.             {
  2671.                 return true;
  2672.             }
  2673.         }
  2674.  
  2675.         return true;
  2676.     }
  2677.  
  2678.     /**
  2679.      * The event which runs before moving a record
  2680.      *
  2681.      * @param   boolean  $updateNulls  Should nulls be saved as nulls (true) or just skipped over (false)?
  2682.      *
  2683.      * @return  boolean  True to allow moving
  2684.      */
  2685.     protected function onBeforeMove($updateNulls)
  2686.     {
  2687.         // Call the behaviors
  2688.         $result $this->tableDispatcher->trigger('onBeforeMove'array(&$this$updateNulls));
  2689.  
  2690.         if (in_array(false$resulttrue))
  2691.         {
  2692.             // Behavior failed, return false
  2693.             return false;
  2694.         }
  2695.  
  2696.         if ($this->_trigger_events)
  2697.         {
  2698.             $name FOFInflector::pluralize($this->getKeyName());
  2699.  
  2700.             $result     FOFPlatform::getInstance()->runPlugins('onBeforeMove' ucfirst($name)array(&$this$updateNulls));
  2701.  
  2702.             if (in_array(false$resulttrue))
  2703.             {
  2704.                 return false;
  2705.             }
  2706.             else
  2707.             {
  2708.                 return true;
  2709.             }
  2710.         }
  2711.  
  2712.         return true;
  2713.     }
  2714.  
  2715.     /**
  2716.      * The event which runs after moving a record
  2717.      *
  2718.      * @return  boolean  True to allow moving without an error
  2719.      */
  2720.     protected function onAfterMove()
  2721.     {
  2722.         // Call the behaviors
  2723.         $result $this->tableDispatcher->trigger('onAfterMove'array(&$this));
  2724.  
  2725.         if (in_array(false$resulttrue))
  2726.         {
  2727.             // Behavior failed, return false
  2728.             return false;
  2729.         }
  2730.  
  2731.         if ($this->_trigger_events)
  2732.         {
  2733.             $name FOFInflector::pluralize($this->getKeyName());
  2734.  
  2735.             $result     FOFPlatform::getInstance()->runPlugins('onAfterMove' ucfirst($name)array(&$this));
  2736.  
  2737.             if (in_array(false$resulttrue))
  2738.             {
  2739.                 return false;
  2740.             }
  2741.             else
  2742.             {
  2743.                 return true;
  2744.             }
  2745.         }
  2746.  
  2747.         return true;
  2748.     }
  2749.  
  2750.     /**
  2751.      * The event which runs before reordering a table
  2752.      *
  2753.      * @param   string  $where  The WHERE clause of the SQL query to run on reordering (record filter)
  2754.      *
  2755.      * @return  boolean  True to allow reordering
  2756.      */
  2757.     protected function onBeforeReorder($where '')
  2758.     {
  2759.         // Call the behaviors
  2760.         $result $this->tableDispatcher->trigger('onBeforeReorder'array(&$this$where));
  2761.  
  2762.         if (in_array(false$resulttrue))
  2763.         {
  2764.             // Behavior failed, return false
  2765.             return false;
  2766.         }
  2767.  
  2768.         if ($this->_trigger_events)
  2769.         {
  2770.             $name FOFInflector::pluralize($this->getKeyName());
  2771.  
  2772.             $result     FOFPlatform::getInstance()->runPlugins('onBeforeReorder' ucfirst($name)array(&$this$where));
  2773.  
  2774.             if (in_array(false$resulttrue))
  2775.             {
  2776.                 return false;
  2777.             }
  2778.             else
  2779.             {
  2780.                 return true;
  2781.             }
  2782.         }
  2783.  
  2784.         return true;
  2785.     }
  2786.  
  2787.     /**
  2788.      * The event which runs after reordering a table
  2789.      *
  2790.      * @return  boolean  True to allow the reordering to complete without an error
  2791.      */
  2792.     protected function onAfterReorder()
  2793.     {
  2794.         // Call the behaviors
  2795.         $result $this->tableDispatcher->trigger('onAfterReorder'array(&$this));
  2796.  
  2797.         if (in_array(false$resulttrue))
  2798.         {
  2799.             // Behavior failed, return false
  2800.             return false;
  2801.         }
  2802.  
  2803.         if ($this->_trigger_events)
  2804.         {
  2805.             $name FOFInflector::pluralize($this->getKeyName());
  2806.  
  2807.             $result     FOFPlatform::getInstance()->runPlugins('onAfterReorder' ucfirst($name)array(&$this));
  2808.  
  2809.             if (in_array(false$resulttrue))
  2810.             {
  2811.                 return false;
  2812.             }
  2813.             else
  2814.             {
  2815.                 return true;
  2816.             }
  2817.         }
  2818.  
  2819.         return true;
  2820.     }
  2821.  
  2822.     /**
  2823.      * The event which runs before deleting a record
  2824.      *
  2825.      * @param   integer  $oid  The PK value of the record to delete
  2826.      *
  2827.      * @return  boolean  True to allow the deletion
  2828.      */
  2829.     protected function onBeforeDelete($oid)
  2830.     {
  2831.         // Call the behaviors
  2832.         $result $this->tableDispatcher->trigger('onBeforeDelete'array(&$this$oid));
  2833.  
  2834.         if (in_array(false$resulttrue))
  2835.         {
  2836.             // Behavior failed, return false
  2837.             return false;
  2838.         }
  2839.  
  2840.         if ($this->_trigger_events)
  2841.         {
  2842.             $name FOFInflector::pluralize($this->getKeyName());
  2843.  
  2844.             $result     FOFPlatform::getInstance()->runPlugins('onBeforeDelete' ucfirst($name)array(&$this$oid));
  2845.  
  2846.             if (in_array(false$resulttrue))
  2847.             {
  2848.                 return false;
  2849.             }
  2850.             else
  2851.             {
  2852.                 return true;
  2853.             }
  2854.         }
  2855.  
  2856.         return true;
  2857.     }
  2858.  
  2859.     /**
  2860.      * The event which runs after deleting a record
  2861.      *
  2862.      * @param   integer  $oid  The PK value of the record which was deleted
  2863.      *
  2864.      * @return  boolean  True to allow the deletion without errors
  2865.      */
  2866.     protected function onAfterDelete($oid)
  2867.     {
  2868.         // Call the behaviors
  2869.         $result $this->tableDispatcher->trigger('onAfterDelete'array(&$this$oid));
  2870.  
  2871.         if (in_array(false$resulttrue))
  2872.         {
  2873.             // Behavior failed, return false
  2874.             return false;
  2875.         }
  2876.  
  2877.         if ($this->_trigger_events)
  2878.         {
  2879.             $name FOFInflector::pluralize($this->getKeyName());
  2880.  
  2881.             $result     FOFPlatform::getInstance()->runPlugins('onAfterDelete' ucfirst($name)array(&$this$oid));
  2882.  
  2883.             if (in_array(false$resulttrue))
  2884.             {
  2885.                 return false;
  2886.             }
  2887.             else
  2888.             {
  2889.                 return true;
  2890.             }
  2891.         }
  2892.  
  2893.         return true;
  2894.     }
  2895.  
  2896.     /**
  2897.      * The event which runs before hitting a record
  2898.      *
  2899.      * @param   integer  $oid  The PK value of the record to hit
  2900.      * @param   boolean  $log  Should we log the hit?
  2901.      *
  2902.      * @return  boolean  True to allow the hit
  2903.      */
  2904.     protected function onBeforeHit($oid$log)
  2905.     {
  2906.         // Call the behaviors
  2907.         $result $this->tableDispatcher->trigger('onBeforeHit'array(&$this$oid$log));
  2908.  
  2909.         if (in_array(false$resulttrue))
  2910.         {
  2911.             // Behavior failed, return false
  2912.             return false;
  2913.         }
  2914.  
  2915.         if ($this->_trigger_events)
  2916.         {
  2917.             $name FOFInflector::pluralize($this->getKeyName());
  2918.  
  2919.             $result     FOFPlatform::getInstance()->runPlugins('onBeforeHit' ucfirst($name)array(&$this$oid$log));
  2920.  
  2921.             if (in_array(false$resulttrue))
  2922.             {
  2923.                 return false;
  2924.             }
  2925.             else
  2926.             {
  2927.                 return true;
  2928.             }
  2929.         }
  2930.  
  2931.         return true;
  2932.     }
  2933.  
  2934.     /**
  2935.      * The event which runs after hitting a record
  2936.      *
  2937.      * @param   integer  $oid  The PK value of the record which was hit
  2938.      *
  2939.      * @return  boolean  True to allow the hitting without errors
  2940.      */
  2941.     protected function onAfterHit($oid)
  2942.     {
  2943.         // Call the behaviors
  2944.         $result $this->tableDispatcher->trigger('onAfterHit'array(&$this$oid));
  2945.  
  2946.         if (in_array(false$resulttrue))
  2947.         {
  2948.             // Behavior failed, return false
  2949.             return false;
  2950.         }
  2951.  
  2952.         if ($this->_trigger_events)
  2953.         {
  2954.             $name FOFInflector::pluralize($this->getKeyName());
  2955.  
  2956.             $result     FOFPlatform::getInstance()->runPlugins('onAfterHit' ucfirst($name)array(&$this$oid));
  2957.  
  2958.             if (in_array(false$resulttrue))
  2959.             {
  2960.                 return false;
  2961.             }
  2962.             else
  2963.             {
  2964.                 return true;
  2965.             }
  2966.         }
  2967.  
  2968.         return true;
  2969.     }
  2970.  
  2971.     /**
  2972.      * The even which runs before copying a record
  2973.      *
  2974.      * @param   integer  $oid  The PK value of the record being copied
  2975.      *
  2976.      * @return  boolean  True to allow the copy to take place
  2977.      */
  2978.     protected function onBeforeCopy($oid)
  2979.     {
  2980.         // Call the behaviors
  2981.         $result $this->tableDispatcher->trigger('onBeforeCopy'array(&$this$oid));
  2982.  
  2983.         if (in_array(false$resulttrue))
  2984.         {
  2985.             // Behavior failed, return false
  2986.             return false;
  2987.         }
  2988.  
  2989.         if ($this->_trigger_events)
  2990.         {
  2991.             $name FOFInflector::pluralize($this->getKeyName());
  2992.  
  2993.             $result     FOFPlatform::getInstance()->runPlugins('onBeforeCopy' ucfirst($name)array(&$this$oid));
  2994.  
  2995.             if (in_array(false$resulttrue))
  2996.             {
  2997.                 return false;
  2998.             }
  2999.             else
  3000.             {
  3001.                 return true;
  3002.             }
  3003.         }
  3004.  
  3005.         return true;
  3006.     }
  3007.  
  3008.     /**
  3009.      * The even which runs after copying a record
  3010.      *
  3011.      * @param   integer  $oid  The PK value of the record which was copied (not the new one)
  3012.      *
  3013.      * @return  boolean  True to allow the copy without errors
  3014.      */
  3015.     protected function onAfterCopy($oid)
  3016.     {
  3017.         // Call the behaviors
  3018.         $result $this->tableDispatcher->trigger('onAfterCopy'array(&$this$oid));
  3019.  
  3020.         if (in_array(false$resulttrue))
  3021.         {
  3022.             // Behavior failed, return false
  3023.             return false;
  3024.         }
  3025.  
  3026.         if ($this->_trigger_events)
  3027.         {
  3028.             $name FOFInflector::pluralize($this->getKeyName());
  3029.  
  3030.             $result     FOFPlatform::getInstance()->runPlugins('onAfterCopy' ucfirst($name)array(&$this$oid));
  3031.  
  3032.             if (in_array(false$resulttrue))
  3033.             {
  3034.                 return false;
  3035.             }
  3036.             else
  3037.             {
  3038.                 return true;
  3039.             }
  3040.         }
  3041.  
  3042.         return true;
  3043.     }
  3044.  
  3045.     /**
  3046.      * The event which runs before a record is (un)published
  3047.      *
  3048.      * @param   integer|array &$cid     The PK IDs of the records being (un)published
  3049.      * @param   integer        $publish  1 to publish, 0 to unpublish
  3050.      *
  3051.      * @return  boolean  True to allow the (un)publish to proceed
  3052.      */
  3053.     protected function onBeforePublish(&$cid$publish)
  3054.     {
  3055.         // Call the behaviors
  3056.         $result $this->tableDispatcher->trigger('onBeforePublish'array(&$this&$cid$publish));
  3057.  
  3058.         if (in_array(false$resulttrue))
  3059.         {
  3060.             // Behavior failed, return false
  3061.             return false;
  3062.         }
  3063.  
  3064.         if ($this->_trigger_events)
  3065.         {
  3066.             $name FOFInflector::pluralize($this->getKeyName());
  3067.  
  3068.             $result     FOFPlatform::getInstance()->runPlugins('onBeforePublish' ucfirst($name)array(&$this&$cid$publish));
  3069.  
  3070.             if (in_array(false$resulttrue))
  3071.             {
  3072.                 return false;
  3073.             }
  3074.             else
  3075.             {
  3076.                 return true;
  3077.             }
  3078.         }
  3079.  
  3080.         return true;
  3081.     }
  3082.  
  3083.     /**
  3084.      * The event which runs after the object is reset to its default values.
  3085.      *
  3086.      * @return  boolean  True to allow the reset to complete without errors
  3087.      */
  3088.     protected function onAfterReset()
  3089.     {
  3090.         // Call the behaviors
  3091.         $result $this->tableDispatcher->trigger('onAfterReset'array(&$this));
  3092.  
  3093.         if (in_array(false$resulttrue))
  3094.         {
  3095.             // Behavior failed, return false
  3096.             return false;
  3097.         }
  3098.  
  3099.         if ($this->_trigger_events)
  3100.         {
  3101.             $name FOFInflector::pluralize($this->getKeyName());
  3102.  
  3103.             $result     FOFPlatform::getInstance()->runPlugins('onAfterReset' ucfirst($name)array(&$this));
  3104.  
  3105.             if (in_array(false$resulttrue))
  3106.             {
  3107.                 return false;
  3108.             }
  3109.             else
  3110.             {
  3111.                 return true;
  3112.             }
  3113.         }
  3114.  
  3115.         return true;
  3116.     }
  3117.  
  3118.     /**
  3119.      * The even which runs before the object is reset to its default values.
  3120.      *
  3121.      * @return  boolean  True to allow the reset to complete
  3122.      */
  3123.     protected function onBeforeReset()
  3124.     {
  3125.         // Call the behaviors
  3126.         $result $this->tableDispatcher->trigger('onBeforeReset'array(&$this));
  3127.  
  3128.         if (in_array(false$resulttrue))
  3129.         {
  3130.             // Behavior failed, return false
  3131.             return false;
  3132.         }
  3133.  
  3134.         if ($this->_trigger_events)
  3135.         {
  3136.             $name FOFInflector::pluralize($this->getKeyName());
  3137.  
  3138.             $result     FOFPlatform::getInstance()->runPlugins('onBeforeReset' ucfirst($name)array(&$this));
  3139.  
  3140.             if (in_array(false$resulttrue))
  3141.             {
  3142.                 return false;
  3143.             }
  3144.             else
  3145.             {
  3146.                 return true;
  3147.             }
  3148.         }
  3149.  
  3150.         return true;
  3151.     }
  3152.  
  3153.     /**
  3154.      * Replace the input object of this table with the provided FOFInput object
  3155.      *
  3156.      * @param   FOFInput  $input  The new input object
  3157.      *
  3158.      * @return  void 
  3159.      */
  3160.     public function setInput(FOFInput $input)
  3161.     {
  3162.         $this->input = $input;
  3163.     }
  3164.  
  3165.     /**
  3166.      * Get the columns from database table.
  3167.      *
  3168.      * @return  mixed  An array of the field names, or false if an error occurs.
  3169.      *
  3170.      * @deprecated  2.1
  3171.      */
  3172.     public function getFields()
  3173.     {
  3174.         return $this->getTableFields();
  3175.     }
  3176.  
  3177.     /**
  3178.      * Add a filesystem path where FOFTable should search for table class files.
  3179.      * You may either pass a string or an array of paths.
  3180.      *
  3181.      * @param   mixed  $path  A filesystem path or array of filesystem paths to add.
  3182.      *
  3183.      * @return  array  An array of filesystem paths to find FOFTable classes in.
  3184.      */
  3185.     public static function addIncludePath($path null)
  3186.     {
  3187.         // If the internal paths have not been initialised, do so with the base table path.
  3188.         if (empty(self::$_includePaths))
  3189.         {
  3190.             self::$_includePaths array(__DIR__);
  3191.         }
  3192.  
  3193.         // Convert the passed path(s) to add to an array.
  3194.         settype($path'array');
  3195.  
  3196.         // If we have new paths to add, do so.
  3197.         if (!empty($path&& !in_array($pathself::$_includePaths))
  3198.         {
  3199.             // Check and add each individual new path.
  3200.             foreach ($path as $dir)
  3201.             {
  3202.                 // Sanitize path.
  3203.                 $dir trim($dir);
  3204.  
  3205.                 // Add to the front of the list so that custom paths are searched first.
  3206.                 array_unshift(self::$_includePaths$dir);
  3207.             }
  3208.         }
  3209.  
  3210.         return self::$_includePaths;
  3211.     }
  3212.  
  3213.     /**
  3214.      * Loads the asset table related to this table.
  3215.      * This will help tests, too, since we can mock this function.
  3216.      *
  3217.      * @return bool|JTableAsset    False on failure, otherwise JTableAsset
  3218.      */
  3219.     protected function getAsset()
  3220.     {
  3221.         $name     $this->_getAssetName();
  3222.  
  3223.         // Do NOT touch JTable here -- we are loading the core asset table which is a JTable, not a FOFTable
  3224.         $asset    JTable::getInstance('Asset');
  3225.  
  3226.         if (!$asset->loadByName($name))
  3227.         {
  3228.             return false;
  3229.         }
  3230.  
  3231.         return $asset;
  3232.     }
  3233.  
  3234.     /**
  3235.      * Method to compute the default name of the asset.
  3236.      * The default name is in the form table_name.id
  3237.      * where id is the value of the primary key of the table.
  3238.      *
  3239.      * @throws  UnexpectedValueException
  3240.      *
  3241.      * @return  string 
  3242.      */
  3243.     public function getAssetName()
  3244.     {
  3245.         $k $this->_tbl_key;
  3246.  
  3247.         // If there is no assetKey defined, let's set it to table name
  3248.         if(!$this->_assetKey)
  3249.         {
  3250.             throw new UnexpectedValueException('Table must have an asset key defined in order to track assets');
  3251.         }
  3252.  
  3253.         return $this->_assetKey . '.' . (int) $this->$k;
  3254.     }
  3255.  
  3256.     /**
  3257.      * Method to compute the default name of the asset.
  3258.      * The default name is in the form table_name.id
  3259.      * where id is the value of the primary key of the table.
  3260.      *
  3261.      * @throws  UnexpectedValueException
  3262.      *
  3263.      * @return  string 
  3264.      */
  3265.     public function getAssetKey()
  3266.     {
  3267.         return $this->_assetKey;
  3268.     }
  3269.  
  3270.     /**
  3271.      * Method to return the title to use for the asset table.  In
  3272.      * tracking the assets a title is kept for each asset so that there is some
  3273.      * context available in a unified access manager.  Usually this would just
  3274.      * return $this->title or $this->name or whatever is being used for the
  3275.      * primary name of the row. If this method is not overridden, the asset name is used.
  3276.      *
  3277.      * @return  string  The string to use as the title in the asset table.
  3278.      */
  3279.     public function getAssetTitle()
  3280.     {
  3281.         return $this->getAssetName();
  3282.     }
  3283.  
  3284.     /**
  3285.      * Method to get the parent asset under which to register this one.
  3286.      * By default, all assets are registered to the ROOT node with ID,
  3287.      * which will default to 1 if none exists.
  3288.      * The extended class can define a table and id to lookup.  If the
  3289.      * asset does not exist it will be created.
  3290.      *
  3291.      * @param   FOFTable  $table  A FOFTable object for the asset parent.
  3292.      * @param   integer   $id     Id to look up
  3293.      *
  3294.      * @return  integer 
  3295.      */
  3296.     public function getAssetParentId($table null$id null)
  3297.     {
  3298.         // For simple cases, parent to the asset root.
  3299.         $assets JTable::getInstance('Asset''JTable'array('dbo' => $this->getDbo()));
  3300.         $rootId $assets->getRootId();
  3301.  
  3302.         if (!empty($rootId))
  3303.         {
  3304.             return $rootId;
  3305.         }
  3306.  
  3307.         return 1;
  3308.     }
  3309.  
  3310.     /**
  3311.      * This method sets the asset key for the items of this table. Obviously, it
  3312.      * is only meant to be used when you have a table with an asset field.
  3313.      *
  3314.      * @param   string  $assetKey  The name of the asset key to use
  3315.      *
  3316.      * @return  void 
  3317.      */
  3318.     public function setAssetKey($assetKey)
  3319.     {
  3320.         $this->_assetKey = $assetKey;
  3321.     }
  3322.  
  3323.     /**
  3324.      * Method to get the database table name for the class.
  3325.      *
  3326.      * @return  string  The name of the database table being modeled.
  3327.      */
  3328.     public function getTableName()
  3329.     {
  3330.         return $this->_tbl;
  3331.     }
  3332.  
  3333.     /**
  3334.      * Method to get the primary key field name for the table.
  3335.      *
  3336.      * @return  string  The name of the primary key for the table.
  3337.      */
  3338.     public function getKeyName()
  3339.     {
  3340.         return $this->_tbl_key;
  3341.     }
  3342.  
  3343.     /**
  3344.      * Method to get the JDatabaseDriver object.
  3345.      *
  3346.      * @return  JDatabaseDriver  The internal database driver object.
  3347.      */
  3348.     public function getDbo()
  3349.     {
  3350.         return $this->_db;
  3351.     }
  3352.  
  3353.     /**
  3354.      * Method to set the JDatabaseDriver object.
  3355.      *
  3356.      * @param   JDatabaseDriver  $db  A JDatabaseDriver object to be used by the table object.
  3357.      *
  3358.      * @return  boolean  True on success.
  3359.      */
  3360.     public function setDBO(JDatabaseDriver $db)
  3361.     {
  3362.         $this->_db = $db;
  3363.  
  3364.         return true;
  3365.     }
  3366.  
  3367.     /**
  3368.      * Method to set rules for the record.
  3369.      *
  3370.      * @param   mixed  $input  A JAccessRules object, JSON string, or array.
  3371.      *
  3372.      * @return  void 
  3373.      */
  3374.     public function setRules($input)
  3375.     {
  3376.         if ($input instanceof JAccessRules)
  3377.         {
  3378.             $this->_rules = $input;
  3379.         }
  3380.         else
  3381.         {
  3382.             $this->_rules = new JAccessRules($input);
  3383.         }
  3384.     }
  3385.  
  3386.     /**
  3387.      * Method to get the rules for the record.
  3388.      *
  3389.      * @return  JAccessRules object
  3390.      */
  3391.     public function getRules()
  3392.     {
  3393.         return $this->_rules;
  3394.     }
  3395.  
  3396.     /**
  3397.      * Method to check if the record is treated as an ACL asset
  3398.      *
  3399.      * @return  boolean [description]
  3400.      */
  3401.     public function isAssetsTracked()
  3402.     {
  3403.         return $this->_trackAssets;
  3404.     }
  3405.  
  3406.     /**
  3407.      * Method to provide a shortcut to binding, checking and storing a FOFTable
  3408.      * instance to the database table.  The method will check a row in once the
  3409.      * data has been stored and if an ordering filter is present will attempt to
  3410.      * reorder the table rows based on the filter.  The ordering filter is an instance
  3411.      * property name.  The rows that will be reordered are those whose value matches
  3412.      * the FOFTable instance for the property specified.
  3413.      *
  3414.      * @param   mixed   $src             An associative array or object to bind to the FOFTable instance.
  3415.      * @param   string  $orderingFilter  Filter for the order updating
  3416.      * @param   mixed   $ignore          An optional array or space separated list of properties
  3417.      *                                    to ignore while binding.
  3418.      *
  3419.      * @return  boolean  True on success.
  3420.      */
  3421.     public function save($src$orderingFilter ''$ignore '')
  3422.     {
  3423.         // Attempt to bind the source to the instance.
  3424.         if (!$this->bind($src$ignore))
  3425.         {
  3426.             return false;
  3427.         }
  3428.  
  3429.         // Run any sanity checks on the instance and verify that it is ready for storage.
  3430.         if (!$this->check())
  3431.         {
  3432.             return false;
  3433.         }
  3434.  
  3435.         // Attempt to store the properties to the database table.
  3436.         if (!$this->store())
  3437.         {
  3438.             return false;
  3439.         }
  3440.  
  3441.         // Attempt to check the row in, just in case it was checked out.
  3442.         if (!$this->checkin())
  3443.         {
  3444.             return false;
  3445.         }
  3446.  
  3447.         // If an ordering filter is set, attempt reorder the rows in the table based on the filter and value.
  3448.         if ($orderingFilter)
  3449.         {
  3450.             $filterValue $this->$orderingFilter;
  3451.             $this->reorder($orderingFilter $this->_db->qn($orderingFilter' = ' $this->_db->q($filterValue'');
  3452.         }
  3453.  
  3454.         // Set the error to empty and return true.
  3455.         $this->setError('');
  3456.  
  3457.         return true;
  3458.     }
  3459.  
  3460.     /**
  3461.      * Method to get the next ordering value for a group of rows defined by an SQL WHERE clause.
  3462.      * This is useful for placing a new item last in a group of items in the table.
  3463.      *
  3464.      * @param   string  $where  WHERE clause to use for selecting the MAX(ordering) for the table.
  3465.      *
  3466.      * @return  mixed  Boolean false an failure or the next ordering value as an integer.
  3467.      */
  3468.     public function getNextOrder($where '')
  3469.     {
  3470.         // If there is no ordering field set an error and return false.
  3471.         $ordering $this->getColumnAlias('ordering');
  3472.         if (!in_array($ordering$this->getKnownFields()))
  3473.         {
  3474.             throw new UnexpectedValueException(sprintf('%s does not support ordering.'get_class($this)));
  3475.         }
  3476.  
  3477.         // Get the largest ordering value for a given where clause.
  3478.         $query $this->_db->getQuery(true);
  3479.         $query->select('MAX('.$this->_db->qn($ordering).')');
  3480.         $query->from($this->_tbl);
  3481.  
  3482.         if ($where)
  3483.         {
  3484.             $query->where($where);
  3485.         }
  3486.  
  3487.         $this->_db->setQuery($query);
  3488.         $max = (int) $this->_db->loadResult();
  3489.  
  3490.         // Return the largest ordering value + 1.
  3491.         return ($max 1);
  3492.     }
  3493.  
  3494.     /**
  3495.      * Method to lock the database table for writing.
  3496.      *
  3497.      * @return  boolean  True on success.
  3498.      *
  3499.      * @throws  RuntimeException
  3500.      */
  3501.     protected function _lock()
  3502.     {
  3503.         $this->_db->lockTable($this->_tbl);
  3504.         $this->_locked = true;
  3505.  
  3506.         return true;
  3507.     }
  3508.  
  3509.     /**
  3510.      * Method to unlock the database table for writing.
  3511.      *
  3512.      * @return  boolean  True on success.
  3513.      */
  3514.     protected function _unlock()
  3515.     {
  3516.         $this->_db->unlockTables();
  3517.         $this->_locked = false;
  3518.  
  3519.         return true;
  3520.     }
  3521.  
  3522.     public function setConfig(array $config)
  3523.     {
  3524.         $this->config $config;
  3525.     }
  3526.  
  3527.     /**
  3528. /**
  3529.      * Get the content type for ucm
  3530.      *
  3531.      * @return string The content type alias
  3532.      */
  3533.     public function getContentType()
  3534.     {
  3535.         $component $this->input->get('option');
  3536.  
  3537.         $view FOFInflector::singularize($this->input->get('view'));
  3538.         $alias $component '.' $view;
  3539.  
  3540.         return $alias;
  3541.     }
  3542. }

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