commit d083259ee9703ff54b63913d8db2a23ffe814744 Author: Jordan Date: Sat Feb 21 21:13:57 2026 +0000 Initial commit: plantilla base PHP para webs Acai CMS diff --git a/.docker/Dockerfile b/.docker/Dockerfile new file mode 100644 index 0000000..382c5a6 --- /dev/null +++ b/.docker/Dockerfile @@ -0,0 +1,36 @@ +FROM php:8.2-apache + +# Instalar dependencias para GD +RUN apt-get update && apt-get install -y \ + libfreetype6-dev \ + libjpeg62-turbo-dev \ + libpng-dev \ + libwebp-dev \ + libzip-dev \ + && rm -rf /var/lib/apt/lists/* + +# Configurar y instalar extensiones PHP +RUN docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \ + && docker-php-ext-install -j$(nproc) gd mysqli pdo pdo_mysql zip + +# Instalar extension Redis +RUN pecl install redis && docker-php-ext-enable redis + +# short_open_tag +RUN echo "short_open_tag = On" > /usr/local/etc/php/conf.d/short-tags.ini + +# Modulos Apache +RUN a2enmod rewrite headers deflate expires + +# AllowOverride All para .htaccess +RUN sed -i '//,/<\/Directory>/ s/AllowOverride None/AllowOverride All/' /etc/apache2/apache2.conf && \ + sed -i '//,/<\/Directory>/ s/Options Indexes FollowSymLinks/Options Indexes FollowSymLinks/' /etc/apache2/apache2.conf + +# ServerName +RUN echo "ServerName localhost" >> /etc/apache2/apache2.conf + +# Cliente MariaDB para importar SQL +RUN apt-get update && apt-get install -y mariadb-client && rm -rf /var/lib/apt/lists/* + +WORKDIR /var/www/html +EXPOSE 80 diff --git a/.docker/docker-compose.yml b/.docker/docker-compose.yml new file mode 100644 index 0000000..5b9a664 --- /dev/null +++ b/.docker/docker-compose.yml @@ -0,0 +1,64 @@ +services: + web: + build: . + container_name: dw-quantumasis-web-completa-web + labels: + docker-web: "true" + docker-web-project: "quantumasis-web-completa" + ports: + - "8080:80" + volumes: + - /Users/jordandiaz/Documents/GitHub/quantumasis-web-completa:/var/www/html + - ./init.sh:/docker-entrypoint-init.d/init.sh + - ./quantu_web2134-209-184-114-2-2619-49.sql:/docker-entrypoint-init.d/quantu_web2134-209-184-114-2-2619-49.sql + environment: + - DB_SERVER=${DB_SERVER} + - DB_DATABASE=${DB_DATABASE} + - DB_USERNAME=${DB_USERNAME} + - DB_PASSWORD=${DB_PASSWORD} + depends_on: + db: + condition: service_healthy + command: > + bash -c "chmod +x /docker-entrypoint-init.d/init.sh && + /docker-entrypoint-init.d/init.sh && + apache2-foreground" + + db: + image: mariadb:10.11 + container_name: dw-quantumasis-web-completa-db + labels: + docker-web: "true" + docker-web-project: "quantumasis-web-completa" + environment: + - MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD} + - MYSQL_DATABASE=${DB_DATABASE} + - MYSQL_USER=${DB_USERNAME} + - MYSQL_PASSWORD=${DB_PASSWORD} + volumes: + - db_data:/var/lib/mysql + ports: + - "33060:3306" + healthcheck: + test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] + interval: 10s + timeout: 5s + retries: 5 + + redis: + image: redis:7-alpine + container_name: dw-quantumasis-web-completa-redis + labels: + docker-web: "true" + docker-web-project: "quantumasis-web-completa" + restart: unless-stopped + network_mode: "service:web" + depends_on: + - web + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 3 +volumes: + db_data: diff --git a/.docker/init.sh b/.docker/init.sh new file mode 100755 index 0000000..97ec9d5 --- /dev/null +++ b/.docker/init.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +echo "Esperando a que la base de datos este lista..." +sleep 5 + +TABLE_COUNT=$(mysql -h"$DB_SERVER" -u"$DB_USERNAME" -p"$DB_PASSWORD" "$DB_DATABASE" --skip-ssl -e "SHOW TABLES;" 2>/dev/null | wc -l) + +if [ "$TABLE_COUNT" -le 1 ]; then + SQL_FILE=$(find /docker-entrypoint-init.d/ -name "*.sql" -type f 2>/dev/null | head -1) + if [ -n "$SQL_FILE" ]; then + echo "Importando base de datos desde $SQL_FILE..." + mysql -h"$DB_SERVER" -u"$DB_USERNAME" -p"$DB_PASSWORD" "$DB_DATABASE" --skip-ssl < "$SQL_FILE" + echo "Base de datos importada correctamente." + else + echo "Advertencia: No se encontro archivo .sql para importar" + fi +else + echo "La base de datos ya contiene $TABLE_COUNT tablas. Omitiendo importacion." +fi + +echo "Inicializacion completada." diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b980197 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +cms/uploads/ +cms/data/cache/ +.DS_Store diff --git a/.htaccess b/.htaccess new file mode 100755 index 0000000..533077a --- /dev/null +++ b/.htaccess @@ -0,0 +1,272 @@ + + AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript application/json + + + + + Header set X-XSS-Protection "1; mode=block" + Header set X-Content-Type-Options nosniff + Header set Access-Control-Allow-Origin "*" + Header set Access-Control-Allow-Headers "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With, X-Acai-Token" + + + + + + ExpiresActive Off + + + FileETag None + Header unset ETag + Header unset Pragma + Header unset Cache-Control + Header unset Last-Modified + Header set Pragma "no-cache" + Header set Cache-Control "max-age=0, no-cache, no-store, must-revalidate" + Header set Expires "jue, 1 Jan 1970 00:00:00 GMT" + + + +#php_flag opcache.enable Off + +ExpiresActive On +ExpiresByType image/jpg "access 1 year" +ExpiresByType image/jpeg "access 1 year" +ExpiresByType image/gif "access 1 year" +ExpiresByType image/png "access 1 year" +ExpiresByType image/webp "access 1 year" +ExpiresByType image/svg+xml "access 1 year" +ExpiresByType font/opentype "access 1 year" +ExpiresByType font/ttf "access 1 year" +ExpiresByType text/css "access 1 year" +ExpiresByType application/pdf "access 1 month" +ExpiresByType application/javascript "access 1 year" +ExpiresByType application/x-javascript "access 1 year" +ExpiresByType application/x-shockwave-flash "access 1 year" +ExpiresByType image/x-icon "access 1 year" +ExpiresDefault "access 2 days" + + + AddOutputFilterByType DEFLATE "application/atom+xml" \ + "application/javascript" \ + "application/json" \ + "application/ld+json" \ + "application/manifest+json" \ + "application/rdf+xml" \ + "application/rss+xml" \ + "application/schema+json" \ + "application/vnd.geo+json" \ + "application/vnd.ms-fontobject" \ + "application/x-font-ttf" \ + "application/x-javascript" \ + "application/x-web-app-manifest+json" \ + "application/xhtml+xml" \ + "application/xml" \ + "font/eot" \ + "font/opentype" \ + "image/bmp" \ + "image/svg+xml" \ + "image/vnd.microsoft.icon" \ + "image/x-icon" \ + "text/cache-manifest" \ + "text/css" \ + "text/html" \ + "text/javascript" \ + "text/plain" \ + "text/vcard" \ + "text/vnd.rim.location.xloc" \ + "text/vtt" \ + "text/x-component" \ + "text/x-cross-domain-policy" \ + "text/xml" + + + +# Use HTTP Strict Transport Security to force client to use secure connections only +Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" + + + Order allow,deny + Deny from all + + + +Options -Indexes +Options +FollowSymLinks +Options -MultiViews + +DirectoryIndex index.php + +ErrorDocument 404 /404.php +ErrorDocument 403 /403.php +ErrorDocument 400 /400.php +ErrorDocument 500 /500.php +ErrorDocument 401 /401.php + +#php_value memory_limit 256M + + +# Activar RewriteEngine + RewriteEngine on + + RewriteCond %{REQUEST_URI} /index.php + RewriteRule ^(.+[^/])$ / [R=301,L] + + RewriteCond %{REQUEST_URI} /+[^\.]+$ + RewriteRule ^(.+[^/])$ %{REQUEST_URI}/ [R=301,L] + +# Redirección a HTTPS y a WWW + + RewriteCond %{SERVER_PORT} 80 + RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L] + + RewriteCond %{HTTP_HOST} ^www.quantumasis.plandeweb.com [NC] + RewriteRule ^(.*)$ https://quantumasis.plandeweb.com/$1 [L,R=301] + + RewriteCond %{HTTP_HOST} ^www.lightbeecorp.com [NC] + RewriteRule ^(.*)$ https://lightbeecorp.com/$1 [L,R=301] + + RewriteCond %{HTTP_HOST} ^www.lightbee-corp.com [NC] + RewriteRule ^(.*)$ https://lightbee-corp.com/$1 [L,R=301] + + RewriteCond %{HTTP_HOST} ^www.opticaingenio.com [NC] + RewriteRule ^(.*)$ https://opticaingenio.com/$1 [L,R=301] + + RewriteCond %{HTTP_HOST} ^www.grancanariavv.com [NC] + RewriteRule ^(.*)$ https://grancanariavv.com/$1 [L,R=301] + + RewriteCond %{HTTP_HOST} ^www.vmcprevensys.com [NC] + RewriteRule ^(.*)$ https://vmcprevensys.com/$1 [L,R=301] + + RewriteCond %{HTTP_HOST} ^www.clorofilachef.com [NC] + RewriteRule ^(.*)$ https://clorofilachef.com/$1 [L,R=301] + + RewriteCond %{HTTP_HOST} ^www.grupoquintanas.com [NC] + RewriteRule ^(.*)$ https://grupoquintanas.com/$1 [L,R=301] + + RewriteCond %{HTTP_HOST} ^www.mabriba.es [NC] + RewriteRule ^(.*)$ https://mabriba.es/$1 [L,R=301] + + RewriteCond %{HTTP_HOST} ^www.nutibarainvest.com [NC] + RewriteRule ^(.*)$ https://nutibarainvest.com/$1 [L,R=301] + + RewriteCond %{HTTP_HOST} ^www.seingroup.es [NC] + RewriteRule ^(.*)$ https://seingroup.es/$1 [L,R=301] + + RewriteCond %{HTTP_HOST} ^www.elalpendredefelix.com [NC] + RewriteRule ^(.*)$ https://elalpendredefelix.com/$1 [L,R=301] + + RewriteCond %{HTTP_HOST} ^www.marisagarcespsicologia.es [NC] + RewriteRule ^(.*)$ https://marisagarcespsicologia.es/$1 [L,R=301] + + RewriteCond %{HTTP_HOST} ^www.betarealty.es [NC] + RewriteRule ^(.*)$ https://betarealty.es/$1 [L,R=301] + + RewriteCond %{HTTP_HOST} ^www.comercial-yaumar.es [NC] + RewriteRule ^(.*)$ https://comercial-yaumar.es/$1 [L,R=301] + + RewriteCond %{HTTP_HOST} ^www.doblelinea.com [NC] + RewriteRule ^(.*)$ https://doblelinea.com/$1 [L,R=301] + + RewriteCond %{HTTP_HOST} ^www.vannagamma.com [NC] + RewriteRule ^(.*)$ https://vannagamma.com/$1 [L,R=301] + + RewriteCond %{HTTP_HOST} ^www.fisioterapiamarcosleon.com [NC] + RewriteRule ^(.*)$ https://fisioterapiamarcosleon.com/$1 [L,R=301] + + RewriteCond %{HTTP_HOST} ^www.clinicagaba.com [NC] + RewriteRule ^(.*)$ https://clinicagaba.com/$1 [L,R=301] + + RewriteCond %{HTTP_HOST} ^www.regufarma.com [NC] + RewriteRule ^(.*)$ https://regufarma.com/$1 [L,R=301] + + RewriteCond %{HTTP_HOST} ^www.mundoyc.com [NC] + RewriteRule ^(.*)$ https://mundoyc.com/$1 [L,R=301] + + RewriteCond %{HTTP_HOST} ^www.usegturservicios.com [NC] + RewriteRule ^(.*)$ https://usegturservicios.com/$1 [L,R=301] + + RewriteCond %{HTTP_HOST} ^www.tuyoga.es [NC] + RewriteRule ^(.*)$ https://tuyoga.es/$1 [L,R=301] + + RewriteCond %{HTTP_HOST} ^www.clinica-ramos.com [NC] + RewriteRule ^(.*)$ https://clinica-ramos.com/$1 [L,R=301] + + RewriteCond %{HTTP_HOST} ^www.terapeutaocupasionada.com [NC] + RewriteRule ^(.*)$ https://terapeutaocupasionada.com/$1 [L,R=301] + + RewriteCond %{HTTP_HOST} ^www.pilarquijadacomunicacion.com [NC] + RewriteRule ^(.*)$ https://pilarquijadacomunicacion.com/$1 [L,R=301] + + # RewriteCond %{HTTP_HOST} ^www.enredhadas.com [NC] + # RewriteRule ^(.*)$ https://enredhadas.com/$1 [L,R=301] + + RewriteCond %{HTTP_HOST} ^www.bungalowselpalmital.es [NC] + RewriteRule ^(.*)$ https://bungalowselpalmital.es/$1 [L,R=301] + + RewriteCond %{HTTP_HOST} ^www.barbaraleebtraducciones.com [NC] + RewriteRule ^(.*)$ https://barbaraleebtraducciones.com/$1 [L,R=301] + +# FAVICON + RewriteRule ^favicon.ico$ /template/estandar/icons/favicon.ico [L] + +# SITEMAP Y ROBOTS + RewriteRule ^sitemap\.xml /cms/sitemap.php [L] + RewriteRule ^robots\.txt /cms/robots.php [L] + +# IDIOMAS + #RewriteRule ^es/(.*)$ /$1 [L] + +# BLUR IMAGENES + RewriteRule ^blur/([a-zA-Z0-9-_./]*)(.*)$ blur.php?imagen=$1 [L] + +# APARTADOS + RewriteRule ^apartados/([a-zA-Z0-9-_]*)/([0-9]+)\.html(.*)$ apartados.php?num=$2 [L] + +# CONTENIDOS + RewriteRule ^contenidos/([a-zA-Z0-9-_]*)/([0-9]+)\.html(.*)$ contenidos.php?familia=$2 [L] + RewriteRule ^contenidos/([a-zA-Z0-9-_]*)/([a-zA-Z0-9-_]*)/([0-9]+)/([0-9]+)\.html(.*)$ contenidos.php?familia=$3&num=$4 [L] + +# OTROS CONTENIDOS + RewriteRule ^otros-contenidos/([a-zA-Z0-9-_]*)/([0-9]+)\.html(.*)$ otros_contenidos.php?num=$2 [L] + +# CONTACTO + RewriteRule ^contacto\.html(.*)$ contacto.php [L] + +# CATEGORIAS + RewriteRule ^categorias/([a-zA-Z0-9-_]*)/([0-9]+)\.html(.*)$ productos.php?categoria=$2 + +# PRODUCTOS + RewriteRule ^productos/([a-zA-Z0-9-_]*)/([0-9]+)\.html(.*)$ productos.php?num=$2 + +# SERVICE WORKER + RewriteRule ^sw.js$ lib/sw/sw.php [L] + RewriteRule ^manifest.json$ lib/sw/manifest.php [L] + RewriteRule ^template/estandar/js/sw-controller.js$ lib/sw/sw-controller.php [L] + RewriteRule ^(.+)-hsh([A-Za-z0-9]+).(.+)$ $1.$3 [L] + RewriteRule ^custom-builder-style.css cms/lib/plugins/builder_saas/replace_code.php?getStyle=1 [L] + RewriteRule ^custom-builder-javascript.js cms/lib/plugins/builder_saas/replace_code.php?getJavascript=1 [L] + +# CSS y JS + RewriteRule ^template/estandar/css/main.css$ lib/recursos.php?fileType=css [L] + RewriteRule ^template/estandar/js/main.js$ lib/recursos.php?fileType=js [L] + + +############## +# SLUG # +############## + + #RewriteBase /dashboard-client/ + #RewriteRule ^index\.html$ - [L] + #RewriteCond %{REQUEST_FILENAME} !-f + #RewriteCond %{REQUEST_FILENAME} !-d + #RewriteRule . /template/estandar/modulos/cuentadashboard_38o4db/index.html [L] + +# Permitir acceso directo a la carpeta 2fa y sus archivos +RewriteCond %{REQUEST_URI} ^/2fa(/.*)?$ +RewriteRule ^ - [L] + +# ENLACES PERMANENTES +RewriteRule ^([a-zA-Z0-9-\/_]*)$ slug.php?enlace=$1&%{QUERY_STRING}&tipo=barra [L] + + diff --git a/.user.ini b/.user.ini new file mode 100755 index 0000000..f90f906 --- /dev/null +++ b/.user.ini @@ -0,0 +1,2 @@ +#opcache.enable = On +zlib.output_compression = 1 diff --git a/README.md b/README.md new file mode 100644 index 0000000..a51d969 --- /dev/null +++ b/README.md @@ -0,0 +1,35 @@ +# acai-vscode-webbase + +Plantilla base PHP para webs locales de desarrollo con Acai CMS. + +## Descripcion + +Este repositorio contiene la estructura base que se usa al crear nuevas webs locales. Incluye los archivos PHP necesarios, configuracion de Apache, y la estructura de directorios del CMS. + +## Estructura + +``` +. +├── index.php # Punto de entrada +├── header.php # Encabezado +├── footer.php # Pie de pagina +├── funciones.php # Funciones globales +├── slug.php # Gestion de URLs +├── sesion.php # Gestion de sesiones +├── apartados.php # Gestion de apartados +├── captcha.php # Verificacion CAPTCHA +├── .htaccess # Config Apache +├── .user.ini # Config PHP +├── .docker/ # Config Docker (Dockerfile, etc.) +├── cms/ # Core del CMS +│ ├── lib/ # Clases, handlers, plugins +│ ├── data/ # Schemas y cache +│ └── uploads/ # Archivos subidos +├── hooks/ # Hooks PHP del proyecto +├── template/ # Plantillas y modulos +└── cgi-bin/ # Scripts CGI +``` + +## Relacion con el servidor + +El servidor (`acai-vscode-server`) referencia este directorio como `WEB_BASE_DIR` para copiar la estructura base al crear nuevas webs locales. Se configura en `~/.docker-web-gui/config.json` como `web_base_dir`. diff --git a/apartados.php b/apartados.php new file mode 100755 index 0000000..7f1899f --- /dev/null +++ b/apartados.php @@ -0,0 +1,35 @@ + '
404
', + "content" => '
'.t_var("La Página solicitada no existe. Disculpe las molestias")."

".t_var("Ir a inicio")."
" + ]; + echo tpl("apartados",array("apartado" => $apartado)); + include "footer.php"; + die(); +} +include("header.php"); + +$portada = CocoDB::get("portada","","",1);$portada = @$portada[0]; + +$config_apartados = array( + 'portada' => @$portada, + 'apartado' => $apartado + ); +echo tpl('apartados',$config_apartados); + +include("footer.php"); +?> diff --git a/captcha.php b/captcha.php new file mode 100755 index 0000000..35b1bad --- /dev/null +++ b/captcha.php @@ -0,0 +1,55 @@ + + diff --git a/cgi-bin/.htaccess b/cgi-bin/.htaccess new file mode 100755 index 0000000..18e19f2 --- /dev/null +++ b/cgi-bin/.htaccess @@ -0,0 +1,2 @@ +Options -Indexes +ExecCGI +AddHandler cgi-script .cgi .pl diff --git a/cms/lib/classes/CocoDB-copia.php b/cms/lib/classes/CocoDB-copia.php new file mode 100755 index 0000000..c420075 --- /dev/null +++ b/cms/lib/classes/CocoDB-copia.php @@ -0,0 +1,380 @@ + $uploadColumn){ + foreach($uploadColumn as $keyCol => $urlPath){ + self::insertRecords("uploads",[ + "urlPath" => $urlPath, + "filePath" => realpath(__DIR__."/../../../".$urlPath), + "fieldName" => $keyColumn, + "recordNum" => $lastSaved, + "tableName" => $table, + "createdTime" => date("Y-m-d H:i:s"), + "order" => time() + $keyCol, + "width" => 640, + "height" => 480 + ],[],["ignoreSchema" => true]); + } + } + endforeach; + + + if (@$preSave) { + $query = "UPDATE {$prefix}uploads " + . " SET recordNum = LAST_INSERT_ID(), preSaveTempId = '' " + . " WHERE tableName = '".mysql_real_escape_string($table)."' AND " + . " preSaveTempId = '".mysql_real_escape_string($options["preSaveTempId"])."'"; + + mysql_query($query) or die("MySQL Error: ". htmlspecialchars(mysql_error()) . "\n"); + + $query = "UPDATE {$prefix}traducciones " + . " SET recordNum = LAST_INSERT_ID(), preSaveTempId = '' " + . " WHERE tableName = '".mysql_real_escape_string($table)."' AND " + . " preSaveTempId = '".mysql_real_escape_string($options["preSaveTempId"])."'"; + mysql_query($query) or die("MySQL Error: ". htmlspecialchars(mysql_error()) . "\n"); + } + + + if (@$options['return_last_id']) { + return $lastSaved; + } + + return $result; + } + + /** + * Elimina registros de una tabla + * @destacar + * @category DB + * @param table: Tabla de la que vamos a eliminar registros + * @param where: Array asociativo que recoge campo => valor, operador, or + * @return boolean que indica si se pudo ejecutar la consulta + */ + static function deleteRecords($table, $where, $options = []) { + list($_, $_, $prefix) = self::parse_options($options); + + $where = self::parse_where($where, $table); + if (!@$where) return false; + + $q = mysql_query("DELETE FROM $prefix$table WHERE $where"); + if (!$q) return false; + return true; + } + + /** + * Actualiza registros en una tabla + * @destacar + * @category DB + * @param table: Tabla de inserción + * @param records: Lista de registros a insertar + * @param functions: Array asociativo con funciones a aplicar a cada key + * @param options: Lista de opciones posibles que pasarle al método + * @return Número de registros insertados + */ + static function updateRecords($table, $records, $where, $functions = [], $options = []) { + if (!isset($records[0])) { + $records = [$records]; + } + + list($ignoreFields, $ignoreSchema, $prefix) = self::parse_options($options); + + if (!$ignoreSchema) { + $schema = @loadSchema($table); + if (!@$schema) die('Error. Tabla no encontrada'); + } + $sqlBase = self::prepareBaseSQL($prefix, $table, @$schema, true); + $result = 0; + foreach ($records as $record): + $record = self::unsetKeys($record, $ignoreFields); + // Está comentado, pero no se si hace falta, si se descomenta se rompe el guardar del Builder (Plugin) + // if (@$schema['menuType'] == 'category' && !isset($record['parentNum'])) { + // continue; + // } + self::insertOrUpdate($record, $sqlBase, $result, $where, $prefix.$table, $functions, $ignoreSchema, @$schema); + endforeach; + + return $result; + } + + + /** + * Hace el insert o update de un único registro + * @param record: Registro con el que vamos a operar + * @param sqlBase: SQL Base + * @param result: Número de registros con los que hemos operado. In-out + * @param where: Where de la operación (solo si es un UPDATE) + * @param table: Tabla de la operación + * @param functions: Lista de funciones con las que podemos parsear un determinado valor + * @param ignoreSchema: Boolean que indica si vamos a ignorar el Schema o no + * @param schema: Schema de la tabla + */ + private static function insertOrUpdate($record, $sqlBase, &$result, $where = null, $table = null, $functions = null, $ignoreSchema = false, $schema = null) { + $sql = $sqlBase; + self::$uploadColumns = []; + foreach ($record as $key => $value): + $column_exists = self::column_exists($key, @$schema, $table); + if (!$column_exists) continue; + if (is_array($column_exists) && $column_exists["type"] === "upload"){ + if (!is_array($value)) $value = [$value]; + if (!isset(self::$uploadColumns[$key])) self::$uploadColumns[$key] = []; + foreach($value as $val){ + self::$uploadColumns[$key][] = $val; + } + continue; + } + + if (is_array($functions) && isset($functions[$key]) && is_callable($functions[$key])) { + $value = $functions[$key]($value); + } + else if (!$ignoreSchema) { + $value = self::parse_value_schema($value, $schema, $key); + } + + if ($value === null) { + continue; + // return false; + } + + if (is_array($value)) { + $value = json_encode($value); + } + + $sql .= ", `$key`='".mysql_real_escape_string($value)."'"; + endforeach; + + if (@$where) { + $where = self::parse_where($where, $table); + $sql .= " WHERE ".$where; + } + + if (mysql_query($sql)) { + $result++; + }else{ + if(class_exists('API') && class_exists('ApiError')) + API::error(new ApiError(json_encode(["error" => mysql_error(),"sql" => $sql]))); + else + die(json_encode(["error" => mysql_error(),"sql" => $sql])); + } + } + + /** + * Función que prepara el SQL base dependiendo del schema y de si es INSERT o UPDATE + * @param prefix: Prefijo de la tabla + * @param table: Tabla de la operación + * @param schema: Schema de la tabla + * @param update: Boolean que indica si vamos a actualizar o insertar + * @return sql + */ + private static function prepareBaseSQL($prefix, $table, $schema = null, $update = false) { + $operation = $update ? "UPDATE" : "INSERT INTO"; + if (@$schema) { + $d = date('Y-m-d H:i:s'); + $t = time(); + $sqlBase = "$operation $prefix$table SET updatedDate='$d'"; + if (!$update) { + $sqlBase .= ", num=NULL, createdDate='$d', createdByUserNum=1, updatedByUserNum=1"; + switch ($schema['menuType']) { + case 'category': + $sqlBase .= ", globalOrder=0, siblingOrder=0, lineage='', depth=0, breadcrumb=''"; + break; + case 'multi': + $sqlBase .= ", dragSortOrder='$t'"; + break; + default: + break; + } + } + } + else { + $sqlBase = "$operation $prefix$table SET num=".($update ? "num" : "NULL"); + } + return $sqlBase; + } + + /** + * Parsea las opciones de los métodos + * @return lista con las opciones ignoreFields, ignoreSchema y prefix + */ + private static function parse_options($options) { + global $TABLE_PREFIX; + + $ignoreFields = ['num']; + if (@$options['ignoreFields']) { + $ignoreFields = array_merge($ignoreFields, $options['ignoreField']); + } + + $ignoreSchema = @$options['ignoreSchema'] ?: false; + $prefix = isset($options["prefix"]) ? $options["prefix"] : $TABLE_PREFIX; + return [$ignoreFields, $ignoreSchema, $prefix]; + } + + /** + * Comprueba si una columna existe en una tabla + * @return boolean que indica si existe o no la columna + */ + private static function column_exists($key, $schema, $table) { + if (isset(self::$tableCache[$table]) && isset(self::$tableCache[$table][$key])) { + return self::$tableCache[$table][$key]; + } + if ($schema && isset($schema[$key])) { + return $schema[$key]; + } + + $result = mysql_query("SHOW COLUMNS FROM `$table` LIKE '$key'"); + $exists = mysql_num_rows($result) > 0; + self::cache_column($table, $key, $exists); + + return $exists; + } + + /** + * Cachea la comprobación de una columna en una tabla + */ + private static function cache_column($table, $column, $exists) { + if (!isset(self::$tableCache[$table])) { + self::$tableCache[$table] = []; + } + if (!isset(self::$tableCache[$table][$column])) { + self::$tableCache[$table][$column] = $exists; + } + } + + /** + * Parsea el valor dependiendo del tipo de campo + * @return valor parseado + */ + private static function parse_value_schema($value, $schema, $key) { + switch($schema[$key]['type']) { + case 'list': + switch ($schema[$key]['listType']) { + case 'pulldownMulti': + if (is_array($value)) { + $value = "\t".join("\t", $value)."\t"; + } + break; + default: + break; + } + break; + case 'multitext': + if (is_array($value)) { + $value = json_encode($value); + } + break; + case 'checkbox': + $value = @$value ? 1 : 0; + break; + default: + break; + } + return $value; + } + + /** + * Parsea el where pasado por parámetro + * - or: Si se envía a true usa OR como enlace en lugar de AND + * - not: Si se envía a true se usa + * - operador: LIKE, IN, != o = + * + * @param where: String o array + * @return where + */ + private static function parse_where($where, $table) { + $builtWhere = ""; + if (is_array($where)) { + foreach ($where as $key => $w): + if (is_array($w)) { + if (!isset($w["value"]) || !isset($w["column"])) return false; + $key = $w["column"]; + if (!self::column_exists($key, null, $table)) return false; + $value = $w["value"]; + $enlace = @$w["or"] ? "OR" : "AND"; + $not = @$w["not"] ? "NOT " : ""; + switch (strtoupper(@$w["operator"])) { + case "LIKE": + $value = "'".mysql_real_escape_string($value)."'"; + $operador = "LIKE"; + break; + case "IN": + if (is_array($value)) { + $value = join(", ", array_map(function($a) { + if (is_int($a)) return intval($a); + if (is_numeric($a)) return floatval($a); + return "'".mysql_real_escape_string($a)."'"; + }, $value)); + } + $value = "(".$value.")"; + $operador = "IN"; + break; + case "!=": + $value = "'".mysql_real_escape_string($value)."'"; + $operador = "!="; + break; + default: + $value = "'".mysql_real_escape_string($value)."'"; + $operador = "="; + } + + if (@$builtWhere) $builtWhere .= " $enlace "; + $builtWhere .= "`$key` $not$operador $value"; + } + else { + if (@$builtWhere) $builtWhere .= " AND "; + $builtWhere .= "`$key`='".mysql_real_escape_string($w)."'"; + } + endforeach; + } + else { + $builtWhere = $where; + } + return $builtWhere; + } + + /** + * Elimina del primer array las claves pasadas en el segundo parámetro + * @param array: Array del que vamos a eliminar las keys + * @param keys: Array que contiene las keys que queremos eliminar + * @return nuevo array + */ + private static function unsetKeys($array, $keys) { + $c = $array; + foreach ($keys as $removeKey) { + unset($c[$removeKey]); + } + return $c; + } +} \ No newline at end of file diff --git a/cms/lib/classes/CocoDB.php b/cms/lib/classes/CocoDB.php new file mode 100755 index 0000000..a0766b9 --- /dev/null +++ b/cms/lib/classes/CocoDB.php @@ -0,0 +1,1415 @@ + 'Error. Tabla no encontrada']); + } + + + $result = 0; + $lastSaved = 0; + + foreach ($records as $record) : + $sqlBase = self::prepareBaseSQL($prefix, $table, @$schema, false, $ignoreFields, $record); + $record = self::unsetKeys($record, $ignoreFields); + if (!in_array("num", @$ignoreFields) && isset($record["num"])) { + $sqlBase = str_replace("num=NULL", "num=" . intval(@$record["num"]), $sqlBase); + $record = self::unsetKeys($record, ["num"]); + } + self::insertOrUpdate($record, $sqlBase, $result, null, $prefix . $table, $functions, $ignoreSchema, @$schema, $options); + $lastSaved = mysql_insert_id(); + foreach (self::$uploadColumns as $keyColumn => $uploadColumn) { + foreach ($uploadColumn as $keyCol => $urlPath) { + if (!@$urlPath) continue; + self::insertRecords("uploads", [ + "urlPath" => $urlPath, + "filePath" => realpath(__DIR__ . "/../../../" . $urlPath), + "fieldName" => $keyColumn, + "recordNum" => $lastSaved, + "tableName" => $table, + "createdTime" => date("Y-m-d H:i:s"), + "order" => time() + $keyCol, + "width" => 640, + "height" => 480 + ], [], ["ignoreSchema" => true]); + } + } + + endforeach; + + + if (@$preSave) { + $query = "UPDATE {$prefix}uploads " + . " SET recordNum = LAST_INSERT_ID(), preSaveTempId = '' " + . " WHERE tableName = '" . mysql_real_escape_string($table) . "' AND " + . " preSaveTempId = '" . mysql_real_escape_string($options["preSaveTempId"]) . "'"; + + mysql_query($query) or self::error(["error" => "MySQL Error: " . htmlspecialchars(mysql_error()) . "\n"]); + + $query = "UPDATE {$prefix}traducciones " + . " SET recordNum = LAST_INSERT_ID(), preSaveTempId = '' " + . " WHERE tableName = '" . mysql_real_escape_string($table) . "' AND " + . " preSaveTempId = '" . mysql_real_escape_string($options["preSaveTempId"]) . "'"; + mysql_query($query) or self::error(["error" => "MySQL Error: " . htmlspecialchars(mysql_error()) . "\n"]); + } + + if (@$options['generate_category_metadata']) { + self::updateCategoryMetadata($table); + } + + if (@$options['return_last_id']) { + return $lastSaved; + } + + return $result; + } + + /** + * Elimina registros de una tabla + * @destacar + * @category DB + * @param table: Tabla de la que vamos a eliminar registros + * @param where: Array asociativo que recoge campo => valor, operador, or + * @return boolean que indica si se pudo ejecutar la consulta + */ + static function deleteRecords($table, $where, $options = []) + { + list($_, $_, $prefix) = self::parse_options($options); + + $where = self::parse_where($where, $table, $prefix); + + if (!@$where) return false; + + $sql = "DELETE FROM $prefix$table WHERE $where"; + + if (@$options['dieBeforeQuery'] && $table == "uploads") { + self::error(["info" => $sql]); + } + + $q = mysql_query($sql); + if (!$q) return false; + return true; + } + + /** + * Actualiza registros en una tabla + * @destacar + * @category DB + * @param table: Tabla de inserción + * @param records: Lista de registros a insertar + * @param functions: Array asociativo con funciones a aplicar a cada key + * @param options: Lista de opciones posibles que pasarle al método + * @return Número de registros insertados + */ + static function updateRecords($table, $records, $where, $functions = [], $options = []) + { + global $TABLE_PREFIX; + + if (!isset($records[0])) { + $records = [$records]; + } + + list($ignoreFields, $ignoreSchema, $prefix) = self::parse_options($options); + + if (!$ignoreSchema) { + $schema = @loadSchema($table); + if (!@$schema) self::error(["error" => 'Error. Tabla no encontrada']); + } + + $result = 0; + foreach ($records as $record) : + $sqlBase = self::prepareBaseSQL($prefix, $table, @$schema, true, $ignoreFields, $record); + $lastNum = @$record['num']; + $record = self::unsetKeys($record, $ignoreFields); + // Está comentado, pero no se si hace falta, si se descomenta se rompe el guardar del Builder (Plugin) + // if (@$schema['menuType'] == 'category' && !isset($record['parentNum'])) { + // continue; + // } + + self::insertOrUpdate($record, $sqlBase, $result, $where, $prefix . $table, $functions, $ignoreSchema, @$schema, $options); + + if (@$lastNum && @$options['delete_upload_nums']) { + // El Prefix se añade "por la cara" porque el show_columns peta sin el + self::deleteRecords("uploads", [ + ["column" => "num", "value" => $options['delete_upload_nums'], "operator" => "IN"] + ], ["prefix" => $TABLE_PREFIX]); + } + + foreach (self::$uploadColumns as $keyColumn => $uploadColumn) { + // El Prefix se añade "por la cara" por lo mismo que el delete_upload_nums aunque este no está probado + if (@$lastNum && @$options['delete_old_uploads']) { + self::deleteRecords("uploads", [ + "fieldName" => $keyColumn, + "recordNum" => $lastNum, + "tableName" => $table, + ], ["prefix" => $TABLE_PREFIX]); + } + + foreach ($uploadColumn as $keyCol => $urlPath) { + + if (@$lastNum && @$options['insert_new_uploads']) { + self::insertRecords("uploads", [ + "urlPath" => $urlPath, + "filePath" => realpath(__DIR__ . "/../../../" . $urlPath), + "fieldName" => $keyColumn, + "recordNum" => $lastNum, + "tableName" => $table, + "createdTime" => date("Y-m-d H:i:s"), + "order" => time() + $keyCol, + "width" => 640, + "height" => 480 + ], [], ["ignoreSchema" => true]); + } + } + } + endforeach; + + if (@$options['generate_category_metadata']) { + self::updateCategoryMetadata($table); + } + + return $result; + } + + + /** + * Hace el insert o update de un único registro + * @param record: Registro con el que vamos a operar + * @param sqlBase: SQL Base + * @param result: Número de registros con los que hemos operado. In-out + * @param where: Where de la operación (solo si es un UPDATE) + * @param table: Tabla de la operación + * @param functions: Lista de funciones con las que podemos parsear un determinado valor + * @param ignoreSchema: Boolean que indica si vamos a ignorar el Schema o no + * @param schema: Schema de la tabla + */ + public static function insertOrUpdate($record, $sqlBase, &$result, $where = null, $table = null, $functions = null, $ignoreSchema = false, $schema = null, $options = []) + { + $sql = $sqlBase; + self::$uploadColumns = []; + foreach ($record as $key => $value) : + $column_exists = self::column_exists($key, @$schema, $table); + if (!$column_exists) continue; + if (is_array($column_exists) && $column_exists["type"] === "upload") { + if (!is_array($value)) $value = [$value]; + if (!isset(self::$uploadColumns[$key])) self::$uploadColumns[$key] = []; + foreach ($value as $val) { + self::$uploadColumns[$key][] = $val; + } + continue; + } + + if (is_array($functions) && isset($functions[$key]) && is_callable($functions[$key])) { + $value = $functions[$key]($value); + } else if (!$ignoreSchema) { + $value = self::parse_value_schema($value, $schema, $key); + } + + if ($value === null) { + continue; + // return false; + } + + if (is_array($value)) { + $value = json_encode($value); + } + + $sql .= ", `$key`='" . mysql_real_escape_string($value) . "'"; + endforeach; + + if (@$where) { + $where = self::parse_where($where, $table); + $sql .= " WHERE " . $where; + } + + if (@$options['dieBeforeQuery']) + self::error(["info" => $sql]); + + if (mysql_query($sql)) { + $result++; + } else { + self::error(["error" => mysql_error(), "sql" => $sql]); + } + } + + public static function error($array = []) + { + if (class_exists('API') && class_exists('ApiError')) { + API::$die = true; + API::error(new ApiError(json_encode($array))); + } else { + die(json_encode($array)); + } + } + /** + * Función que prepara el SQL base dependiendo del schema y de si es INSERT o UPDATE + * @param prefix: Prefijo de la tabla + * @param table: Tabla de la operación + * @param schema: Schema de la tabla + * @param update: Boolean que indica si vamos a actualizar o insertar + * @return sql + */ + public static function prepareBaseSQL($prefix, $table, $schema = null, $update = false, $ignoreFields = [], $record = []) + { + $operation = $update ? "UPDATE" : "INSERT INTO"; + if (@$schema) { + $d = date('Y-m-d H:i:s'); + $t = time(); + + $user = 1; + if(class_exists('API')) { + $user = @Api::$user["num"] ?: 1; + } + + $keysBase = ["createdDate" => $d, "createdByUserNum" => $user]; + + + $keys = [ + "category" => ["globalOrder" => 0, "siblingOrder" => 0, "lineage" => '', "depth" => 0, "breadcrumb" => '', "parentNum" => 0], + "multi" => ["dragSortOrder" => $t] + ]; + + $sqlBase = "$operation $prefix$table SET updatedDate='$d', updatedByUserNum='$user'"; + + if (!$update) { + $sqlBase .= ", num=NULL"; + + foreach ($keysBase as $keyBase => $valueBase) { + if (isset($record[$keyBase])) continue; + if (is_string($valueBase)) { + $sqlBase .= ", `" . $keyBase . "`='" . $valueBase . "'"; + } else { + $sqlBase .= ", `" . $keyBase . "`=" . ($valueBase ?: 'NULL'); + } + } + + if (@$keys[$schema['menuType']]) { + foreach ($keys[$schema['menuType']] as $key => $value) { + if (isset($record[$key])) continue; + if (is_string($value)) { + $sqlBase .= ", `" . $key . "`='" . $value . "'"; + } else { + $sqlBase .= ", `" . $key . "`=" . $value; + } + } + } + } + } else { + $sqlBase = "$operation $prefix$table SET num=" . ($update ? "num" : "NULL"); + } + return $sqlBase; + } + + /** + * Parsea las opciones de los métodos + * @return lista con las opciones ignoreFields, ignoreSchema y prefix + */ + public static function parse_options($options) + { + global $TABLE_PREFIX; + + $ignoreFields = @$options["forceNum"] ? [] : ['num']; + + if (@$options['ignoreFields']) { + $ignoreFields = array_merge($ignoreFields, $options['ignoreField']); + } + + $ignoreSchema = @$options['ignoreSchema'] ?: false; + $prefix = isset($options["prefix"]) ? $options["prefix"] : $TABLE_PREFIX; + return [$ignoreFields, $ignoreSchema, $prefix]; + } + + /** + * Comprueba si una columna existe en una tabla + * @return boolean que indica si existe o no la columna + */ + public static function column_exists($key, $schema, $table, $prefix = "") + { + global $TABLE_PREFIX; + + if (isset(self::$tableCache[$table]) && isset(self::$tableCache[$table][$key])) { + return self::$tableCache[$table][$key]; + } + if ($schema && isset($schema[$key])) { + return $schema[$key]; + } + + $result = mysql_query("SHOW COLUMNS FROM `$prefix$table` LIKE '$key'"); + + $exists = mysql_num_rows($result) > 0; + self::cache_column($table, $key, $exists); + + return $exists; + } + + /** + * Cachea la comprobación de una columna en una tabla + */ + public static function cache_column($table, $column, $exists) + { + if (!isset(self::$tableCache[$table])) { + self::$tableCache[$table] = []; + } + if (!isset(self::$tableCache[$table][$column])) { + self::$tableCache[$table][$column] = $exists; + } + } + + /** + * Parsea el valor dependiendo del tipo de campo + * @return valor parseado + */ + public static function parse_value_schema($value, $schema, $key) + { + if (isset($schema[$key]) && isset($schema[$key]['type'])) { + switch ($schema[$key]['type']) { + case 'list': + switch ($schema[$key]['listType']) { + case 'pulldownMulti': + if (is_array($value)) { + $value = "\t" . join("\t", $value) . "\t"; + } + break; + default: + break; + } + break; + case 'multitext': + if (is_array($value)) { + $value = json_encode($value); + } + break; + case 'checkbox': + $value = @$value ? 1 : 0; + break; + default: + break; + } + } + return $value; + } + + /** + * Parsea el where pasado por parámetro + * - or: Si se envía a true usa OR como enlace en lugar de AND + * - not: Si se envía a true se usa + * - operador: LIKE, IN, != o = + * - raw_key: evita que ponga la comillas en la key y que no compruebe si existe + * + * @param where: String o array + * @return where + */ + public static function parse_where($where, $table, $prefix = "") + { + $builtWhere = ""; + $add_parenthesis = false; + if (is_array($where)) { + + foreach ($where as $key => $w) : + if (is_array($w)) { + if (!isset($w["value"]) || !isset($w["column"])) return false; + if (!isset($w["operator"])) $w["operator"] = '='; + $key = $w["column"]; + + if ($table && !@$w["raw_key"] && !self::column_exists($key, null, $table, $prefix)) return false; + + $value = $w["value"]; + $enlace = @$w["or"] ? ") OR (" : "AND"; + if (@$w["or"]) $add_parenthesis = true; + $not = @$w["not"] ? "NOT " : ""; + + switch (strtoupper(@$w["operator"])) { + case "LIKE": + // Cambiado para DAXAuto... aunque no recuerdo el motivo exacto. + // $value = "'".mysql_real_escape_string($value)."'"; + $value = "'" . str_replace('\\\\t', '\\t', mysql_real_escape_string($value)) . "'"; + $operador = "LIKE"; + break; + case "IS NULL": + $value = "NULL"; + if ($not == '') { + $operador = "IS"; + } else { + $operador = "IS NOT"; + $not = ''; + } + break; + case "IN": + if (is_array($value)) { + $value = join(", ", array_map(function ($a) { + if (is_int($a)) return intval($a); + if (is_numeric($a)) return floatval($a); + return "'" . mysql_real_escape_string($a) . "'"; + }, $value)); + } + $value = "(" . $value . ")"; + $operador = "IN"; + break; + case "!=": + $value = "'" . mysql_real_escape_string($value) . "'"; + $operador = "!="; + break; + default: + $value = "'" . mysql_real_escape_string($value) . "'"; + if (in_array(strtoupper($w["operator"]), ['<', '>', '<=', '>=', '='])) { + $operador = $w["operator"]; + } else { + $operador = "="; + } + } + + if (@$builtWhere) $builtWhere .= " $enlace "; + if(!@$w["raw_key"]) { + $builtWhere .= "$key $not$operador $value"; + } else { + $builtWhere .= "`$key` $not$operador $value"; + } + } else { + if (@$builtWhere) $builtWhere .= " AND "; + $builtWhere .= "`$key`='" . mysql_real_escape_string($w) . "'"; + } + endforeach; + } else { + $builtWhere = $where; + } + if ($add_parenthesis) return "(" . $builtWhere . ")"; + return $builtWhere; + } + + /** + * Elimina del primer array las claves pasadas en el segundo parámetro + * @param array: Array del que vamos a eliminar las keys + * @param keys: Array que contiene las keys que queremos eliminar + * @return nuevo array + */ + public static function unsetKeys($array, $keys) + { + $c = $array; + foreach ($keys as $removeKey) { + unset($c[$removeKey]); + } + return $c; + } + + /** + * Setea un punto de BackTrace + * @param string: string informativo + */ + static function setBacktracePoint($string) + { + self::$backTracePoint = $string; + } + /** + * Devuelve la información de TrackData ( uso en acai code ) + * @return trackData + */ + static function getTrackData() + { + return self::$trackData; + } + /** + * Setea información de track para saber por donde va toda la web ( uso en acai code ) + * @return pushed data + */ + static function setTrackData($init = false, $type = null, $id = null, $data = []) + { + if ($init) { + + + $pushedData = [ + "ip" => $_SERVER["REMOTE_ADDR"], + "timestamp" => round(floatval(microtime(true) * 1000), 4), + "totalTime" => 0, + "host" => $_SERVER["HTTP_HOST"], + "url" => $_SERVER["REQUEST_URI"], + "trackData" => [] + ]; + self::$trackData[] = $pushedData; + } else { + // if (count(self::$trackData[count(self::$trackData)-1]["trackData"])){ + // $prevTimestamp = self::$trackData[count(self::$trackData)-1]["trackData"][count(self::$trackData[count(self::$trackData)-1]["trackData"])-1]["timestamp"]; + // }else{ + // + // } + $prevTimestamp = self::$trackData[count(self::$trackData) - 1]["timestamp"]; + $pushedData = [ + "timestamp" => round((floatval(microtime(true) * 1000) - $prevTimestamp), 4), + "type" => $type, + "id" => $id, + "transferKeys" => !isset($data[0]) ? array_keys($data) : (is_array($data[0]) ? array_keys($data[0]) : ["undefined" => $data[0]]) + // "data" => $data + ]; + self::$trackData[count(self::$trackData) - 1]["trackData"][] = $pushedData; + self::$trackData[count(self::$trackData) - 1]["totalTime"] = $pushedData["timestamp"]; + $prevPercent = 0; + foreach (self::$trackData[count(self::$trackData) - 1]["trackData"] as $cont => $trackData) { + $percent = ($trackData["timestamp"] * 100) / self::$trackData[count(self::$trackData) - 1]["totalTime"]; + self::$trackData[count(self::$trackData) - 1]["trackData"][$cont]["percent"] = round($percent, 2); + self::$trackData[count(self::$trackData) - 1]["trackData"][$cont]["initPercent"] = $prevPercent; + self::$trackData[count(self::$trackData) - 1]["trackData"][$cont]["widthPercent"] = $percent - $prevPercent; + $prevPercent = $percent; + } + + /*self::$trackData[count(self::$trackData)-1]["totalTime"] += $pushedData["timestamp"]; + self::$trackData[count(self::$trackData)-1]["totalTime"] = round(self::$trackData[count(self::$trackData)-1]["totalTime"],4); + + self::$trackData[count(self::$trackData)-1]["trackData"][] = $pushedData; + + $sum = 0; + foreach( self::$trackData[count(self::$trackData)-1]["trackData"] as $cont => $trackData){ + $percent = ($trackData["timestamp"] * 100)/self::$trackData[count(self::$trackData)-1]["totalTime"]; + + self::$trackData[count(self::$trackData)-1]["trackData"][$cont]["percent"] = $sum; + $sum+=$percent; + $sum = round($sum,2); + }*/ + } + return $pushedData; + } + + /** + * Muestra la variable debug + * @return html content + */ + static function showDebug($formated = false, $index = -1) + { + if (!$formated) return json_encode(self::$debugData, JSON_PRETTY_PRINT); + if (!self::$debugData) return ""; + $result = ' + +
+ '; + foreach (self::$debugData as $cont => $debugQuery) { + if ($index > -1 && $cont != $index) continue; + $result .= '
'; + $result .= '
'; + $result .= $debugQuery["hora"] . "
"; + if (self::$backTracePoint) $result .= self::$backTracePoint . "
"; + $result .= $debugQuery["query"] . "
" . $debugQuery["time"] . " microsegundos
"; + $result .= ""; + $result .= ""; + $result .= '
'; + $result .= ''; + $result .= ''; + $result .= '
'; + } + $result .= '
'; + return $result; + } + /** + * Parsea un record dado de un resultado de busqueda en la base de datos añadiendo información extra de valor + */ + static function parseGetRecord(&$record, $firstTable, $schema, $options = [], $uploadsResult = []) + { + global $TABLE_PREFIX; + $record["tableName"] = $firstTable; + + if (@$uploadsResult) { + foreach ($schema as $fieldName => $fieldValue) { + if (!is_array($fieldValue)) continue; + if (@$fieldValue["type"] != "upload") continue; + + if (@$uploadsResult[$firstTable][$fieldName][$record['num']]) { + $record[$fieldName] = $uploadsResult[$firstTable][$fieldName][$record['num']]; + } + } + } + + foreach ($record as $recordKey => $recordValue) { + $schemaField = @$schema[$recordKey]; + if (!@$schemaField || !is_array($schemaField)) continue; + if (@$options["relations"] && is_array($options["relations"]) && !in_array($recordKey, $options["relations"])) continue; + switch (@$schemaField["type"]) { + case "list": + switch ($schemaField["optionsType"]) { + case "query": + $query = getEvalOutput($schemaField['optionsQuery']); + if (!isset(self::$queryCaches[md5($query)])) { + self::$queryCaches[md5($query)] = mysql_query_fetch_all_assoc($query); + } + preg_match('/FROM\s+(.*)\s+/', $query . " ", $matches); + $tableQuery = null; + if (@$matches[1]) $tableQuery = str_replace($TABLE_PREFIX, "", trim($matches[1])); + + $result = array_filter(self::$queryCaches[md5($query)], function ($rec) use ($recordValue) { + return $recordValue == $rec[array_keys($rec)[0]]; + }); + $result = array_merge(array_map(function ($rec) use ($tableQuery) { + if ($tableQuery) $rec["tableName"] = $tableQuery; + return $rec; + }, $result)); + $record[$recordKey . "_bd"] = $result; + break; + case "text": + $optionsText = array_filter(explode("\n", $schemaField["optionsText"])); + $optionsList = []; + foreach ($optionsText as $option) { + $sepOption = explode("|", $option); + if (!isset($sepOption[1])) $sepOption[1] = $sepOption[0]; + $optionsList[$sepOption[0]] = $sepOption[1]; + } + // Anael: evitamos que se haga un explode de los valores null (casos extraños como el blog). + $resultDatas = []; + if(@$recordValue) $resultDatas = explode("\t", $recordValue); + $record[$recordKey . "_bd"] = []; + foreach ($resultDatas as $resultData) { + if (isset($optionsList[$resultData])) { + $record[$recordKey . "_bd"][] = ["key" => $resultData, "value" => t_var($optionsList[$resultData])]; + } else { + $record[$recordKey . "_bd"][] = ["key" => $resultData, "value" => t_var($resultData)]; + } + } + break; + case "table": + + $nums = array_filter(explode("\t", mysql_real_escape_string($recordValue ?? ''))); + if (@$nums && @$options["relationsDepth"]) { + /*$hash_query_caches = 'query_all_'.$schemaField["optionsTablename"] . '_' . $options["relationsDepth"]; + if(!isset(self::$queryCaches[$hash_query_caches])) { + self::$queryCaches[$hash_query_caches] = self::get($schemaField["optionsTablename"], null, null, null, [ + "relationsDepth" => intval(@$options["relationsDepth"])-1, + "debug" => @$options["debug"], + "translates" => @$options["translates"], + "uploads" => @$options["uploads"] + ]); + } + $cache_filter_by_key = $schemaField["optionsValueField"]; + $cache_filter_by_values = $nums; + $record[$recordKey."_bd"] = array_values(array_filter(self::$queryCaches[$hash_query_caches], function($each) use($cache_filter_by_key, $cache_filter_by_values) { + return in_array($each[$cache_filter_by_key], $cache_filter_by_values); + }));*/ + // SE HA ELIMINADO ESTE SCRIPT PORQUE DUPLICABA EL TIEMPO DE RESPUESTA EN BANANA + //$record[$recordKey."_bd"] = self::get($schemaField["optionsTablename"], $schemaField["optionsValueField"]." IN ('".join("','", $nums)."')", null, null, ["relationsDepth" => intval(@$options["relationsDepth"])-1,"debug" => @$options["debug"]]); + $record[$recordKey . "_bd"] = self::get($schemaField["optionsTablename"], $schemaField["optionsValueField"] . " IN ('" . join("','", $nums) . "')", null, null, [ + "relationsDepth" => intval(@$options["relationsDepth"]) - 1, + "debug" => @$options["debug"], + "translates" => @$options["translates"], + "uploads" => @$options["uploads"] + ]); + } + break; + default: + } + break; + case "multitext": + $record[$recordKey . "_bd"] = json_decode($recordValue ?? '', true); + break; + default: + } + } + // Translates + if (@$options["translates"]) { + $idiomaActual = @$_REQUEST["idioma"]; + $_REQUEST["idioma"] = @$options["translates"]; + if (@$_REQUEST["idioma"]) { + $record = self::t_recursivo($record, null); + } + $_REQUEST["idioma"] = $idiomaActual; + } + + $record["breadcrumbField"] = @$schema["breadcrumbField"]; + // Si es parentNum ponemos valores por defecto + if (@$record["breadcrumbField"] == "parentNum") { + $record["optionsTablename"] = $record["tableName"]; + $record["optionsValueField"] = "num"; + } else if (@$record["breadcrumbField"]) { + // Si no es parentNum, ponemos los que dicte el schema + $record["optionsTablename"] = @$schema[$schema["breadcrumbField"]]["optionsTablename"]; + $record["optionsValueField"] = @$schema[$schema["breadcrumbField"]]["optionsValueField"]; + } + + // Para el campo principal (para la generación de enlaces y el breadcrumb) + if (@$record["name"]) { + $record["mainFieldBreadcrumb"] = $record["name"]; + } else if (@$record["title"]) { + $record["mainFieldBreadcrumb"] = $record["title"]; + } else if (@$record["titulo"]) { + $record["mainFieldBreadcrumb"] = $record["titulo"]; + } else if (@$record["nombre"]) { + $record["mainFieldBreadcrumb"] = $record["nombre"]; + } else { + foreach ($schema as $key => $value) : + if (!is_array($value)) continue; + if (@$value["type"] == "textfield" && $key != "enlace") { + $record["mainFieldBreadcrumb"] = $record[$key]; + break; + } + endforeach; + } + } + /** + * Obtiene los uploads de una tabla y los cachea + * @return un array con todos los uploads + */ + static function getUploadsResults($options) + { + global $TABLE_PREFIX; + $pathCacheUploads = __DIR__ . "/../../../cache/"; + if (!file_exists($pathCacheUploads)) { + mkdir($pathCacheUploads); + } + $fileName = $pathCacheUploads . "uploads-" . date("Y-m-d", time()) . "-" . $_SERVER['HTTP_HOST'] . ".json"; + if (!file_exists($fileName)) { + $fields_to_select = "num, `order`, tableName, fieldName, recordNum, filePath, urlPath, info1, info2, info3, info4, info5, alt"; + $uploads = mysql_query_fetch_all_assoc("SELECT $fields_to_select FROM " . $TABLE_PREFIX . "uploads ORDER BY `order` ASC"); + $uploadsResult = []; + foreach ($uploads as $upload) { + $table = $upload['tableName']; + $field = $upload['fieldName']; + $num = $upload['recordNum']; + if (!isset($uploadsResult[$table])) $uploadsResult[$table] = []; + if (!isset($uploadsResult[$table][$field])) $uploadsResult[$table][$field] = []; + if (!isset($uploadsResult[$table][$field][$num])) $uploadsResult[$table][$field][$num] = []; + $uploadsResult[$table][$field][$num][] = $upload; + } + file_put_contents($fileName, json_encode($uploadsResult)); + } else { + if ($options['useAbsoluteUrls']) { + $uploadsResult = json_decode(str_replace('"urlPath":"\/cms', '"urlPath":"https:\/\/' . $_SERVER['HTTP_HOST'] . '\/cms', file_get_contents($fileName)), true); + } else { + $uploadsResult = json_decode(file_get_contents($fileName), true); + } + // $uploadsResult = isset($uploadsResult[$tableName]) ? $uploadsResult[$tableName] : []; + } + return $uploadsResult; + } + /** + * Obtiene los uploads consultándolos a base de datos registro por registro + * @return void + */ + static function getUploadsResultsFromRecord(&$record, $firstTable, $schema, $options) + { + global $TABLE_PREFIX; + if (!is_array($schema)) return; + $record["tableName"] = $firstTable; + $fields_to_select = "num, `order`, tableName, fieldName, recordNum, filePath, urlPath, info1, info2, info3, info4, info5, alt"; + foreach ($schema as $schemaKey => $schemaField) { + if (!is_array($schemaField)) continue; + switch (@$schemaField["type"]) { + case "upload": + $uploads = mysql_query_fetch_all_assoc("SELECT " . $fields_to_select . " FROM " . $TABLE_PREFIX . "uploads WHERE tableName = '" . $firstTable . "' and fieldName = '" . $schemaKey . "' and recordNum = " . $record["num"] . " ORDER BY `order` ASC"); + if (@$options['useAbsoluteUrls'] && is_array($uploads)) { + $uploads = array_map(function ($each) { + if (strpos($each['urlPath'], '/cms') === 0) { + $each['urlPath'] = str_replace('/cms', 'https://' . $_SERVER['HTTP_HOST'] . '/cms', $each['urlPath']); + } + return $each; + }, $uploads); + } + $record[$schemaKey] = @$uploads; + break; + default: + } + } + } + + /** + * Recupera todas las configuraciones de los plugins en una sola consulta y devuelve el resultado si existe el plugin buscado en un array general + */ + + static function getPluginsConfig($table,$where){ + if (!self::$pluginsConfig){ + $pluginsConfig = mysql_query_fetch_all_assoc("SELECT * FROM aux_plg_config ORDER BY num DESC"); + + self::$pluginsConfig = []; + foreach($pluginsConfig as $pluginConfig){ + if (!@self::$pluginsConfig[$pluginConfig["plugin"]]) self::$pluginsConfig[$pluginConfig["plugin"]] = $pluginConfig; + } + } + + $auxWhere = str_replace(" ","",strtolower(trim($where))); + $auxWhere = str_replace("plugin=","",$auxWhere); + $auxWhere = str_replace("'","",$auxWhere); + $auxWhere = str_replace('"','',$auxWhere); + + return @self::$pluginsConfig[$auxWhere] ?: []; + } + + /** + * Obtiene registros de una tabla + * @param table: Tabla de la que vamos a eliminar registros + * @param where: string con el where o array de condiciones ( condicion ["field" => num,"operator" => "=","value" => 1] ) + * @param order: string + * @param limit: string con el limit o array de limite y offset ( ["limit" => 10,"offset" => 20] ) + * @param options: Array asociativo que recoge opciones + @option : debug => Boolean + @option : translates => string con el idioma + @option : uploads => Boolean + @option : groupBy => string + @option : aggregates => array de aggregates + @option : relations => Boolean o array de campos en los que emitir las relaciones ( ['islas'] ) + @option : relationsDepth => Int + + * @return todos los registros + */ + static function get($table, $where, $order = null, $limit = null, $options = []) + { + global $TABLE_PREFIX; + + /*if ($table == "aux_plg_config"){ + // Evitar demasiadas consultas a aux_plg_config + $resultPlugin = self::getPluginsConfig($table,$where); + return $resultPlugin; + Desactivado temporalmente porque se pierden los márgenes + }*/ + + $microtime = microtime(true); + $optionsDefault = [ + "debug" => false, + "translates" => @$_REQUEST["idioma"], + "uploads" => true, + "useAbsoluteUrls" => false, + "groupBy" => null, + "ignoreSchema" => false, + "aggregates" => [], + "relations" => true, + "redis" => null, + "onlyFields" => null, + "redis_expire" => 60, + "relationsDepth" => self::$defaultRelationsDepth, + "dieBeforeQuery" => false, + "prefix" => $TABLE_PREFIX + ]; + + // ALIAS PARA JORDAN + if (@$options["ignoreSchemas"]) $options["ignoreSchema"] = $options["ignoreSchemas"]; + + foreach ($optionsDefault as $key => $value) { + if (!isset($options[$key])) $options[$key] = $value; + } + + + if (self::$force_redis) { + $options["redis"] = is_null($options["redis"]) ? true : $options["redis"]; + if (self::$redis_expireTime) $options["redis_expire"] = self::$redis_expireTime; + } + + + // EN CASO DE PEDIR CACHE REDIS INSTANCIAMOS Y CONECTAMOS + if (@$options["redis"]) { + self::initCache(); + } + + if (intval(@$options["relationsDepth"]) < 0) return []; + // Definición de tablas y schemas + $tables = array_filter(explode(",", $table)); + $tables = array_map("trim", $tables); + $schemas = []; + $fullSchemas = []; + + if (!@$options["ignoreSchema"]) { + foreach ($tables as $index => $table) { + $tableName = explode(" ", $table)[0]; + $schemaLoaded = loadSchema($tableName); + if (empty($schemaLoaded)) { + unset($tables[$index]); + continue; + } + if (!$order) $order = $schemaLoaded["listPageOrder"]; + $fullSchemas[$tableName] = $schemaLoaded; + + $schemas[$tableName] = array_filter($schemaLoaded, function ($rec) use ($options) { + // Creo que esto es innecesario pero por si acaso se necesite el squema sin uploads si no se pide + if (@$options["uploads"]) { + return !empty($rec) && is_array($rec) && isset($rec["type"]) && $rec["type"] != "separator"; + } else { + return !empty($rec) && is_array($rec) && isset($rec["type"]) && $rec["type"] != "separator" && $rec["type"] != "upload"; + } + }); + } + } + + $select = @$options["onlyFields"] ?: ["*"]; + + if (count($tables) > 1) { + $select = []; + foreach ($tables as $index => $table) { + $referencia = @explode(" ", $table)[1]; + $tableName = @explode(" ", $table)[0]; + if (!isset($schemas[$tableName])) continue; + + if (!isset($referencia)) $referencia = $options["prefix"] . $tableName; + if ($index === 0) { + $select[] = $referencia . ".*"; + } else { + $selectResult = $referencia . "." . join("," . $referencia . ".", array_map(function ($key) use ($referencia) { + return $key . " AS '" . $referencia . "." . $key . "'"; + }, array_keys($schemas[$tableName]))); + $select[] = $selectResult; + } + } + } + if (isset($options["aggregates"]) && is_array($options["aggregates"])) { + foreach ($options["aggregates"] as $aggregate) { + $select[] = $aggregate; + } + } + + // Definición de los FROM + $from = join(",", array_map(function ($table) use ($options) { + return $options["prefix"] . $table; + }, $tables)); + + // Definición del Where + $where = self::parse_where($where, null); + $meta_limit = 1000000; + + // Definición del Limit + if ($limit && is_array($limit)) { + if (isset($limit["perPage"])) $limit["limit"] = $limit["perPage"]; + if (!isset($limit["limit"])) self::error(["error" => "No se puede poner limit sin limit"]); + if (isset($limit["page"]) && !isset($limit["offset"])) $limit["offset"] = (max(1, intval($limit["page"])) - 1) * (intval($limit["limit"])); + $meta_limit = intval($limit["limit"]); + if (isset($limit["offset"])) { + $limit = intval($limit["offset"]) . "," . intval($limit["limit"]); + } else { + $limit = intval($limit["limit"]); + } + } else if ($limit) { + if(strpos($limit, ',') !== false) { + $meta_limit = intval(explode(',', $limit)[1]); + } else { + $meta_limit = intval($limit); + } + } + + + $select = join(', ', $select); + $sql = "SELECT " . $select; + $sql .= " FROM " . $from; + $sql .= $where ? " WHERE " . $where : ""; + $sql .= @$options["groupBy"] ? " GROUP BY " . $options["groupBy"] : ""; + $sql .= $order ? " ORDER BY " . $order : ""; + + $query_without_limit = $sql; + + $sql .= $limit ? " LIMIT " . $limit : ""; + + if (@$options['dieBeforeQuery']) { + self::error(["info" => $sql]); + } + + $hashSql = self::cacheGenerateHash(md5($sql . json_encode($options))); + + if (!self::$noCacheTABLES || (self::$noCacheTABLES && !in_array($TABLE_PREFIX . str_replace($TABLE_PREFIX, "", $table), self::$noCacheTABLES))) { + if (@$options["redis"]) { + $resultQueryRedis = self::cacheGet($hashSql); + if (@$resultQueryRedis) { + return json_decode($resultQueryRedis, true); + } + } else if (self::$force_load_cache) { + if (!empty(self::$getCaches[$hashSql])) { + return json_decode(self::$getCaches[$hashSql], true); + } + } + } + + $num_rows = null; + if (@$options["withMetas"]) { + $countSql = "SELECT COUNT(*) AS totalRecords FROM " . $from; + $countSql .= $where ? " WHERE " . $where : ""; + + // Cuando hay GROUP BY, totalRecords debe contar grupos y no filas crudas. + if (@$options["groupBy"]) { + $countSql = "SELECT COUNT(*) AS totalRecords FROM (SELECT 1 FROM " . $from; + $countSql .= $where ? " WHERE " . $where : ""; + $countSql .= " GROUP BY " . $options["groupBy"] . ") __count_groups"; + } + + $countResult = @mysql_query($countSql); + if ($countResult) { + $countRow = mysql_fetch_assoc($countResult); + if (isset($countRow["totalRecords"])) $num_rows = intval($countRow["totalRecords"]); + } + + // Fallback para no alterar comportamiento si el COUNT optimizado falla. + if (is_null($num_rows)) { + $num_rows = mysql_num_rows(mysql_query($query_without_limit)); + } + } + + $resultQuery = mysql_query($sql) or self::error(["error" => "Error en la consulta SQL " . mysql_error()]); + $records = []; + + $firstTable = explode(" ", $tables[0])[0]; + + // Uploads cacheados + if (@$options["uploads"] && self::$force_json_cache_uploads) { + $uploadsResult = self::getUploadsResults($options); + $uploadsResult[$firstTable] = isset($uploadsResult[$firstTable]) ? $uploadsResult[$firstTable] : []; + $possible_keys_of_uploads = array_filter(array_keys(@$uploadsResult[$firstTable])); + } + + // Records + while ($record = mysql_fetch_assoc($resultQuery)) { + // Uploads sin cache + if (@$options["uploads"] && !self::$force_json_cache_uploads) { + self::getUploadsResultsFromRecord($record, $firstTable, @$fullSchemas[$firstTable], $options); + } + if (!@$options["ignoreSchema"]) self::parseGetRecord($record, $firstTable, @$fullSchemas[$firstTable], $options, @$uploadsResult); + + + if (@$options["returnDataByKey"]) { + + $records[$record[$options["returnDataByKey"]]] = $record; + } else { + + $records[] = $record; + } + } + + if (self::$storeDebugData) { + $debugData = [ + "hora" => date("Y-m-d H:i:s", time()) . " " . microtime(), + "query" => $sql, + "records" => $records, + "time" => microtime(true) - $microtime + ]; + + if (self::$backTracePoint) { + $debugData["backTracePoint"] = self::$backTracePoint; + } + + self::$debugData[] = $debugData; + } + + if (@$options["debug"]) { + echo self::showDebug(true, count(self::$debugData) - 1); + } + + if (@$options["withMetas"]) { + $listDetails = [ + "totalRecords" => $num_rows, + //"totalMatches" => count($records), + "perPage" => $meta_limit, + //"keyword" => "", + "totalPages" => ceil($num_rows / max(1, $meta_limit)), + //"page" => 1, + //"prevPage" => 1, + //"nextPage" => 1 + ]; + if (@$options["redis"] && @$hashSql) { + if (!self::$noCacheTABLES || (self::$noCacheTABLES && !in_array($TABLE_PREFIX . str_replace($TABLE_PREFIX, "", $table), self::$noCacheTABLES))) { + self::cacheSet($hashSql, json_encode([$listDetails, $records]), $options["redis_expire"]); + } + } else if (self::$force_load_cache) { + self::$getCaches[$hashSql] = json_encode([$listDetails, $records]); + } + return [$listDetails, $records]; + } + if (@$options["redis"] && @$hashSql) { + if (!self::$noCacheTABLES || (self::$noCacheTABLES && !in_array($TABLE_PREFIX . str_replace($TABLE_PREFIX, "", $table), self::$noCacheTABLES))) { + self::cacheSet($hashSql, json_encode($records), $options["redis_expire"]); + } + } else if (self::$force_load_cache) { + self::$getCaches[$hashSql] = json_encode($records); + } + return $records; + } + + /** + * Fuerza que todas las consulta se ejecuten con caché redis + */ + static function localCache() + { + self::$force_load_cache = true; + } + + /** + * Inicializa la caché + * TO DO : Falta comprobar si no se puede conectar + */ + static function initCache() + { + $redisHost = '127.0.0.1'; + $redisPort = 6379; + + if (!self::$redis) { + self::$redis = new Redis(); + self::$redis->connect($redisHost, $redisPort); + } else if (!self::$redis->isConnected()) { + self::$redis->connect($redisHost, $redisPort); + } + } + + /** + * Fuerza que todas las consulta se ejecuten con caché redis + */ + static function fullCache($expireTime = 60) + { + self::$force_redis = true; + self::$redis_expireTime = $expireTime; + + self::initCache(); + } + /** + * Genera un hash para esta web + */ + static function cacheGenerateHash($string) + { + return $_SERVER["HTTP_HOST"] . "_" . $string . "_" . self::$redis_expireTime; + } + + /** + * Setea datos en el caché a través de un hash + */ + static function cacheSet($hash, $data, $expireTime = null) + { + if (!self::$redis) return; + if (!self::$redis->isConnected()) return; + if (!$expireTime && self::$redis_expireTime) $expireTime = self::$redis_expireTime; + if (!$expireTime) $expireTime = 60; + self::$redis->set($hash, $data); + self::$redis->expire($hash, $expireTime); + } + + /** + * Obtiene datos de el caché a través de un hash + */ + static function cacheGet($hash) + { + if (!self::$redis) return; + if (!self::$redis->isConnected()) return; + // SE HA MODIFICADO ESTE SCRIPT PORQUE CONSUMIA MAS RECURSOS EL GET + return self::$redis->exists($hash) ? self::$redis->get($hash) : null; + } + + /** + * Reajusta las variables de activación de cache según la url + */ + static function bloquedCacheByURL($url) + { + if (!self::$noCacheURIS) return false; + if (in_array($url, self::$noCacheURIS)) return true; + foreach (self::$noCacheURIS as $noCacheURI) { + if (preg_match('/' . $noCacheURI . '/i', $url, $matches, PREG_OFFSET_CAPTURE)) return true; + } + return false; + } + + /** + * Reemplaza el hook token en el html resultante para que la seguridad no bloquee las peticiones + */ + static function replaceHooksToken($html) + { + session_start(); + $html = preg_replace_callback( + "/var hooksToken(\s)?=(\s)?[\'\"]([a-zA-Z0-9]+)[\'\"]\;/i", + function ($matches) { + $token = sha1(session_id() . $_SERVER["HTTP_HOST"]); + return "var hooksToken = '" . $token . "'; console.log('⚡️⚡️⚡️ Render cached HTML ⚡️⚡️⚡️');"; + }, + $html + ); + return $html; + } + /** + * Realiza una traducción recursiva de un array de valores + */ + static function t_recursivo($record, $idx = null) + { + global $TABLE_PREFIX; + if (is_null(self::$allowedTranslateFields)) { + self::$allowedTranslateFields = array_flip(array_map(function ($field) { + return $field['fieldName']; + }, mysql_query_fetch_all_assoc("SELECT DISTINCT fieldName FROM {$TABLE_PREFIX}traducciones"))); + } + $it = $idx ? $record[$idx] : $record; + if (is_array($it)) { + foreach ($it as $key => $value) { + if (is_array($it[$key])) { + $it[$key] = self::t_recursivo($it[$key], null); + } else { + if (isset(self::$allowedTranslateFields[$key])) { + $it[$key] = t($it, $key); + if (isset($it[$key . '_bd']) && strpos($it[$key], '[') === 0 && strpos($it[$key], ']') === (strlen($it[$key]) - 1)) { + $it[$key . '_bd'] = json_decode($it[$key], true); + } + } + } + } + if ($idx) { + $record[$idx] = $it; + } else { + $record = $it; + } + } else { + if ($idx && isset(self::$allowedTranslateFields[$idx])) { + $record[$idx] = t($record, $idx); + if (isset($record[$idx . '_bd']) && strpos($record[$idx], '[') === 0 && strpos($record[$idx], ']') === (strlen($record[$idx]) - 1)) { + $record[$idx . '_bd'] = json_decode($record[$idx], true); + } + } + } + return $record; + } + /* + * Función traida del CMS lib/menus/default/common.php + */ + static function updateCategoryMetadata($tableName = null, $where = '') + { + global $escapedTableName, $schema, $TABLE_PREFIX; + + if (!$tableName) { + $newEscapedTableName = $escapedTableName; + $newSchema = $schema; + } else { + $newEscapedTableName = $TABLE_PREFIX . str_replace($TABLE_PREFIX, "", $tableName); + $newSchema = loadSchema($newEscapedTableName); + } + if ($newSchema['menuType'] != 'category') { + return; + } + + // load categoriesByNum + $categoriesByNum = array(); + + if ($where) $where = " WHERE $where"; + + $query = "SELECT * FROM $newEscapedTableName $where ORDER BY globalOrder"; + $result = mysql_query($query) or die("MySQL Error: " . mysql_error() . "\n"); + while ($row = mysql_fetch_assoc($result)) { + $categoriesByNum[$row['num']] = $row; + } + if (is_resource($result)) { + mysql_free_result($result); + } + + // get childNums for each parentNum + $childNumsOfParentNum = array(); + foreach (array_keys($categoriesByNum) as $num) { + $parentNum = (int) $categoriesByNum[$num]['parentNum']; + $childNumsOfParentNum[$parentNum][] = $num; + } + + // reset order + self::_updateCategoryBranch(array( + 'branchParent' => 0, + 'records' => &$categoriesByNum, + 'childNodes' => $childNumsOfParentNum, + )); + + + // save new order + foreach ($categoriesByNum as $num => $category) { + $query = "UPDATE `$newEscapedTableName` SET "; + $query .= "`globalOrder` = '" . mysql_real_escape_string($category['globalOrder']) . "', "; + $query .= "`siblingOrder` = '" . mysql_real_escape_string($category['siblingOrder']) . "', "; + $query .= "`depth` = '" . mysql_real_escape_string($category['depth']) . "', "; + $query .= "`lineage` = '" . mysql_real_escape_string($category['lineage']) . "', "; + $query .= "`breadcrumb` = '" . mysql_real_escape_string($category['breadcrumb']) . "' "; + $query .= "WHERE num = '{$category['num']}'"; + + mysql_query($query) or die("There was an error updating the category metadata:\n\n" . htmlspecialchars(mysql_error()) . "\n"); + } + } + /* + * Función traida del CMS lib/menus/default/common.php + */ + static function _updateCategoryBranch($args) + { + + ## set defaults + if (!@$args['globalOrder']) { + $args['globalOrder'] = 0; + } + if (!@$args['depth']) { + $args['depth'] = 0; + } + if (!@$args['lineage']) { + $args['lineage'] = ":"; + } + + # sort branch children + $sortedChildren = array(); + foreach ($args['childNodes'][$args['branchParent']] as $childNum) { + $sortedChildren[$childNum] = &$args['records'][$childNum]; + } + uasort($sortedChildren, 'self::_sortCategoriesBySiblingOrder'); + + # loop over branch children + $siblingOrder = 0; + foreach (array_keys($sortedChildren) as $childNum) { + $childRecord = &$args['records'][$childNum]; + + $childRecord['globalOrder'] = ++$args['globalOrder']; + $childRecord['siblingOrder'] = ++$siblingOrder; + $childRecord['depth'] = $args['depth']; + $childRecord['lineage'] = $args['lineage'] . "$childNum:"; + $childRecord['breadcrumb'] = @$args['breadcrumb'] ? "{$args['breadcrumb']} : {$childRecord['name']}" : $childRecord['name']; + + # if child has children, loop over them + if (@$args['childNodes'][$childNum]) { + self::_updateCategoryBranch(array( + 'branchParent' => $childNum, + 'globalOrder' => &$args['globalOrder'], + 'records' => &$args['records'], + 'childNodes' => $args['childNodes'], + 'depth' => ($args['depth'] + 1), + 'lineage' => $childRecord['lineage'], + 'breadcrumb' => $childRecord['breadcrumb'], + )); + } + } + } + /* + * Función traida del CMS lib/menus/default/common.php + */ + static function _sortCategoriesBySiblingOrder($arrayA, $arrayB) + { + if ($arrayA['siblingOrder'] < $arrayB['siblingOrder']) { + return -1; + } + if ($arrayA['siblingOrder'] > $arrayB['siblingOrder']) { + return 1; + } + return 0; + } +} diff --git a/cms/lib/classes/CocoEmail.php b/cms/lib/classes/CocoEmail.php new file mode 100755 index 0000000..b872fda --- /dev/null +++ b/cms/lib/classes/CocoEmail.php @@ -0,0 +1,468 @@ + "smtp.gmail.com", + "secure" => "ssl", + "port" => 465, + "username" => "soporte@cocosolution.com", + "password" => "", + "from" => "soporte@cocosolution.com", + "from_name" => "", + ]; + + static $use_dkim = false; + + static $dkim = [ + 'DKIM_domain' => 'cocosolution.com', + 'DKIM_selector' => 'default', + 'DKIM_private' => __DIR__ . '/default', + 'DKIM_identity' => 'soporte@cocosolution.com', + ]; + + static $bloqued_emails = []; + + static $mail_data = [ + "from" => "", + "from_name" => "", + ]; + + static $replyTo = [ + "to" => "", + "to_name" => "", + ]; + + static $send_copy_to = []; + static $send_blind_copy_to = []; + + static $template = " + + + {{TITLE}} + + + +
+ {{HEADER}} + {{CONTENIDO}} + {{FOOTER}} +
+ + + "; + + static $styles = " + body{font-family:Arial; color:#777; background-color:#F6F8FB;padding:20px;} + #contenido{max-width:640px; margin:0 auto; padding:20px; border: 1px solid #F6F8FB; background-color:#fff; border-radius:25px; -webkit-box-shadow:0px 0px 20px rgba(0,0,0,0.1); box-shadow:0px 0px 20px rgba(0,0,0,0.1);} + #contenido img {max-width: 100%;} + h3{font-weight:normal; color:#111;} + a{color:#CE482F; text-decoration:none;} + table td{border:solid 1px #ddd; padding:5px; width:100%; margin:0px;} + "; + + static $header = " +
+ +
+
+ "; + + static $footer = ""; + + /** + * Envía un correo y parsea su contenido con las variables pasadas por parámetro + * + * @param string $key + * @param array $params + * @param array $to + * @param string $subject + * @param boolean $returnHTML + * @param array options : [ + "twig" : Boolean -> Twig Mode, + "base64Decode" : Boolean -> Decode content from base64 to ascii + ] + * @return void + */ + static function send($key = null, $params = [], $to = [], $subject = null, $content = null, $returnHTML = false, $options = []) { + global $TABLE_PREFIX; + if ($key) { + $key = mysql_real_escape_string($key); + $record = mysql_fetch_assoc(mysql_query("SELECT * FROM $TABLE_PREFIX".self::$table." WHERE `".self::$field_key."` LIKE '$key'")); + if (!$record) { + throw new Exception('Correo no encontrado'); + } + $record["tableName"] = self::$table; + } + else if ($content) { + $record = [ + self::$field_subject => $subject, + self::$field_content => $content + ]; + } + else { + throw new Exception('Tienes que enviar la clave o el contenido'); + } + + if (!is_array($to)) { + $to = explode(',', $to); + } + $to = array_filter(array_map('trim', $to), function($a) { + return filter_var($a, FILTER_VALIDATE_EMAIL); + }); + + if (@$options["base64Decode"]) { + $content = base64_decode(t($record,self::$field_content)); + }else{ + $content = t($record, self::$field_content); + } + + $content = self::parse($content, $params,@$options ?: []); + + $subject = t($record, self::$field_subject); + $subject = self::parse($subject, $params, $options); + + $header = t($record, self::$field_header); + if(@$header) self::$header = self::parse($header, $params); + + $footer = t($record, self::$field_footer); + if(@$footer) self::$footer = self::parse($footer, $params); + + $styles = t($record, self::$field_styles); + if(@$styles) self::$styles = self::parse($styles, $params); + + if ($returnHTML) { + return $content; + } + if (empty($to)) { + throw new Exception('No hay destinatarios válidos'); + } + + if (!$subject && $record) $subject = t($record, self::$field_subject); + + $result = []; + foreach ($to as $destinatario) { + $resultString = self::send_email_coco_proxy($destinatario, $subject, $content); + $result[] = @json_decode($resultString,true) ?: ["success" => false, "message" => "Error decoding response", "raw_response" => $resultString]; + } + self::$attach_files = []; + return $result; + } + /* + options : [ + "twig" : Boolean -> Twig Mode + ] + */ + static function parse($content, $params, $options = []) { + if (@$options["twig"]){ + if (!function_exists("compileTWIG") && file_exists(__DIR__."/../plugins/builder_saas")) require_once __DIR__."/../plugins/builder_saas/builder_functions.php"; + $tempFolder = sys_get_temp_dir()."/".md5($content); + $content = html_entity_decode($content); + if (!file_exists($tempFolder)) mkdir($tempFolder); + if (!file_exists($tempFolder."/index.twig")){ + $php = compileTWIG($content,$tempFolder); + file_put_contents($tempFolder."/index.twig",$php); + }else{ + $php = file_get_contents($tempFolder."/index.twig"); + } + + ob_start(); + require($tempFolder."/index.twig"); + $acaiResultData->doDisplay($params); + $resultado = ob_get_clean(); + + return $resultado; + }else{ + $params = self::array_change_key_case_recursive($params); + $params_parsed = []; + foreach ($params as $key => $param) { + if (isset($param['tablename'])) { + $params_parsed[strtolower($param['tablename'])] = $param; + } + else { + $key = str_replace(['{', '}'], ['', ''], $key); + $params_parsed[$key] = $param; + } + } + + return preg_replace_callback("/{([^}]+)}/", function($matches) use($params_parsed) { + $token = explode(".", strtolower($matches[1])); + // Comprobamos si es un token simple o compuesto + if (count($token) === 1) { + // Token simple. Comprobamos si se refiere a una tabla o un valor fijo + if (isset($params_parsed[$token[0]])) { + if (is_array($params_parsed[$token[0]])) { + // Es un array. Devolvemos su mainField o, en su defecto, el primer campo que encontremos + if (isset($params_parsed[$token[0]]['mainfieldbreadcrumb'])) { + return $params_parsed[$token[0]]['mainfieldbreadcrumb']; + } + reset($params_parsed); + return t($params_parsed, key($params_parsed)); + } + else { + // Es un valor fijo + return $params_parsed[$token[0]]; + } + } + else { + return self::$show_variables ? '{'.$matches[1].'}' : '-'; + } + } + else { + // Es un token compuesto. Lo recorremos hasta que no queden más tokens y devolvemos el resultado + if (!isset($params_parsed[$token[0]]) || !is_array($params_parsed[$token[0]])) return '{'.$matches[1].'}'; + + $i = 0; + $current = $params_parsed; + do { + $tok = $token[$i]; + if (!isset($current[$tok])) return '{'.$matches[1].'}'; + $current = $current[$tok]; + $i += 1; + } while ($i < count($token)); + return $current; + } + }, $content); + } + } + + static function array_change_key_case_recursive($arr) { + return array_map(function($item){ + if(is_array($item)) + $item = self::array_change_key_case_recursive($item); + return $item; + }, array_change_key_case($arr)); + } + + function encrypt($string, $key) { + $result = ''; + for($i=0; $i self::$smtp_data, + "mail_data" => self::$mail_data, + "replyTo" => self::$replyTo, + "send_copy_to" => self::$send_copy_to, + "params" => [ + "to" => $destinatario, + "subject" => $asunto, + "body" => $contenido + ], + "bloqued_emails" => self::$bloqued_emails, + "encode_with_base64" => self::$encode_with_base64, + "template" => self::$template, + "styles" => self::$styles, + "header" => self::$header, + "footer" => self::$footer + ]; + + //$encryptData = self::encrypt(json_encode($data), "Analiticaempresas17"); + + $opts = array('http' => + array( + 'method' => 'POST', + 'header' => 'Content-Type: application/json', + 'content' => json_encode($data) + ) + ); + + $context = stream_context_create($opts); + $result = file_get_contents("https://cocosolution.com/?sendQuantumEmail=1", false, $context); + return $result; + } + + static function send_email($destinatario="soporte@cocosolution.com", $asunto="Error al enviar correo", $contenido="", $respuesta="") { + global $configuracionRecord; + + if (in_array($destinatario,self::$bloqued_emails)) die("Testing"); + + if (!isset($configuracionRecord)){ + $configuracionRecord = @CocoDB::get("configuracion","num != 0")[0]; + } + + require_once __DIR__ . '/../vendor/PHPMailer/PHPMailerAutoload.php'; + + /*$mensaje = " + + + ".$asunto." + + + +
+
+
+ ".$contenido." +
+ + + ";*/ + + $mensaje = self::$template; + $mensaje = str_replace('{{STYLES}}', self::$styles, $mensaje); + $mensaje = str_replace('{{HEADER}}', self::$header, $mensaje); + $mensaje = str_replace('{{TITLE}}', $asunto, $mensaje); + $mensaje = str_replace('{{CONTENIDO}}', $contenido, $mensaje); + $mensaje = str_replace('{{FOOTER}}', self::$footer, $mensaje); + + try { + $mail = new PHPMailer(); + // Basic + $mail->CharSet = "UTF-8"; + if(self::$encode_with_base64) $mail->Encoding = "base64"; + $mail->IsHTML(true); + + //Debug + if(self::$debug) { + $mail->SMTPDebug = 4; + echo "
"; + echo $mensaje; + echo "

"; + var_dump([ + 'To:' => $destinatario, + 'Asunto' => $asunto, + 'Contenido' => $mensaje + ]); + echo "
"; + } + + // SMTP + if(self::$smtp) { + $mail->Host = self::$smtp_data['host']; + $mail->IsSMTP(); + $mail->SMTPAuth = true; + if(self::$smtp_data['secure']) + $mail->SMTPSecure = self::$smtp_data['secure']; + $mail->Helo = @self::$smtp_data['helo'] ?:'webs.cocosolution.com'; + $mail->Port = self::$smtp_data['port']; + $mail->Username = self::$smtp_data['username']; + $mail->Password = self::$smtp_data['password']; + $mail->SetFrom(self::$smtp_data['from'], self::$smtp_data['from_name']); + } + + // DKIM + if (self::$use_dkim) { + $mail->DKIM_domain = self::$dkim['DKIM_domain']; + $mail->DKIM_private = self::$dkim['DKIM_private']; + $mail->DKIM_selector = self::$dkim['DKIM_selector']; + $mail->DKIM_identity = self::$dkim['DKIM_identity']; + } + + // Config + if(!self::$smtp) { + $mail->setFrom(self::$mail_data["from"] ?: $configuracionRecord["correo_admin"], self::$mail_data["from_name"] ?: $configuracionRecord["tienda_nombre_empresa"]); + } + $mail->addReplyTo(self::$replyTo["to"] ?: $configuracionRecord["correo_admin"], self::$replyTo["to_name"] ?: $configuracionRecord["tienda_nombre_empresa"]); + + if(self::$verify) { + $mail->AddAddress('check-auth-soporte=cocosolution.com@verifier.port25.com'); + } else { + $mail->AddAddress($destinatario); + foreach (self::$send_copy_to as $email) { + $mail->AddCC($email); + } + + foreach (self::$send_blind_copy_to as $email) { + $mail->addBCC($email); + } + + } + + $mail->Subject = $asunto; + + $mail->msgHTML($mensaje); + foreach (self::$attach_files as $file) { + $mail->addAttachment($file,basename($file)); + } + + if(!self::$stop_sending_emails) { + $resultMail = $mail->Send(); + + try{ + if (self::$webhook_url || self::$webhook_function){ + $dataMail = [ + "from" => $mail->From, + "from_name" => $mail->FromName, + "sender" => $mail->Sender, + "subject" => $mail->Subject, + "to" => $destinatario, + "body" => $mail->Body, + "alt_body" => $mail->AltBody, + "sended" => @$mail->ErrorInfo ? false : true, + "error" => @$mail->ErrorInfo + ]; + + if (!empty(self::$webhook_url) && filter_var(self::$webhook_url, FILTER_VALIDATE_URL)) { + + $opts = array('http' => + array( + 'method' => 'POST', + 'header' => 'Content-Type: application/json', + 'content' => json_encode($dataMail) + ) + ); + + $context = stream_context_create($opts); + $result = file_get_contents(self::$webhook_url, false, $context); + + } + + if (is_callable(self::$webhook_function)) { + + $resultFunction = call_user_func(self::$webhook_function,$dataMail); + + } + + } + } catch (Exception $e){ + // En caso de error no hacemos nada + } + + + } + + } catch (phpmailerException $e) { + echo $e->errorMessage(); //Pretty error messages from PHPMailer + } catch (Exception $e) { + echo $e->getMessage(); //Boring error messages from anything else! + } + } +} diff --git a/cms/lib/classes/CocoEnlace.php b/cms/lib/classes/CocoEnlace.php new file mode 100755 index 0000000..ff9edc7 --- /dev/null +++ b/cms/lib/classes/CocoEnlace.php @@ -0,0 +1,300 @@ + $idiomaNuevo){ + if (isset($idiomasViejo[$idioma]) && isset($idiomasNuevo[$idioma]) && $idiomasViejo[$idioma] != $idiomasNuevo[$idioma]){ + $preSQL = "num=null, createdDate='".date("Y-m-d H:i:s")."', updatedDate='".date("Y-m-d H:i:s")."', createdByUserNum=1, updatedByUserNum=1, dragSortOrder=".time(); + mysql_query("DELETE FROM ".$TABLE_PREFIX."alias_urls where url_alias='".$idiomasNuevo[$idioma]."'"); + mysql_query("DELETE FROM ".$TABLE_PREFIX."alias_urls where url_alias='".$idiomasViejo[$idioma]."' AND url_destino='".$idiomasNuevo[$idioma]."'"); + mysql_query("INSERT INTO ".$TABLE_PREFIX."alias_urls set ".$preSQL.", url_alias='".$idiomasViejo[$idioma]."', url_destino='".$idiomasNuevo[$idioma]."'"); + mysql_query("UPDATE ".$TABLE_PREFIX."alias_urls set url_destino='".$idiomasNuevo[$idioma]."' where url_destino='".$idiomasViejo[$idioma]."'"); + mysql_query("DELETE FROM ".$TABLE_PREFIX."alias_urls where url_alias=''"); + + } + } + + + } + } + return $enlaceNuevo; + + } + + static function _getEnlaceIdiomas($valores,$enlaceNuevo,$enlaceAnterior,$estado=0,$table = null){ + global $SETTINGS,$tableName,$TABLE_PREFIX; + $result = []; + if (!$table) $table = $tableName; + $result = mysql_query_fetch_all_assoc("SELECT prefix,fieldValue FROM ".$TABLE_PREFIX."traducciones WHERE tableName='".$table."' and fieldName='enlace' and recordNum='".$valores["num"]."'"); + if (!empty($result)) { + + $result2 = []; + foreach($result as $rec){ + $result2[$rec["prefix"]] = base64_decode($rec["fieldValue"]); + } + return $result2; + } + return $result; + } + + static function _seteaEnlaceIdiomas($valores,$enlaceNuevo,$enlaceAnterior,$estado=0,$table = null){ + global $SETTINGS,$tableName,$TABLE_PREFIX; + $result = []; + if (!$table) $table = $tableName; + + // REINICIAMOS LOS ENLACES DE LOS IDIOMAS + //die("Aun falta establecer los enlaces para los idiomas así que el cdn está inservible hasta que se haga"); + switch($estado){ + case 5: + case 6: + case 4: + case 2: + // EN ESTE CASO NO HACEMOS NADA + break; + case 1: + // AL PONER ENLACE DE FORMA MANUAL NO HACEMOS NADA EN IDIOMAS + break; + case 3: + default: + + mysql_query("DELETE FROM ".$TABLE_PREFIX."traducciones where tableName='".$table."' and fieldName='enlace' and recordNum='".$valores["num"]."'"); + + foreach($SETTINGS["idiomas"] as $key => $value): + + if ($value&&$value!="www"){ + $enlace = base64_encode("/".$value.$enlaceNuevo); + $result[$value] = "/".$value.$enlaceNuevo; + mysql_query("INSERT INTO ".$TABLE_PREFIX."traducciones set num=null,prefix='".$value."', tableName='".$table."', fieldName='enlace', fieldValue='".$enlace."', recordNum='".intval(@$valores["num"])."', preSaveTempId='".@$valores["preSaveTempId"]."'") or die(mysql_error()); + } + endforeach; + } + return $result; + } + + static function _getLinkPrefix($record,$table = null) { + global $tableName, $TABLE_PREFIX; + + if (!$table) $table = $tableName; + + $enlaces = array(); + + $record["tableName"] = $table; + $cont = 0; + while (true && $cont++ <= 50) { // Contador de seguridad para evitar el bucle infinito (que en teoría nunca pasará, jaja) + // Comprobamos si la tabla ha cambiado para no volver a cargar el schema + if ($record["tableName"] != @$tabla) { + + $tabla = $record["tableName"]; + if(class_exists('SchemaAPI')) { + $schema = SchemaAPI::getInstance()->loadSchema($tabla); + } else { + $schema = loadSchema($tabla); + } + $breadcrumbField = @$schema["breadcrumbField"]; + + if ($breadcrumbField == "parentNum") { + $schema[$breadcrumbField]["optionsTablename"] = $tabla; + $schema[$breadcrumbField]["optionsValueField"] = "num"; + } + + if (@$schema[$breadcrumbField]["optionsType"] == "query"){ + preg_match("/SELECT ([0-9a-z_]*),[\s?]([_0-9a-z]*) FROM ([a-z_]*)/",$schema[$breadcrumbField]["optionsQuery"],$matches); + + if (@$matches[1]) $schema[$breadcrumbField]["optionsValueField"] = $matches[1]; + if (@$matches[3]) $schema[$breadcrumbField]["optionsTablename"] = str_replace($TABLE_PREFIX,"",$matches[3]); + + + } + } + + if (!@$breadcrumbField || !@$schema[$breadcrumbField]["optionsTablename"]) { + break; + } + + + $record = @mysql_fetch_assoc(mysql_query("SELECT * FROM ".$TABLE_PREFIX.$schema[$breadcrumbField]["optionsTablename"]." WHERE `".$schema[$breadcrumbField]["optionsValueField"]."`='".$record[$breadcrumbField]."'")); + if (!@$record) break; + $record["tableName"] = $schema[$breadcrumbField]["optionsTablename"]; + array_unshift($enlaces, $record); + } + + if (!@$enlaces) return ""; + + $prefix = "/".join("/", array_map(function($a) { + return self::parsea_enlace($a[self::_getTitleField($a, $a["tableName"])]); + }, $enlaces)); + + return $prefix; + } + + static function _getTitleField($record, $tabla, $schema = null) { + if (@$record["name"]) return "name"; + if (@$record["title"]) return "title"; + if (@$record["titulo"]) return "titulo"; + if (@$record["nombre"]) return "nombre"; + + if (!@$campo) { + + if(class_exists('SchemaAPI')) { + if (!@$schemaAux) $schemaAux = SchemaAPI::getInstance()->loadSchema($tabla); + } else { + if (!@$schemaAux) $schemaAux = loadSchema($tabla); + } + foreach ($schemaAux as $key => $value): + if (@$value["type"] == "textfield" && $key != "enlace") { + return $key; + break; + } + endforeach; + } + } + + static function parsea_enlace($txt) { + + $transliterationTable = array("’" => "", ' ' => '',' ' => '','' => '', 'á' => 'a', 'Á' => 'A', 'à' => 'a', 'À' => 'A', 'ă' => 'a', 'Ă' => 'A', 'â' => 'a', 'Â' => 'A', 'å' => 'a', 'Å' => 'A', 'ã' => 'a', 'Ã' => 'A', 'ą' => 'a', 'Ą' => 'A', 'ā' => 'a', 'Ā' => 'A', 'ä' => 'a', 'Ä' => 'A', 'æ' => 'ae', 'Æ' => 'AE', 'ḃ' => 'b', 'Ḃ' => 'B', 'ć' => 'c', 'Ć' => 'C', 'ĉ' => 'c', 'Ĉ' => 'C', 'č' => 'c', 'Č' => 'C', 'ċ' => 'c', 'Ċ' => 'C', 'ç' => 'c', 'Ç' => 'C', 'ď' => 'd', 'Ď' => 'D', 'ḋ' => 'd', 'Ḋ' => 'D', 'đ' => 'd', 'Đ' => 'D', 'ð' => 'dh', 'Ð' => 'Dh', 'é' => 'e', 'É' => 'E', 'è' => 'e', 'È' => 'E', 'ĕ' => 'e', 'Ĕ' => 'E', 'ê' => 'e', 'Ê' => 'E', 'ě' => 'e', 'Ě' => 'E', 'ë' => 'e', 'Ë' => 'E', 'ė' => 'e', 'Ė' => 'E', 'ę' => 'e', 'Ę' => 'E', 'ē' => 'e', 'Ē' => 'E', 'ḟ' => 'f', 'Ḟ' => 'F', 'ƒ' => 'f', 'Ƒ' => 'F', 'ğ' => 'g', 'Ğ' => 'G', 'ĝ' => 'g', 'Ĝ' => 'G', 'ġ' => 'g', 'Ġ' => 'G', 'ģ' => 'g', 'Ģ' => 'G', 'ĥ' => 'h', 'Ĥ' => 'H', 'ħ' => 'h', 'Ħ' => 'H', 'í' => 'i', 'Í' => 'I', 'ì' => 'i', 'Ì' => 'I', 'î' => 'i', 'Î' => 'I', 'ï' => 'i', 'Ï' => 'I', 'ĩ' => 'i', 'Ĩ' => 'I', 'į' => 'i', 'Į' => 'I', 'ī' => 'i', 'Ī' => 'I', 'ĵ' => 'j', 'Ĵ' => 'J', 'ķ' => 'k', 'Ķ' => 'K', 'ĺ' => 'l', 'Ĺ' => 'L', 'ľ' => 'l', 'Ľ' => 'L', 'ļ' => 'l', 'Ļ' => 'L', 'ł' => 'l', 'Ł' => 'L', 'ṁ' => 'm', 'Ṁ' => 'M', 'ń' => 'n', 'Ń' => 'N', 'ň' => 'n', 'Ň' => 'N', 'ñ' => 'n', 'Ñ' => 'N', 'ņ' => 'n', 'Ņ' => 'N', 'ó' => 'o', 'Ó' => 'O', 'ò' => 'o', 'Ò' => 'O', 'ô' => 'o', 'Ô' => 'O', 'ő' => 'o', 'Ő' => 'O', 'õ' => 'o', 'Õ' => 'O', 'ø' => 'o', 'Ø' => 'O', 'ō' => 'o', 'Ō' => 'O', 'ơ' => 'o', 'Ơ' => 'O', 'ö' => 'o', 'Ö' => 'O', 'ṗ' => 'p', 'Ṗ' => 'P', 'ŕ' => 'r', 'Ŕ' => 'R', 'ř' => 'r', 'Ř' => 'R', 'ŗ' => 'r', 'Ŗ' => 'R', 'ś' => 's', 'Ś' => 'S', 'ŝ' => 's', 'Ŝ' => 'S', 'š' => 's', 'Š' => 'S', 'ṡ' => 's', 'Ṡ' => 'S', 'ş' => 's', 'Ş' => 'S', 'ș' => 's', 'Ș' => 'S', 'ß' => 'SS', 'ť' => 't', 'Ť' => 'T', 'ṫ' => 't', 'Ṫ' => 'T', 'ţ' => 't', 'Ţ' => 'T', 'ț' => 't', 'Ț' => 'T', 'ŧ' => 't', 'Ŧ' => 'T', 'ú' => 'u', 'Ú' => 'U', 'ù' => 'u', 'Ù' => 'U', 'ŭ' => 'u', 'Ŭ' => 'U', 'û' => 'u', 'Û' => 'U', 'ů' => 'u', 'Ů' => 'U', 'ű' => 'u', 'Ű' => 'U', 'ũ' => 'u', 'Ũ' => 'U', 'ų' => 'u', 'Ų' => 'U', 'ū' => 'u', 'Ū' => 'U', 'ư' => 'u', 'Ư' => 'U', 'ü' => 'u', 'Ü' => 'U', 'ẃ' => 'w', 'Ẃ' => 'W', 'ẁ' => 'w', 'Ẁ' => 'W', 'ŵ' => 'w', 'Ŵ' => 'W', 'ẅ' => 'w', 'Ẅ' => 'W', 'ý' => 'y', 'Ý' => 'Y', 'ỳ' => 'y', 'Ỳ' => 'Y', 'ŷ' => 'y', 'Ŷ' => 'Y', 'ÿ' => 'y', 'Ÿ' => 'Y', 'ź' => 'z', 'Ź' => 'Z', 'ž' => 'z', 'Ž' => 'Z', 'ż' => 'z', 'Ż' => 'Z', 'þ' => 'th', 'Þ' => 'Th', 'µ' => 'u', 'а' => 'a', 'А' => 'a', 'б' => 'b', 'Б' => 'b', 'в' => 'v', 'В' => 'v', 'г' => 'g', 'Г' => 'g', 'д' => 'd', 'Д' => 'd', 'е' => 'e', 'Е' => 'E', 'ё' => 'e', 'Ё' => 'E', 'ж' => 'zh', 'Ж' => 'zh', 'з' => 'z', 'З' => 'z', 'и' => 'i', 'И' => 'i', 'й' => 'j', 'Й' => 'j', 'к' => 'k', 'К' => 'k', 'л' => 'l', 'Л' => 'l', 'м' => 'm', 'М' => 'm', 'н' => 'n', 'Н' => 'n', 'о' => 'o', 'О' => 'o', 'п' => 'p', 'П' => 'p', 'р' => 'r', 'Р' => 'r', 'с' => 's', 'С' => 's', 'т' => 't', 'Т' => 't', 'у' => 'u', 'У' => 'u', 'ф' => 'f', 'Ф' => 'f', 'х' => 'h', 'Х' => 'h', 'ц' => 'c', 'Ц' => 'c', 'ч' => 'ch', 'Ч' => 'ch', 'ш' => 'sh', 'Ш' => 'sh', 'щ' => 'sch', 'Щ' => 'sch', 'ъ' => '', 'Ъ' => '', 'ы' => 'y', 'Ы' => 'y', 'ь' => '', 'Ь' => '', 'э' => 'e', 'Э' => 'e', 'ю' => 'ju', 'Ю' => 'ju', 'я' => 'ja', 'Я' => 'ja', "!" => "", "|" => "", "'" => "", "\"" => "", "'" => "", "@" => "", "·" => "", "#" => "", "$" => "", "¢" => "", "%" => "", "∞" => "", "¬" => "", "/" => "", "÷" => "", "(" => "", "“" => "", ")" => "", "”" => "", "≠" => "", "?" => "", "'" => "", "¡" => "", "¿" => "", "‚" => "", "´" => "", "^" => "", "`" => "", "[" => "", "*" => "", "+" => "", "]" => "", "¨" => "", "´" => "", "{" => "", "}" => "", "," => "", ";" => "", "„" => "", "." => "", ":" => "", "…" => "", "<" => "", ">" => "", "≤" => "", "≥" => "", "»" => "", "«" => "", "œ" => "", "æ" => "", "®" => "", "†" => "", "¥" => "", "π" => "", "∫" => "", "" => "", "™" => "", "¶" => "", "§" => "", "~" => "", "Ω" => "", "∑" => "", "©" => "", "√" => "", "µ" => "", "=" => "", "&" => "", " " => "-", "–" => "-", "_" => "-", " " => "-", '€' => 'e', 'º' => '', '°' => '', 'ª' => '', '&' => 'y', '\'' => ''); + $enlace = trim(strtolower(str_replace(array_keys($transliterationTable), array_values($transliterationTable), $txt))); + $enlace = preg_replace("/([\-]+)/", "-", $enlace); + if (substr($enlace,strlen($enlace)-1) == "-") $enlace = substr($enlace,0,strlen($enlace)-1); + $enlace = str_replace("-/","/",$enlace); + $enlace = str_replace("/-","/",$enlace); + $enlace = urlencode($enlace); + $enlace = str_replace("%C2","",$enlace); + $enlace = str_replace("%A0","",$enlace); + $enlace = str_replace("%250D","",$enlace); + return urlencode($enlace); + } + +} diff --git a/cms/lib/classes/CocoParser.php b/cms/lib/classes/CocoParser.php new file mode 100755 index 0000000..237d359 --- /dev/null +++ b/cms/lib/classes/CocoParser.php @@ -0,0 +1,533 @@ +", "