PageRenderTime 44ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/administrator/components/com_joomfish/mldatabase.class.php

https://bitbucket.org/dgough/annamaria-daneswood-25102012
PHP | 541 lines | 336 code | 55 blank | 150 comment | 116 complexity | de300ae39e216cb6c22873a0e75fc6fb MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1
  1. <?php
  2. /**
  3. * Joom!Fish - Multi Lingual extention and translation manager for Joomla!
  4. * Copyright (C) 2003-2007 Think Network GmbH, Munich
  5. *
  6. * All rights reserved. The Joom!Fish project is a set of extentions for
  7. * the content management system Joomla!. It enables Joomla!
  8. * to manage multi lingual sites especially in all dynamic information
  9. * which are stored in the database.
  10. *
  11. * This program is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU General Public License
  13. * as published by the Free Software Foundation; either version 2
  14. * of the License, or (at your option) any later version.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU General Public License
  22. * along with this program; if not, write to the Free Software
  23. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,USA.
  24. *
  25. * The "GNU General Public License" (GPL) is available at
  26. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  27. * -----------------------------------------------------------------------------
  28. * $Id: mldatabase.class.php 781 2007-11-28 09:13:57Z geraint $
  29. *
  30. */
  31. // Don't allow direct linking
  32. defined( '_VALID_MOS' ) or die( 'Direct Access to this location is not allowed.' );
  33. require_once( mosMainFrame::getBasePath() .'/components/com_joomfish/includes/defines.php' );
  34. require_once( JOOMFISH_LIBPATH .DS. 'joomla' .DS. 'language.php' );
  35. require_once( JOOMFISH_LIBPATH .DS. 'joomla' .DS. 'registry.php' );
  36. require_once( mosMainFrame::getBasePath().'/components/com_joomfish/includes/joomfish.class.php' );
  37. require_once( mosMainFrame::getBasePath()."/administrator/components/com_joomfish/joomfish.class.php");
  38. $joomFishManager =& JoomFishManager::getInstance( dirname( __FILE__ ) );
  39. /**
  40. * Multi lingual Database connector class
  41. *
  42. * This extention of the standard database class converts the output of the query automatically
  43. * with the actual selected language in the web site.
  44. *
  45. * @package joomfish
  46. * @subpackage database
  47. * @copyright 2003-2007 Think Network GmbH
  48. * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
  49. * @version 1.0, 2003-10-16 $Revision: 781 $
  50. * @author Alex Kempkens <joomfish@thinknetwork.com>
  51. */
  52. class mlDatabase extends database {
  53. /** @var array list of multi lingual tables */
  54. var $_mlTableList=null;
  55. /** @var Internal variable to hold array of unique tablenames and mapping data*/
  56. var $_refTables=null;
  57. /** Constructor
  58. */
  59. function mlDatabase( $host='localhost', $user, $pass, $db, $table_prefix='jos_', $driver='' ) {
  60. $this->database( $host, $user, $pass, $db, $table_prefix, $driver );
  61. $query = "select distinct reference_table from #__jf_content";
  62. $this->setQuery( $query );
  63. $this->_mlTableList = $this->loadResultArray();
  64. if( !$this->_mlTableList ){
  65. echo $this->getErrorMsg();
  66. }
  67. }
  68. /**
  69. * This global function loads the first row of a query into an object
  70. *
  71. * If an object is passed to this function, the returned row is bound to the existing elements of <var>object</var>.
  72. * If <var>object</var> has a value of null, then all of the returned query fields returned in the object.
  73. * @param string The SQL query
  74. * @param object The address of variable
  75. */
  76. function loadObject( &$object, $translate=true, $language=null ) {
  77. global $mosConfig_lang, $_JOOMFISH_MANAGER;
  78. if( $language===null) $language = $mosConfig_lang;
  79. $result = parent::loadObject( $object );
  80. if( $translate && isset($_JOOMFISH_MANAGER)) {
  81. $doTranslate=false;
  82. $tables =$this->_getRefTables();
  83. foreach ($tables["fieldTablePairs"] as $i=>$table) {
  84. if ($this->translatedContentAvailable($table)) {
  85. $doTranslate=true;
  86. break;
  87. }
  88. }
  89. if ( $doTranslate) {
  90. JoomFish::translate( $object, $this->_getTableName(), $language, $this->_getRefTables());
  91. }
  92. }
  93. return $result;
  94. }
  95. /**
  96. * Public function to test if table has translated content available
  97. *
  98. * @param string $table : tablename to test
  99. * @return boolean true if content is available
  100. */
  101. function translatedContentAvailable($table){
  102. $result = false;
  103. if( $this->_mlTableList != null && is_array($this->_mlTableList) ) {
  104. $result = in_array( $table, $this->_mlTableList);
  105. }
  106. $result = ($table=="content" ? true : $result);
  107. return $result;
  108. }
  109. /**
  110. * Load a list of database objects
  111. * @param string The field name of a primary key
  112. * @param boolean $translate indecates if the query should automatically be translated or not
  113. * @param string $language indecates the language into which the translation should be done
  114. * @return array If <var>key</var> is empty as sequential list of returned TRANSLATED records.
  115. * If <var>key</var> is not empty then the returned array is indexed by the value
  116. * the database key. Returns <var>null</var> if the query fails.
  117. */
  118. function loadObjectList( $key='', $translate=true, $language=null ) {
  119. global $mosConfig_lang, $_JOOMFISH_MANAGER;
  120. if( $language===null) $language = $mosConfig_lang;
  121. $result = parent::loadObjectList( $key );
  122. if( $translate && isset($_JOOMFISH_MANAGER)) {
  123. $doTranslate=false;
  124. $tables =$this->_getRefTables();
  125. if ($tables == null) return $result; // an unstranslatable query to return result as is
  126. foreach ($tables["fieldTablePairs"] as $i=>$table) {
  127. if ($this->translatedContentAvailable($table)) {
  128. $doTranslate=true;
  129. break;
  130. }
  131. }
  132. if ($doTranslate ) {
  133. JoomFish::translateList( $result, $this->_getTableName(), $language, $this->_getRefTables() );
  134. }
  135. }
  136. return $result;
  137. }
  138. function loadAssocList($key=''){
  139. $objects = $this->loadObjectList($key);
  140. if ($objects && count($objects)>0) {
  141. foreach ($objects as $key=>$val) {
  142. $objects[$key] = get_object_vars($val);
  143. }
  144. }
  145. return $objects;
  146. }
  147. /**
  148. * Overwritten method to loads the first field of the first row returned by the query.
  149. *
  150. * @return The value returned in the query or null if the query failed.
  151. */
  152. function loadResult( $translate=true, $language=null ) {
  153. $result=null;
  154. $ret=null;
  155. $this->loadObject( $result, $translate, $language );
  156. if( $result != null ) {
  157. $fields = get_object_vars( $result );
  158. $field = each($fields);
  159. $ret = $field[1];
  160. }
  161. return $ret;
  162. }
  163. /**
  164. * Overwritten insert function to enable storage of material created in non-default language.
  165. * Note that this creates a translation which is identical to the original - when we update
  166. * the original in the default language we then keep the translation (although it will appread out of date!).
  167. *
  168. * @param string table name
  169. * @param object instance with information to store
  170. * @param string primary key name of table
  171. * @param boolean debug info printed or not
  172. * @param boolean passthru without storing information in a translation table
  173. */
  174. function insertObject( $table, &$object, $keyName = NULL, $verbose=false , $passthru=false) {
  175. global $mosConfig_lang, $mosConfig_defaultLang, $_JOOMFISH_MANAGER;
  176. $passthru = $mosConfig_lang == $mosConfig_defaultLang;
  177. if( !$passthru && isset($_JOOMFISH_MANAGER) && is_a($object, 'mosDBTable')) {
  178. //Must insert parent first to get reference id !
  179. $parentInsertReturn = parent::insertObject( $table, $object, $keyName, $verbose);
  180. $actContentObject=null;
  181. if( isset($table) && $table!="" ) {
  182. $tableName = ereg_replace( '^#__', '', $table);
  183. // *** QUESTION ***//
  184. // IS THIS TEST APPROPRIATE HERE - I THINK IT MEANS YOU CAN'T DO A FIRST TRANSLATION FOR A TABLE VIA THE FRONT END
  185. // ** TEST BEFORE 1.8 **//
  186. //if ($this->translatedContentAvailable($table)) {
  187. $contentElement = $_JOOMFISH_MANAGER->getContentElement( $tableName );
  188. if( isset( $contentElement ) ) {
  189. $actContentObject = new ContentObject( $_JOOMFISH_MANAGER->getLanguageID($mosConfig_lang), $contentElement );
  190. if( isset( $object->$keyName ) ) {
  191. $actContentObject->loadFromContentID( $object->$keyName );
  192. $actContentObject->updateMLContent( $object );
  193. if( isset( $object->state ) ) {
  194. $actContentObject->published = ($object->state == 1) ? true : false;
  195. } else if ( isset( $object->published ) ) {
  196. $actContentObject->published = ($object->published == 1) ? true : false;
  197. }
  198. if ($actContentObject->published){
  199. if ( $_JOOMFISH_MANAGER->getCfg("frontEndPublish")){
  200. global $acl, $my;
  201. $access = new stdClass();
  202. $access->canPublish = $acl->acl_check( 'action', 'publish', 'users', $my->usertype, 'content', 'all' );
  203. if ($access->canPublish) $actContentObject->setPublished($actContentObject->published);
  204. }
  205. }
  206. $actContentObject->store();
  207. }
  208. }
  209. //}
  210. }
  211. return $parentInsertReturn;
  212. }
  213. else {
  214. return parent::insertObject( $table, $object, $keyName, $verbose);
  215. }
  216. }
  217. /**
  218. * Overwritten update function to enable storage of translated information.
  219. * Based on the configuration in the content elements the automatic storage of
  220. * information is activated or not. It is important that this upgraded method will ensure
  221. * that all information will be written into the translation table. Only in the case that the
  222. * default language is choosen the information will be updated directly within the original tables.
  223. * To make sure that all other information will be written into the tables as expected the
  224. * statements will be manipulated as needed.
  225. *
  226. * @param string table name
  227. * @param object instance with information to store
  228. * @param string primary key name of table
  229. * @param boolean update fields with null or not
  230. * @param boolean passthru without storing information in a translation table
  231. */
  232. function updateObject( $table, &$object, $keyName, $updateNulls=true, $passthru=false ) {
  233. global $mosConfig_lang, $mosConfig_defaultLang, $_JOOMFISH_MANAGER;
  234. // if the currect language is the site default language the translations will not be updated!
  235. $passthru = $mosConfig_lang == $mosConfig_defaultLang;
  236. if( !$passthru && isset($_JOOMFISH_MANAGER) && is_a($object, 'mosDBTable')) {
  237. $actContentObject=null;
  238. if( isset($table) && $table!="" ) {
  239. $tableName = ereg_replace( '^#__', '', $table);
  240. // *** QUESTION ***//
  241. // IS THIS TEST APPROPRIATE HERE - I THINK IT MEANS YOU CAN'T DO A FIRST TRANSLATION FOR A TABLE VIA THE FRONT END
  242. // ** TEST BEFORE 1.8 **//
  243. //if ($this->translatedContentAvailable($table)) {
  244. $contentElement = $_JOOMFISH_MANAGER->getContentElement( $tableName );
  245. if( isset( $contentElement ) ) {
  246. $actContentObject = new ContentObject( $_JOOMFISH_MANAGER->getLanguageID($mosConfig_lang), $contentElement );
  247. if( isset( $object->$keyName ) ) {
  248. $actContentObject->loadFromContentID( $object->$keyName );
  249. $actContentObject->updateMLContent( $object );
  250. if( isset( $object->state ) ) {
  251. $actContentObject->published = ($object->state == 1) ? true : false;
  252. } else if ( isset( $object->published ) ) {
  253. $actContentObject->published = ($object->published == 1) ? true : false;
  254. }
  255. if ( $_JOOMFISH_MANAGER->getCfg("frontEndPublish")){
  256. global $acl, $my;
  257. $access = new stdClass();
  258. $access->canPublish = $acl->acl_check( 'action', 'publish', 'users', $my->usertype, 'content', 'all' );
  259. if ($access->canPublish) $actContentObject->setPublished($actContentObject->published);
  260. }
  261. $actContentObject->store();
  262. }
  263. }
  264. //}
  265. }
  266. return parent::updateObject( $table, $object, $keyName, $updateNulls );
  267. } else {
  268. return parent::updateObject( $table, $object, $keyName, $updateNulls );
  269. }
  270. }
  271. /** Internal function to determit the table name from an sql query
  272. *
  273. * @return string table name
  274. */
  275. function _getTableName() {
  276. global $mosConfig_dbprefix;
  277. $posFrom = strpos( strtoupper($this->_sql), 'FROM ') + 5; // after 'FROM '
  278. $posWhere = strpos( strtoupper($this->_sql), 'WHERE ');
  279. $table = substr( $this->_sql, $posFrom, $posWhere - $posFrom);
  280. if( strpos( $table, ' ' ) !== false ) {
  281. $table = substr( $table, 0, strpos( $table, ' ' ) );
  282. }
  283. if (isset($mosConfig_dbprefix) && strlen($mosConfig_dbprefix)>0) $table = ereg_replace( $mosConfig_dbprefix, '', $table);
  284. $table = ereg_replace( "\n", "", $table) ;
  285. return $table;
  286. }
  287. /**
  288. * Override query in order to extract ref tables data
  289. *
  290. * @return n/a
  291. */
  292. function query() {
  293. parent::query();
  294. $this->setRefTables();
  295. return $this->_cursor;
  296. }
  297. function fillRefTableCache($cacheDir,$cacheFile){
  298. $cacheFileContent = serialize($this->_refTables);
  299. $handle = fopen($cacheFile,"wb");
  300. fwrite($handle,$cacheFileContent);
  301. fclose($handle);
  302. // clean out old cache files
  303. // This could be very slow for long list of old files -
  304. // TODO store in database instead
  305. $this->cleanRefTableCache($cacheDir);
  306. }
  307. function cleanRefTableCache($cacheDir){
  308. if (!($dh = opendir($cacheDir))) {
  309. return false;
  310. }
  311. while ($file = readdir($dh)) {
  312. if (($file != '.') && ($file != '..')) {
  313. $file = "$cacheDir/$file";
  314. if (is_file($file) && @filemtime($file) < $this->cacheExpiry) {
  315. if (!@unlink($file)) {
  316. echo "problems clearing cache file $file";
  317. }
  318. }
  319. }
  320. }
  321. return true;
  322. }
  323. function _logSetRefTablecache($action,$tempsql,$sql_exNos,$sqlHash){
  324. $logfile = dirname(__FILE__)."/qalog.txt";
  325. $handle = fopen($logfile,"ab");
  326. // replace tabs and carriage returns with spaces
  327. fwrite($handle,"$action ");
  328. fwrite($handle,preg_replace("/([\t\n\r\f])/"," ",$tempsql));
  329. fwrite($handle," #@�^�@# ");
  330. fwrite($handle,preg_replace("/([\t\n\r\f])/"," ",$sql_exNos));
  331. fwrite($handle," #@�^�@# ");
  332. fwrite($handle,preg_replace("/([\t\n\r\f])/"," ",$sqlHash));
  333. fwrite($handle," # JF LINE END# \n");
  334. fclose($handle);
  335. }
  336. function setRefTables(){
  337. // Before joomfish manager is created since we can't translate so skip this anaylsis
  338. global $_JOOMFISH_MANAGER;
  339. if (!$_JOOMFISH_MANAGER) return;
  340. // This could be speeded up by the use of a cache - but only of benefit is global caching is off
  341. $tempsql = $this->_sql;
  342. // only needed for selects at present - possibly add for inserts/updates later
  343. if (strpos(strtoupper($tempsql),"SELECT")===false) {
  344. return;
  345. }
  346. if ($_JOOMFISH_MANAGER->getCfg("qacaching",0)){
  347. global $mosConfig_cachepath,$mosConfig_cachetime;
  348. // remove time formats (assume all numbers are not necessay - this is experimental
  349. // for example table names or column names could contain numbers
  350. // so this version only replaces numbers not adjacent to alpha characters i.e.field2 doesn't become field
  351. $sql_exNos = preg_replace("/(?![a-z])(.)([0-9]+)(?![a-z]+)/i",'${1}',$tempsql);
  352. $sql_exNos = preg_replace("/(?![a-z]).([0-9]+)$/i",'${1}',$sql_exNos);
  353. global $mosConfig_debug;
  354. if ($mosConfig_debug) {
  355. echo "<p style='background-color:bisque;border:solid 1px black'><strong>setRefTables debug:</strong><br / >"
  356. . "tempsql = $tempsql<br />"
  357. . "sql_exNos = $sql_exNos"
  358. . "</p>";
  359. }
  360. $sqlHash = md5($sql_exNos );
  361. $this->cacheExpiry = time() - $mosConfig_cachetime;
  362. $cacheDir = "$mosConfig_cachepath/refTableSQL";
  363. if (!file_exists($cacheDir)) mkdir($cacheDir);
  364. $cacheFile = "$cacheDir/$sqlHash";
  365. if (file_exists($cacheFile) && @filemtime($cacheFile) > $this->cacheExpiry) {
  366. $cacheFileContent = file_get_contents($cacheFile);
  367. $this->_refTables = unserialize($cacheFileContent);
  368. if ($_JOOMFISH_MANAGER->getCfg("qalogging",0)){
  369. $this->_logSetRefTablecache("r",$tempsql,$sql_exNos,$sqlHash);
  370. }
  371. return;
  372. }
  373. if($this->_cursor===true || $this->_cursor===false) {
  374. if ($_JOOMFISH_MANAGER->getCfg("qalogging",0)){
  375. $this->_logSetRefTablecache("wtf",$tempsql,$sql_exNos,$sqlHash);
  376. }
  377. $this->fillRefTableCache($cacheDir,$cacheFile);
  378. return;
  379. }
  380. }
  381. // get column metadata
  382. $i = 0;
  383. //echo " $this->_sql $this->_cursor ".var_export( $this->_cursor, true )."<br>";
  384. if (is_object($this->_cursor) && get_class($this->_cursor)=="mysqli_result"){
  385. $dbIsMySQL = false;
  386. }
  387. else {
  388. if (!is_resource($this->_cursor)){
  389. // This is a serious problem since we do not have a valid db connection
  390. // or there is an error in the query
  391. if ($this->getErrorMsg()===""){
  392. //echo JText::_("No valid database connection")."<br/>";
  393. }
  394. return;
  395. }
  396. $cursorType = get_resource_type($this->_cursor);
  397. if ($cursorType == "mysql result") {
  398. $dbIsMySQL = true;
  399. }
  400. else $dbIsMySQL = false;
  401. }
  402. if (!$dbIsMySQL && function_exists("mysqli_num_fields")) $fields = mysqli_num_fields($this->_cursor);
  403. else $fields = mysql_num_fields($this->_cursor);
  404. //print "<br> $tempsql $this->_cursor $fields";
  405. if ($_JOOMFISH_MANAGER->getCfg("qacaching",0)){
  406. if ($fields<=0) {
  407. if ($_JOOMFISH_MANAGER->getCfg("qalogging",0)){
  408. $this->_logSetRefTablecache("w0f",$tempsql,$sql_exNos,$sqlHash);
  409. }
  410. $this->fillRefTableCache($cacheDir,$cacheFile);
  411. return;
  412. }
  413. }
  414. $this->_refTables=array();
  415. $this->_refTables["fieldTablePairs"]=array();
  416. $this->_refTables["tableAliases"]=array();
  417. $this->_refTables["fieldAliases"]=array();
  418. $this->_refTables["fieldTableAliasData"]=array();
  419. $this->_refTables["fieldCount"]=$fields;
  420. $this->_refTables["sql"]=$tempsql;
  421. // local variable to keep track of aliases that have already been analysed
  422. $tableAliases = array();
  423. for ($i = 0; $i < $fields; ++$i) {
  424. if (!$dbIsMySQL && function_exists("mysqli_fetch_field")) $meta = mysqli_fetch_field($this->_cursor);
  425. else $meta = mysql_fetch_field($this->_cursor, $i);
  426. if (!$meta) {
  427. echo "No information available<br />\n";
  428. }
  429. else {
  430. $tempTable = $meta->table;
  431. // if I have already found the table alias no need to do it again!
  432. if (array_key_exists($tempTable,$tableAliases)){
  433. $value = $tableAliases[$tempTable];
  434. }
  435. else {
  436. if (!isset($tempTable) || strlen($tempTable)==0) {
  437. continue;
  438. }
  439. //echo "<br>Information for column $i of ".($fields-1)." ".$meta->name." : $tempTable=";
  440. $tempArray=array();
  441. $prefix = $this->_table_prefix;
  442. preg_match_all("/$prefix(\w*)\s+AS\s+".$tempTable."[,\s]/i",$this->_sql, $tempArray, PREG_PATTERN_ORDER);
  443. if (count($tempArray)>1 && count($tempArray[1])>0) $value = $tempArray[1][0];
  444. else $value = null;
  445. if (isset($this->_table_prefix) && strlen($this->_table_prefix)>0 && strpos($tempTable,$this->_table_prefix)===0) $tempTable = substr($tempTable, strlen( $this->_table_prefix));
  446. $value = $value?$value:$tempTable;
  447. $tableAliases[$tempTable]=$value;
  448. }
  449. if ((!($value=="session" || strpos($value,"jf_")===0)) && $this->translatedContentAvailable($value)){
  450. /// ARGH !!! I must also look for aliases for fieldname !!
  451. $tempName = $meta->name;
  452. $tempArray=array();
  453. preg_match_all("/(\w*)\s+AS\s+".$tempName."[,\s]/i",$this->_sql, $tempArray, PREG_PATTERN_ORDER);
  454. if (count($tempArray)>1 && count($tempArray[1])>0) {
  455. //echo "$meta->name is an alias for ".$tempArray[1][0]."<br>";
  456. $nameValue = $tempArray[1][0];
  457. }
  458. else $nameValue = $meta->name;
  459. if (!array_key_exists($value,$this->_refTables["tableAliases"])) $this->_refTables["tableAliases"][$value]=$meta->table;
  460. // I can't use the field name as the key since it may not be unique!
  461. if (!array_key_exists($meta->table,$this->_refTables["fieldTablePairs"])){
  462. $this->_refTables["fieldTablePairs"][$meta->table]=$value;
  463. }
  464. //if (!in_array($value,$this->_refTables["fieldTablePairs"])) $this->_refTables["fieldTablePairs"][]=$value;
  465. if (!array_key_exists($nameValue,$this->_refTables["fieldAliases"])) $this->_refTables["fieldAliases"][$meta->name]=$nameValue;
  466. // Put all the mapping data together so that everything is in sync and I can check fields vs aliases vs tables in one place
  467. $this->_refTables["fieldTableAliasData"][$i]=array("fieldNameAlias"=>$meta->name, "fieldName"=>$nameValue,"tableNameAlias"=>$meta->table,"tableName"=>$value);
  468. }
  469. }
  470. }
  471. if ($_JOOMFISH_MANAGER->getCfg("qacaching",0)){
  472. if ($_JOOMFISH_MANAGER->getCfg("qalogging",0)){
  473. $this->_logSetRefTablecache("wn",$tempsql,$sql_exNos,$sqlHash);
  474. }
  475. $this->fillRefTableCache($cacheDir,$cacheFile);
  476. }
  477. }
  478. /** Internal function to return reference table names from an sql query
  479. *
  480. * @return string table name
  481. */
  482. function _getRefTables(){
  483. return $this->_refTables;
  484. }
  485. }
  486. ?>