PageRenderTime 65ms CodeModel.GetById 29ms RepoModel.GetById 1ms app.codeStats 0ms

/library/text.php

https://github.com/alugo/Goteo
PHP | 680 lines | 477 code | 76 blank | 127 comment | 62 complexity | f4fe4447c82f674265c47933ed2accd2 MD5 | raw file
Possible License(s): AGPL-1.0
  1. <?php
  2. /*
  3. * Copyright (C) 2012 Platoniq y Fundación Fuentes Abiertas (see README for details)
  4. * This file is part of Goteo.
  5. *
  6. * Goteo is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Goteo is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Affero General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Affero General Public License
  17. * along with Goteo. If not, see <http://www.gnu.org/licenses/agpl.txt>.
  18. *
  19. */
  20. namespace Goteo\Library {
  21. use Goteo\Core\Model,
  22. Goteo\Core\Registry,
  23. Goteo\Core\Exception;
  24. /*
  25. * Clase para sacar textos dinámicos de la tabla text
  26. * @TODO, definir donde se define y se cambia la constante LANG y utilizarla en los _::get_
  27. */
  28. class Text {
  29. public
  30. $id,
  31. $lang,
  32. $text,
  33. $purpose,
  34. $html,
  35. $pendiente; // no traducido
  36. /*
  37. * Devuelve un texto en HTML
  38. */
  39. static public function html ($id) {
  40. // sacamos el contenido del texto
  41. $text = call_user_func_array ( 'static::get' , \func_get_args() );
  42. if (self::isHtml($id))
  43. return $text; // el texto ES html, lo devuelve tal cual
  44. else
  45. return \htmlspecialchars ($text); // el texto NO es html, lo pasa por html especial chars
  46. }
  47. /*
  48. * Devuelve un texto sin HTML
  49. */
  50. static public function plain ($id) {
  51. // sacamos el contenido del texto
  52. $text = call_user_func_array ( 'static::get' , \func_get_args() );
  53. if (self::isHtml($id))
  54. return \strip_tags($text) ; // ES html, le quitamos los tags
  55. else
  56. return $text;
  57. }
  58. /*
  59. * Devuelve un texto con comillas escapadas para usarlo en javascript
  60. */
  61. static public function slash ($id) {
  62. // sacamos el contenido del texto
  63. $text = call_user_func_array ( 'static::get' , \func_get_args() );
  64. return \addslashes($text);
  65. }
  66. static public function getAdmin ($id) {
  67. // buscamos el texto en la tabla, si no está sacamos el propósito
  68. $values = array(':id'=>$id, ':lang' => LANG);
  69. $sql = "SELECT
  70. IFNULL(text.text,purpose.purpose) as `text`
  71. FROM purpose
  72. LEFT JOIN text
  73. ON text.id = purpose.text
  74. AND text.lang = :lang
  75. WHERE text.id = :id
  76. ";
  77. $query = Model::query($sql, $values);
  78. return $query->fetchObject()->text;
  79. }
  80. static public function getTrans ($id) {
  81. $lang = $_SESSION['translate_lang'];
  82. // buscamos el texto en la tabla, si no está sacamos el propósito
  83. $values = array(':id'=>$id, ':lang' => $lang);
  84. $sql = "SELECT
  85. IFNULL(text.text,purpose.purpose) as `text`
  86. FROM purpose
  87. LEFT JOIN text
  88. ON text.id = purpose.text
  89. AND text.lang = :lang
  90. WHERE text.id = :id
  91. ";
  92. $query = Model::query($sql, $values);
  93. return $query->fetchObject()->text;
  94. }
  95. /**
  96. * Gettext-like interface for the i18n of interface strings.
  97. *
  98. * @param string $str string to translate
  99. * @return string translated version
  100. */
  101. static public function _($str) {
  102. return Registry::get('translate')->text($str);
  103. }
  104. static public function get ($id) {
  105. $lang = LANG;
  106. if (\defined('GOTEO_ADMIN_NOCACHE')) {
  107. $nocache = true;
  108. } else {
  109. $nocache = false;
  110. }
  111. // si hay mas de un argumento, hay que meter el resto con
  112. $args = \func_get_args();
  113. if (count($args) > 1) {
  114. array_shift($args);
  115. } else {
  116. $args = array();
  117. }
  118. // buscamos el texto en cache
  119. static $_cache = array();
  120. if (!$nocache && isset($_cache[$id][$lang]) && empty($args)) {
  121. return $_cache[$id][$lang];
  122. }
  123. // buscamos el texto en la tabla
  124. $values = array(':id'=>$id, ':lang' => $lang);
  125. // Español de purpose como alternativa
  126. $sql_es = "SELECT
  127. IFNULL(text.text,purpose.purpose) as `text`
  128. FROM purpose
  129. LEFT JOIN text
  130. ON text.id = purpose.text
  131. AND text.lang = :lang
  132. WHERE purpose.text = :id
  133. ";
  134. // Inglés como alternativa
  135. $sql_en = "SELECT
  136. IFNULL(text.text, eng.text) as `text`
  137. FROM purpose
  138. LEFT JOIN text
  139. ON text.id = purpose.text
  140. AND text.lang = :lang
  141. LEFT JOIN text as eng
  142. ON eng.id = purpose.text
  143. AND eng.lang = 'en'
  144. WHERE purpose.text = :id
  145. ";
  146. // idiomas no españoles usan alternativa en inglés
  147. $sql = (in_array($lang, array('es','ca', 'gl', 'eu', 'en'))) ? $sql_es : $sql_en;
  148. $query = Model::query($sql, $values);
  149. if ($exist = $query->fetchObject()) {
  150. $tmptxt = $_cache[$id][$lang] = $exist->text;
  151. //contamos cuantos argumentos necesita el texto
  152. $req_args = \substr_count($exist->text, '%');
  153. if (!empty($args) && $req_args > 0 && count($args) >= $req_args) {
  154. $texto = $nocache ? vsprintf($exist->text, $args) : vsprintf($tmptxt, $args);
  155. } else {
  156. $texto = $nocache ? $exist->text : $tmptxt;
  157. }
  158. } else {
  159. // para catalogar textos nuevos
  160. // Model::query("REPLACE INTO purpose (text, purpose, html, `group`) VALUES (:text, :purpose, NULL, 'new')", array(':text' => $id, ':purpose' => $id));
  161. $texto = $id;
  162. }
  163. $texto = nl2br($texto);
  164. return $texto;
  165. }
  166. static public function getPurpose ($id) {
  167. // buscamos la explicación del texto en la tabla
  168. $query = Model::query("SELECT purpose, html FROM purpose WHERE `text` = :id", array(':id' => $id));
  169. $exist = $query->fetchObject();
  170. if (!empty($exist->purpose)) {
  171. return $exist->purpose;
  172. } else {
  173. Model::query("REPLACE INTO purpose (text, purpose) VALUES (:text, :purpose)", array(':text' => $id, ':purpose' => "Texto $id"));
  174. return 'Texto: ' . $id;
  175. }
  176. }
  177. /*
  178. * Si un texto esta marcado como html devuelve true, si no está marcado así, false
  179. * Se marca en la tabla de propósitos ya que en la tabla texts habría que marcarlo en cada idioma
  180. */
  181. static public function isHtml ($id) {
  182. try
  183. {
  184. // lo miramos en la tabla de propósitos
  185. $query = Model::query("SELECT html FROM purpose WHERE text = :id", array(':id' => $id));
  186. $purpose = $query->fetchObject();
  187. if ($purpose->html == 1)
  188. return true;
  189. else
  190. return false;
  191. } catch (\PDOException $e) {
  192. return false; // Si la tabla purpose no tiene el campo html
  193. }
  194. }
  195. /*
  196. * Metodo para la lista de textos segun idioma
  197. */
  198. public static function getAll($filters = array(), $lang = null) {
  199. $texts = array();
  200. $values = array(':lang' => $lang);
  201. $sql = "SELECT
  202. purpose.text as id,
  203. IFNULL(text.text,purpose.purpose) as text,
  204. IF(text.text IS NULL, 1, 0) as pendiente,
  205. purpose.`group` as `group`
  206. FROM purpose
  207. LEFT JOIN text
  208. ON text.id = purpose.text
  209. AND text.lang = :lang
  210. WHERE purpose.text != ''
  211. ";
  212. if (!empty($filters['idfilter'])) {
  213. $sql .= " AND purpose.text LIKE :idfilter";
  214. $values[':idfilter'] = "%{$filters['idfilter']}%";
  215. }
  216. if (!empty($filters['group'])) {
  217. $sql .= " AND purpose.`group` = :group";
  218. $values[':group'] = "{$filters['group']}";
  219. }
  220. if (!empty($filters['text'])) {
  221. $sql .= " AND ( text.text LIKE :text OR (text.text IS NULL AND purpose.purpose LIKE :text ))";
  222. $values[':text'] = "%{$filters['text']}%";
  223. }
  224. // pendientes de traducir
  225. if (!empty($filters['pending'])) {
  226. $sql .= " HAVING pendiente = 1";
  227. }
  228. $sql .= " ORDER BY pendiente DESC, text ASC";
  229. try {
  230. $query = Model::query($sql, $values);
  231. foreach ($query->fetchAll(\PDO::FETCH_CLASS, __CLASS__) as $text) {
  232. $texts[] = $text;
  233. }
  234. return $texts;
  235. } catch (\PDOException $e) {
  236. throw new Exception($e->getMessage() . "<br />$sql<br /><pre>" . print_r($values, 1) . "</pre>");
  237. }
  238. }
  239. /*
  240. * Esto se usa para traducciones
  241. */
  242. public static function save($data, &$errors = array()) {
  243. if (!is_array($data) ||
  244. empty($data['id']) ||
  245. empty($data['text']) ||
  246. empty($data['lang'])) {
  247. return false;
  248. }
  249. $sql = "REPLACE `text` SET
  250. `text` = :text,
  251. id = :id,
  252. lang = :lang
  253. ";
  254. if (Model::query($sql, array(':text' => $data['text'], ':id' => $data['id'], ':lang' => $data['lang']))) {
  255. return true;
  256. } else {
  257. $errors[] = 'Error al insertar los datos <pre>' . print_r($data, 1) . '</pre>';
  258. return false;
  259. }
  260. }
  261. /*
  262. * Esto se usa para gestión de originales
  263. */
  264. public static function update($data, &$errors = array()) {
  265. if (!is_array($data) ||
  266. empty($data['id']) ||
  267. empty($data['text'])) {
  268. return false;
  269. }
  270. $sql = "UPDATE `purpose` SET
  271. `purpose` = :text
  272. WHERE `text` = :id
  273. ";
  274. if (Model::query($sql, array(':text' => $data['text'], ':id' => $data['id']))) {
  275. return true;
  276. } else {
  277. $errors[] = 'Error al insertar los datos <pre>' . print_r($data, 1) . '</pre>';
  278. return false;
  279. }
  280. }
  281. /*
  282. * Grupos de textos
  283. */
  284. static public function groups()
  285. {
  286. $groups = array(
  287. 'home' => static::_('Portada'),
  288. 'public_profile' => static::_('Pagina de perfil de usuario'),
  289. 'project' => static::_('Proyecto, pública y formulario'),
  290. 'form' => static::_('Generales del formulario de proyecto'),
  291. 'profile' => static::_('Gestión de perfil del usuario'),
  292. 'personal' => static::_('Datos personales del usuario'),
  293. 'overview' => static::_('Descripción del proyecto'),
  294. 'costs' => static::_('Costes del proyecto'),
  295. 'rewards' => static::_('Retornos y recompensas del proyecto'),
  296. 'supports' => static::_('Colaboraciones del proyecto'),
  297. 'preview' => static::_('Previsualización del proyecto'),
  298. 'dashboard'=> static::_('Dashboard del usuario'),
  299. 'register' => static::_('Registro de usuarios'),
  300. 'login' => static::_('Pagina de login'),
  301. 'discover' => static::_('Sección descubre proyectos'),
  302. 'community' => static::_('Sección comunidad'),
  303. 'general' => static::_('Propósito general'),
  304. 'blog' => static::_('Blog/Actualizaciones'),
  305. 'faq' => static::_('Pagina de FAQ'),
  306. 'contact' => static::_('Pagina de contacto'),
  307. 'widget' => static::_('Banderolos'),
  308. 'invest' => static::_('Pagina de aportar a un proyecto'),
  309. 'types' => static::_('Tooltips para tipos de necesidades'),
  310. 'banners' => static::_('Banners y cabeceras'),
  311. 'footer' => static::_('Footer'),
  312. 'social' => static::_('Cuentas de redes sociales'),
  313. 'review' => static::_('Panel revisor'),
  314. 'translate' => static::_('Panel traductor'),
  315. 'menu' => static::_('Menu superior'),
  316. 'feed' => static::_('Eventos recientes'),
  317. 'mailer' => static::_('Emails automaticos'),
  318. 'bluead' => static::_('Avisos azules'),
  319. 'error' => static::_('Errores catastroficos'),
  320. 'call_public' => static::_('Convocatorias: publicos'),
  321. 'call_form' => static::_('Convocatorias: formulario'),
  322. 'call_dash' => static::_('Convocatorias: dashboard'),
  323. 'wof' => static::_('Wall of friends'),
  324. 'node_public' => static::_('Nodos'),
  325. 'contract' => static::_('Formulario Contrato')
  326. );
  327. \asort($groups);
  328. return $groups;
  329. }
  330. /*
  331. * Devuelve el número de palabras del contenido recibido
  332. */
  333. static public function wordCount ($section, $table, $fields = array(), &$total = 0 ) {
  334. $count = 0;
  335. $sqlFilter = '';
  336. switch ($section) {
  337. case 'texts':
  338. // todos son de la tabla purpose, $table nos indica la agrupación
  339. // y hay que filtrar la columna group
  340. $sqlFilter = " WHERE `group` = '{$table}'";
  341. $table = 'purpose';
  342. $fields = array('purpose');
  343. break;
  344. case 'pages':
  345. // table nos indica si es la de descripciones o la de contenido,
  346. // en la de contenido hay que filtrar nodo goteo y español
  347. if ($table == 'page_node') {
  348. $sqlFilter = " WHERE node = 'goteo' AND lang = 'es'";
  349. }
  350. break;
  351. case 'contents':
  352. case 'home':
  353. // ojo! post es solo del blog 1 (goteo)
  354. if ($table == 'post') {
  355. $sqlFilter = " WHERE blog = '1'";
  356. }
  357. break;
  358. }
  359. // seleccionar toda la tabla,
  360. $sql = "SELECT ".implode(', ', $fields)." FROM {$table}{$sqlFilter}";
  361. $query = Model::query($sql, $values);
  362. foreach ($query->fetchAll(\PDO::FETCH_ASSOC) as $row) {
  363. // para cada campo
  364. foreach ($fields as $field) {
  365. // contar palabras (ojo! hay que quitar los tags html)
  366. $count += count(explode(' ', \strip_tags($row[$field])));
  367. }
  368. }
  369. $total += $count;
  370. return $count;
  371. }
  372. /*
  373. * Devuelve el código embed de un widget de proyecto
  374. */
  375. static public function widget ($url, $type = 'project', $styles = null) {
  376. $style = (isset($styles)) ? ' style="'.$styles.'"' : '';
  377. switch ($type) {
  378. case 'fb':
  379. $code = '<div class="fb-like" data-href="'.$url.'" data-send="false" data-layout="button_count" data-width="450" data-show-faces="false"></div>';
  380. break;
  381. case 'fb-nocount':
  382. $code = '<div class="fb-like"'.$style.' data-href="'.$url.'" data-send="false" data-layout="box_count" data-width="0" data-height="0" data-show-faces="false"></div>';
  383. break;
  384. case 'wof':
  385. $code = '<iframe frameborder="0" height="100%" src="'.$url.'" width="630px" scrolling="no"></iframe>';
  386. break;
  387. case 'project':
  388. default:
  389. $code = '<iframe frameborder="0" height="480px" src="'.$url.'" width="250px" scrolling="no"></iframe>';
  390. break;
  391. }
  392. return $code;
  393. }
  394. /*
  395. * Devuelve array de urls para compartir en redes sociales
  396. */
  397. static public function shareLinks ($url, $title) {
  398. $urls = array(
  399. 'twitter' => 'http://twitter.com/home?status=' . rawurlencode($title . ': ' . $url . ' #Goteo'),
  400. 'facebook' => 'http://facebook.com/sharer.php?u=' . rawurlencode($url . '&t=' . rawurlencode($title))
  401. );
  402. return $urls;
  403. }
  404. /*
  405. * Pone el enlace a gmaps segun localidad
  406. * @TODO , ponerle el LANG
  407. */
  408. static public function GmapsLink($location)
  409. {
  410. $texto = '<a href="http://maps.google.es/maps?q='.htmlspecialchars(rawurlencode($location)).'&hl=es" target="_blank">'.htmlspecialchars($location).'</a>';
  411. return $texto;
  412. }
  413. /*
  414. * Método para formatear friendly un texto para ponerlo en la url
  415. */
  416. static public function urliza($texto)
  417. {
  418. $texto = trim(strtolower($texto));
  419. // Acentos
  420. // $texto = strtr($texto, "ÁÀÄÂáàâäÉÈËÊéèêëÍÌÏÎíìîïÓÒÖÔóòôöÚÙÛÜúùûüÇçÑñ", "aaaaaaaaeeeeeeeeiiiiiiiioooooooouuuuuuuuccnn");
  421. $table = array(
  422. 'Š'=>'S', 'š'=>'s', 'Đ'=>'Dj', 'đ'=>'dj', 'Ž'=>'Z', 'ž'=>'z', 'Č'=>'C', 'č'=>'c', 'Ć'=>'C', 'ć'=>'c',
  423. 'À'=>'A', 'Á'=>'A', 'Â'=>'A', 'Ã'=>'A', 'Ä'=>'A', 'Å'=>'A', 'Æ'=>'A', 'Ç'=>'C', 'È'=>'E', 'É'=>'E',
  424. 'Ê'=>'E', 'Ë'=>'E', 'Ì'=>'I', 'Í'=>'I', 'Î'=>'I', 'Ï'=>'I', 'Ñ'=>'N', 'Ò'=>'O', 'Ó'=>'O', 'Ô'=>'O',
  425. 'Õ'=>'O', 'Ö'=>'O', 'Ø'=>'O', 'Ù'=>'U', 'Ú'=>'U', 'Û'=>'U', 'Ü'=>'U', 'Ý'=>'Y', 'Þ'=>'B', 'ß'=>'Ss',
  426. 'à'=>'a', 'á'=>'a', 'â'=>'a', 'ã'=>'a', 'ä'=>'a', 'å'=>'a', 'æ'=>'a', 'ç'=>'c', 'è'=>'e', 'é'=>'e',
  427. 'ê'=>'e', 'ë'=>'e', 'ì'=>'i', 'í'=>'i', 'î'=>'i', 'ï'=>'i', 'ð'=>'o', 'ñ'=>'n', 'ò'=>'o', 'ó'=>'o',
  428. 'ô'=>'o', 'õ'=>'o', 'ö'=>'o', 'ø'=>'o', 'ù'=>'u', 'ú'=>'u', 'û'=>'u', 'ý'=>'y', 'ý'=>'y', 'þ'=>'b',
  429. 'ÿ'=>'y', 'Ŕ'=>'R', 'ŕ'=>'r',
  430. );
  431. $texto = strtr($texto, $table);
  432. // Separadores
  433. $texto = preg_replace("/[\s\,\;\_\/\-]+/i", "-", $texto);
  434. $texto = preg_replace("/[^a-z0-9\.\-\+]/", "", $texto);
  435. return $texto;
  436. }
  437. /*
  438. * Método para recortar un texto
  439. */
  440. static public function recorta ($texto, $longitud, $puntos = '...') {
  441. // Es HTML?
  442. $html = (strip_tags($texto) != $texto);
  443. $palabras_vacias = array();
  444. $separadores = array(" ",".",",",";");
  445. $palabras_vacias = array ("un", "uno", "unos", "unas", "una",
  446. "dos", "tres", "cuatro", "cinco", "seis", "siete", "ocho", "nueve", "diez",
  447. "el", "la", "los", "las", "lo",
  448. "que",
  449. "o", "y", "u", "e", "a",
  450. "ante", "bajo", "cabe", "con", "contra", "de", "desde", "hasta", "hacia", "para", "por", "según", "sin", "sobre", "tras", "durante", "mediante",
  451. );
  452. $texto = trim($texto);
  453. if (strlen($texto) <= $longitud) return $texto;
  454. $texto = substr($texto,0,$longitud);
  455. // Buscamos el último espacio
  456. $texto = substr($texto, 0, strrpos($texto, " "));
  457. // Quitamos palabras vacías
  458. $ultima = self::ultima_palabra($texto,$separadores );
  459. while ($texto != "" && (in_array($ultima,$palabras_vacias) || strlen($ultima)<=2) || ($html && $ultima{1} == "<" && substr($ultima,-1) == ">")) {
  460. $texto = substr($texto,0,strlen($texto)-strlen($ultima));
  461. while ($texto != "" && in_array(substr($texto,-1),$separadores)){
  462. $texto = substr($texto, 0, -1);
  463. }
  464. $ultima = self::ultima_palabra($texto,$separadores);
  465. }
  466. // Hemos cortado una etiqueta html?
  467. if ($html && strrpos($texto,"<") > strrpos($texto,">")) {
  468. $texto = substr($texto,0,strrpos($texto,"<"));
  469. }
  470. // Si el texto era html, cerramos las etiquetas
  471. if ($html) $texto = self::cerrar_etiquetas($texto);
  472. if ($puntos !== false) $texto .= $puntos;
  473. return $texto;
  474. }
  475. static public function ultima_palabra ($texto, $separadores = false) {
  476. $palabra = '';
  477. if ($separadores === false) $separadores = array(" ", ".", ",", ";");
  478. $i = strlen($texto) - 1;
  479. while ($i >= 0 && (!in_array(substr($texto,$i,1), $separadores))) {
  480. $palabra = substr($texto,$i,1).$palabra;
  481. $i--;
  482. }
  483. return $palabra;
  484. }
  485. static public function cerrar_etiquetas ($html) {
  486. // Ponemos todos los tags abiertos en un array
  487. preg_match_all("#<([a-z]+)( .*)?(?!/)>#iU", $html, $res);
  488. $abiertas = $res[1];
  489. // Ponemos todos los tags cerrados en un array
  490. preg_match_all("#</([a-z]+)>#iU", $html, $res);
  491. $cerradas = $res[1];
  492. // Obtenemos el array de etiquetas no cerradas
  493. if (count($cerradas) == count($abiertas)) {
  494. // *Suponemos* que todas las etiquetas están cerradas
  495. return $html;
  496. }
  497. $abiertas = array_reverse($abiertas);
  498. // Cerramos
  499. for ($i = 0;$i < count($abiertas);$i++) {
  500. if (!in_array($abiertas[$i],$cerradas)){
  501. $html .= "</".$abiertas[$i].">";
  502. } else {
  503. unset($cerradas[array_search($abiertas[$i],$cerradas)]);
  504. }
  505. }
  506. return $html;
  507. }
  508. /*
  509. * Método para aplicar saltos de linea y poner links en las url
  510. * ¿¡Como se puede ser tan guay!?
  511. * http://www.kwi.dk/projects/php/UrlLinker/
  512. * -------------------------------------------------------------------------------
  513. * UrlLinker - facilitates turning plaintext URLs into HTML links.
  514. *
  515. * Author: Søren Løvborg
  516. *
  517. * To the extent possible under law, Søren Løvborg has waived all copyright
  518. * and related or neighboring rights to UrlLinker.
  519. * http://creativecommons.org/publicdomain/zero/1.0/
  520. * -------------------------------------------------------------------------------
  521. */
  522. static public function urlink($text)
  523. {
  524. /*
  525. * Regular expression bits used by htmlEscapeAndLinkUrls() to match URLs.
  526. */
  527. $rexProtocol = '(https?://)?';
  528. $rexDomain = '((?:[-a-zA-Z0-9]{1,63}\.)+[-a-zA-Z0-9]{2,63}|(?:[0-9]{1,3}\.){3}[0-9]{1,3})';
  529. $rexPort = '(:[0-9]{1,5})?';
  530. $rexPath = '(/[!$-/0-9:;=@_\':;!a-zA-Z\x7f-\xff]*?)?';
  531. $rexQuery = '(\?[!$-/0-9:;=@_\':;!a-zA-Z\x7f-\xff]+?)?';
  532. $rexFragment = '(#[!$-/0-9:;=@_\':;!a-zA-Z\x7f-\xff]+?)?';
  533. $rexUrlLinker = "{\\b$rexProtocol$rexDomain$rexPort$rexPath$rexQuery$rexFragment(?=[?.!,;:\)\"]?(,|\s|\)|$))}";
  534. /**
  535. * $validTlds is an associative array mapping valid TLDs to the value true.
  536. * Since the set of valid TLDs is not static, this array should be updated
  537. * from time to time.
  538. *
  539. * List source: http://data.iana.org/TLD/tlds-alpha-by-domain.txt
  540. * Last updated: 2010-09-04
  541. */
  542. $validTlds = array_fill_keys(explode(" ", ".ac .ad .ae .aero .af .ag .ai .al .am .an .ao .aq .ar .arpa .as .asia .at .au .aw .ax .az .ba .bb .bd .be .bf .bg .bh .bi .biz .bj .bm .bn .bo .br .bs .bt .bv .bw .by .bz .ca .cat .cc .cd .cf .cg .ch .ci .ck .cl .cm .cn .co .com .coop .cr .cu .cv .cx .cy .cz .de .dj .dk .dm .do .dz .ec .edu .ee .eg .er .es .et .eu .fi .fj .fk .fm .fo .fr .ga .gb .gd .ge .gf .gg .gh .gi .gl .gm .gn .gov .gp .gq .gr .gs .gt .gu .gw .gy .hk .hm .hn .hr .ht .hu .id .ie .il .im .in .info .int .io .iq .ir .is .it .je .jm .jo .jobs .jp .ke .kg .kh .ki .km .kn .kp .kr .kw .ky .kz .la .lb .lc .li .lk .lr .ls .lt .lu .lv .ly .ma .mc .md .me .mg .mh .mil .mk .ml .mm .mn .mo .mobi .mp .mq .mr .ms .mt .mu .museum .mv .mw .mx .my .mz .na .name .nc .ne .net .nf .ng .ni .nl .no .np .nr .nu .nz .om .org .pa .pe .pf .pg .ph .pk .pl .pm .pn .pr .pro .ps .pt .pw .py .qa .re .ro .rs .ru .rw .sa .sb .sc .sd .se .sg .sh .si .sj .sk .sl .sm .sn .so .sr .st .su .sv .sy .sz .tc .td .tel .tf .tg .th .tj .tk .tl .tm .tn .to .tp .tr .travel .tt .tv .tw .tz .ua .ug .uk .us .uy .uz .va .vc .ve .vg .vi .vn .vu .wf .ws .xn--0zwm56d .xn--11b5bs3a9aj6g .xn--80akhbyknj4f .xn--9t4b11yi5a .xn--deba0ad .xn--fiqs8s .xn--fiqz9s .xn--fzc2c9e2c .xn--g6w251d .xn--hgbk6aj7f53bba .xn--hlcj6aya9esc7a .xn--j6w193g .xn--jxalpdlp .xn--kgbechtv .xn--kprw13d .xn--kpry57d .xn--mgbaam7a8h .xn--mgbayh7gpa .xn--mgberp4a5d4ar .xn--o3cw4h .xn--p1ai .xn--pgbs0dh .xn--wgbh1c .xn--xkc2al3hye2a .xn--ygbi2ammx .xn--zckzah .ye .yt .za .zm .zw"), true);
  543. /**
  544. * Transforms plain text into valid HTML, escaping special characters and
  545. * turning URLs into links.
  546. */
  547. $result = "";
  548. $position = 0;
  549. while (preg_match($rexUrlLinker, $text, $match, PREG_OFFSET_CAPTURE, $position))
  550. {
  551. list($url, $urlPosition) = $match[0];
  552. // Add the text leading up to the URL.
  553. $result .= htmlspecialchars(substr($text, $position, $urlPosition - $position));
  554. $domain = $match[2][0];
  555. $port = $match[3][0];
  556. $path = $match[4][0];
  557. // Check that the TLD is valid (or that $domain is an IP address).
  558. // Quitamos esto para que no confunda los millares con ips
  559. //preg_match('{^\.[0-9]{1,3}$}', $tld) ||
  560. $tld = strtolower(strrchr($domain, '.'));
  561. if (isset($validTlds[$tld]))
  562. {
  563. // Prepend http:// if no protocol specified
  564. $completeUrl = $match[1][0] ? $url : "http://$url";
  565. // Add the hyperlink.
  566. $result .= '<a href="' . htmlspecialchars($completeUrl) . '" target="_blank">'
  567. . htmlspecialchars("$domain$port$path")
  568. . '</a>';
  569. }
  570. else
  571. {
  572. // Not a valid URL.
  573. $result .= htmlspecialchars($url);
  574. }
  575. // Continue text parsing from after the URL.
  576. $position = $urlPosition + strlen($url);
  577. }
  578. // Add the remainder of the text.
  579. $result .= htmlspecialchars(substr($text, $position));
  580. return $result;
  581. }
  582. /*
  583. * Método para ocultar parámetros de una url
  584. */
  585. public static function cutUrlParams($url) {
  586. return $url = preg_replace('#/.+#', '', preg_replace('#http|s?://#', '', $url));
  587. }
  588. }
  589. }