sfMessageSource_MySQL.class.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. <?php
  2. /**
  3. * sfMessageSource_MySQL class file.
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the BSD License.
  7. *
  8. * Copyright(c) 2004 by Qiang Xue. All rights reserved.
  9. *
  10. * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue}
  11. * The latest version of PRADO can be obtained from:
  12. * {@link http://prado.sourceforge.net/}
  13. *
  14. * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
  15. * @version $Id: sfMessageSource_MySQL.class.php 9128 2008-05-21 00:58:19Z Carl.Vondrick $
  16. * @package symfony
  17. * @subpackage i18n
  18. */
  19. /**
  20. * sfMessageSource_MySQL class.
  21. *
  22. * Retrieve the message translation from a MySQL database.
  23. *
  24. * See the MessageSource::factory() method to instantiate this class.
  25. *
  26. * MySQL schema:
  27. *
  28. * CREATE TABLE `catalogue` (
  29. * `cat_id` int(11) NOT NULL auto_increment,
  30. * `name` varchar(100) NOT NULL default '',
  31. * `source_lang` varchar(100) NOT NULL default '',
  32. * `target_lang` varchar(100) NOT NULL default '',
  33. * `date_created` int(11) NOT NULL default '0',
  34. * `date_modified` int(11) NOT NULL default '0',
  35. * `author` varchar(255) NOT NULL default '',
  36. * PRIMARY KEY (`cat_id`)
  37. * ) TYPE=InnoDB;
  38. *
  39. * CREATE TABLE `trans_unit` (
  40. * `msg_id` int(11) NOT NULL auto_increment,
  41. * `cat_id` int(11) NOT NULL default '1',
  42. * `id` varchar(255) NOT NULL default '',
  43. * `source` text NOT NULL,
  44. * `target` text NOT NULL,
  45. * `comments` text NOT NULL,
  46. * `date_added` int(11) NOT NULL default '0',
  47. * `date_modified` int(11) NOT NULL default '0',
  48. * `author` varchar(255) NOT NULL default '',
  49. * `translated` tinyint(1) NOT NULL default '0',
  50. * PRIMARY KEY (`msg_id`)
  51. * ) TYPE=InnoDB;
  52. *
  53. * Propel schema (in .xml format):
  54. *
  55. * <database ...>
  56. * ...
  57. * <table name="catalogue">
  58. * <column name="cat_id" type="integer" required="true" primaryKey="true" autoincrement="true" />
  59. * <column name="name" type="varchar" size="100" />
  60. * <column name="source_lang" type="varchar" size="100" />
  61. * <column name="target_lang" type="varchar" size="100" />
  62. * <column name="date_created" type="timestamp" />
  63. * <column name="date_modified" type="timestamp" />
  64. * <column name="author" type="varchar" size="255" />
  65. * </table>
  66. *
  67. * <table name="trans_unit">
  68. * <column name="msg_id" type="integer" required="true" primaryKey="true" autoincrement="true" />
  69. * <column name="cat_id" type="integer" />
  70. * <foreign-key foreignTable="catalogue" onDelete="cascade">
  71. * <reference local="cat_id" foreign="cat_id"/>
  72. * </foreign-key>
  73. * <column name="id" type="varchar" size="255" />
  74. * <column name="source" type="longvarchar" />
  75. * <column name="target" type="longvarchar" />
  76. * <column name="comments" type="longvarchar" />
  77. * <column name="date_created" type="timestamp" />
  78. * <column name="date_modified" type="timestamp" />
  79. * <column name="author" type="varchar" size="255" />
  80. * <column name="translated" type="integer" />
  81. * </table>
  82. * ...
  83. * </database>
  84. *
  85. * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com>
  86. * @version v1.0, last update on Fri Dec 24 16:58:58 EST 2004
  87. * @package symfony
  88. * @subpackage i18n
  89. */
  90. class sfMessageSource_MySQL extends sfMessageSource_Database
  91. {
  92. /**
  93. * The datasource string, full DSN to the database.
  94. * @var string
  95. */
  96. protected $source;
  97. /**
  98. * The DSN array property, parsed by PEAR's DB DSN parser.
  99. * @var array
  100. */
  101. protected $dsn;
  102. /**
  103. * A resource link to the database
  104. * @var db
  105. */
  106. protected $db;
  107. /**
  108. * Constructor.
  109. * Creates a new message source using MySQL.
  110. *
  111. * @param string $source MySQL datasource, in PEAR's DB DSN format.
  112. * @see MessageSource::factory();
  113. */
  114. function __construct($source)
  115. {
  116. $this->source = (string) $source;
  117. $this->dsn = $this->parseDSN($this->source);
  118. $this->db = $this->connect();
  119. }
  120. /**
  121. * Destructor, closes the database connection.
  122. */
  123. function __destruct()
  124. {
  125. @mysql_close($this->db);
  126. }
  127. /**
  128. * Connects to the MySQL datasource
  129. *
  130. * @return resource MySQL connection.
  131. * @throws sfException, connection and database errors.
  132. */
  133. protected function connect()
  134. {
  135. $dsninfo = $this->dsn;
  136. if (isset($dsninfo['protocol']) && $dsninfo['protocol'] == 'unix')
  137. {
  138. $dbhost = ':'.$dsninfo['socket'];
  139. }
  140. else
  141. {
  142. $dbhost = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'localhost';
  143. if (!empty($dsninfo['port']))
  144. {
  145. $dbhost .= ':'.$dsninfo['port'];
  146. }
  147. }
  148. $user = $dsninfo['username'];
  149. $pw = $dsninfo['password'];
  150. $connect_function = 'mysql_connect';
  151. if ($dbhost && $user && $pw)
  152. {
  153. $conn = @$connect_function($dbhost, $user, $pw);
  154. }
  155. elseif ($dbhost && $user)
  156. {
  157. $conn = @$connect_function($dbhost, $user);
  158. }
  159. elseif ($dbhost)
  160. {
  161. $conn = @$connect_function($dbhost);
  162. }
  163. else
  164. {
  165. $conn = false;
  166. }
  167. if (empty($conn))
  168. {
  169. throw new sfException(sprintf('Error in connecting to %s.', $dsninfo));
  170. }
  171. if ($dsninfo['database'])
  172. {
  173. if (!@mysql_select_db($dsninfo['database'], $conn))
  174. {
  175. throw new sfException(sprintf('Error in connecting database, dsn: %s.', $dsninfo));
  176. }
  177. }
  178. else
  179. {
  180. throw new sfException('Please provide a database for message translation.');
  181. }
  182. return $conn;
  183. }
  184. /**
  185. * Gets the database connection.
  186. *
  187. * @return db database connection.
  188. */
  189. public function connection()
  190. {
  191. return $this->db;
  192. }
  193. /**
  194. * Gets an array of messages for a particular catalogue and cultural variant.
  195. *
  196. * @param string $variant the catalogue name + variant
  197. * @return array translation messages.
  198. */
  199. public function &loadData($variant)
  200. {
  201. $variant = mysql_real_escape_string($variant, $this->db);
  202. $statement =
  203. "SELECT t.id, t.source, t.target, t.comments
  204. FROM trans_unit t, catalogue c
  205. WHERE c.cat_id = t.cat_id
  206. AND c.name = '{$variant}'
  207. ORDER BY id ASC";
  208. $rs = mysql_query($statement, $this->db);
  209. $result = array();
  210. while ($row = mysql_fetch_array($rs, MYSQL_NUM))
  211. {
  212. $source = $row[1];
  213. $result[$source][] = $row[2]; //target
  214. $result[$source][] = $row[0]; //id
  215. $result[$source][] = $row[3]; //comments
  216. }
  217. return $result;
  218. }
  219. /**
  220. * Gets the last modified unix-time for this particular catalogue+variant.
  221. * We need to query the database to get the date_modified.
  222. *
  223. * @param string $source catalogue+variant
  224. * @return int last modified in unix-time format.
  225. */
  226. protected function getLastModified($source)
  227. {
  228. $source = mysql_real_escape_string($source, $this->db);
  229. $rs = mysql_query("SELECT date_modified FROM catalogue WHERE name = '{$source}'", $this->db);
  230. $result = $rs ? intval(mysql_result($rs, 0)) : 0;
  231. return $result;
  232. }
  233. /**
  234. * Checks if a particular catalogue+variant exists in the database.
  235. *
  236. * @param string $variant catalogue+variant
  237. * @return boolean true if the catalogue+variant is in the database, false otherwise.
  238. */
  239. public function isValidSource($variant)
  240. {
  241. $variant = mysql_real_escape_string ($variant, $this->db);
  242. $rs = mysql_query("SELECT COUNT(*) FROM catalogue WHERE name = '{$variant}'", $this->db);
  243. $row = mysql_fetch_array($rs, MYSQL_NUM);
  244. $result = $row && $row[0] == '1';
  245. return $result;
  246. }
  247. /**
  248. * Retrieves catalogue details, array($cat_id, $variant, $count).
  249. *
  250. * @param string $catalogue catalogue
  251. * @return array catalogue details, array($cat_id, $variant, $count).
  252. */
  253. protected function getCatalogueDetails($catalogue = 'messages')
  254. {
  255. if (empty($catalogue))
  256. {
  257. $catalogue = 'messages';
  258. }
  259. $variant = $catalogue.'.'.$this->culture;
  260. $name = mysql_real_escape_string($this->getSource($variant), $this->db);
  261. $rs = mysql_query("SELECT cat_id FROM catalogue WHERE name = '{$name}'", $this->db);
  262. if (mysql_num_rows($rs) != 1)
  263. {
  264. return false;
  265. }
  266. $cat_id = intval(mysql_result($rs, 0));
  267. // first get the catalogue ID
  268. $rs = mysql_query("SELECT COUNT(*) FROM trans_unit WHERE cat_id = {$cat_id}", $this->db);
  269. $count = intval(mysql_result($rs, 0));
  270. return array($cat_id, $variant, $count);
  271. }
  272. /**
  273. * Updates the catalogue last modified time.
  274. *
  275. * @return boolean true if updated, false otherwise.
  276. */
  277. protected function updateCatalogueTime($cat_id, $variant)
  278. {
  279. $time = time();
  280. $result = mysql_query("UPDATE catalogue SET date_modified = {$time} WHERE cat_id = {$cat_id}", $this->db);
  281. if ($this->cache)
  282. {
  283. $this->cache->remove($variant.':'.$this->culture);
  284. }
  285. return $result;
  286. }
  287. /**
  288. * Saves the list of untranslated blocks to the translation source.
  289. * If the translation was not found, you should add those
  290. * strings to the translation source via the <b>append()</b> method.
  291. *
  292. * @param string $catalogue the catalogue to add to
  293. * @return boolean true if saved successfuly, false otherwise.
  294. */
  295. function save($catalogue = 'messages')
  296. {
  297. $messages = $this->untranslated;
  298. if (count($messages) <= 0)
  299. {
  300. return false;
  301. }
  302. $details = $this->getCatalogueDetails($catalogue);
  303. if ($details)
  304. {
  305. list($cat_id, $variant, $count) = $details;
  306. }
  307. else
  308. {
  309. return false;
  310. }
  311. if ($cat_id <= 0)
  312. {
  313. return false;
  314. }
  315. $inserted = 0;
  316. $time = time();
  317. foreach ($messages as $message)
  318. {
  319. $count++;
  320. $inserted++;
  321. $message = mysql_real_escape_string($message, $this->db);
  322. $statement = "INSERT INTO trans_unit
  323. (cat_id,id,source,date_added) VALUES
  324. ({$cat_id}, {$count},'{$message}',$time)";
  325. mysql_query($statement, $this->db);
  326. }
  327. if ($inserted > 0)
  328. {
  329. $this->updateCatalogueTime($cat_id, $variant);
  330. }
  331. return $inserted > 0;
  332. }
  333. /**
  334. * Deletes a particular message from the specified catalogue.
  335. *
  336. * @param string $message the source message to delete.
  337. * @param string $catalogue the catalogue to delete from.
  338. * @return boolean true if deleted, false otherwise.
  339. */
  340. function delete($message, $catalogue = 'messages')
  341. {
  342. $details = $this->getCatalogueDetails($catalogue);
  343. if ($details)
  344. {
  345. list($cat_id, $variant, $count) = $details;
  346. }
  347. else
  348. {
  349. return false;
  350. }
  351. $text = mysql_real_escape_string($message, $this->db);
  352. $statement = "DELETE FROM trans_unit WHERE cat_id = {$cat_id} AND source = '{$message}'";
  353. $deleted = false;
  354. mysql_query($statement, $this->db);
  355. if (mysql_affected_rows($this->db) == 1)
  356. {
  357. $deleted = $this->updateCatalogueTime($cat_id, $variant);
  358. }
  359. return $deleted;
  360. }
  361. /**
  362. * Updates the translation.
  363. *
  364. * @param string $text the source string.
  365. * @param string $target the new translation string.
  366. * @param string $comments comments
  367. * @param string $catalogue the catalogue of the translation.
  368. * @return boolean true if translation was updated, false otherwise.
  369. */
  370. function update($text, $target, $comments, $catalogue = 'messages')
  371. {
  372. $details = $this->getCatalogueDetails($catalogue);
  373. if ($details)
  374. {
  375. list($cat_id, $variant, $count) = $details;
  376. }
  377. else
  378. {
  379. return false;
  380. }
  381. $comments = mysql_real_escape_string($comments, $this->db);
  382. $target = mysql_real_escape_string($target, $this->db);
  383. $text = mysql_real_escape_string($text, $this->db);
  384. $time = time();
  385. $statement = "UPDATE trans_unit SET target = '{$target}', comments = '{$comments}', date_modified = '{$time}' WHERE cat_id = {$cat_id} AND source = '{$text}'";
  386. $updated = false;
  387. mysql_query($statement, $this->db);
  388. if (mysql_affected_rows($this->db) == 1)
  389. {
  390. $updated = $this->updateCatalogueTime($cat_id, $variant);
  391. }
  392. return $updated;
  393. }
  394. /**
  395. * Returns a list of catalogue as key and all it variants as value.
  396. *
  397. * @return array list of catalogues
  398. */
  399. function catalogues()
  400. {
  401. $statement = 'SELECT name FROM catalogue ORDER BY name';
  402. $rs = mysql_query($statement, $this->db);
  403. $result = array();
  404. while($row = mysql_fetch_array($rs, MYSQL_NUM))
  405. {
  406. $details = explode('.', $row[0]);
  407. if (!isset($details[1]))
  408. {
  409. $details[1] = null;
  410. }
  411. $result[] = $details;
  412. }
  413. return $result;
  414. }
  415. }