Creando un CMS

Nazari

Buenas, queria compartir este tutorial que estoy elaborando poco a poco.

Un CMS es un un Sistema de Gestión de Contenidos, permite la publicación de contenido en linea de forma ordenada por parte de terceros o del propio administrador, el contenido de los artículos o post enviados se almacena en la base de datos, mientras que de forma separada podemos gestionar la apariencia de nuestro sitio. En la actualidad hay muchos CMS disponibles, dos ejemplos muy importantes podrían ser WordPress y Drupal, ambos muy potentes y con comunidades muy grandes detrás, aunque ambos pueden hacer las mismas funciones, entrar en cual es mejor es una tontería, WordPress es mucho mas practico en sitios cuya función sea un blog, y Drupal es mas practico en blogs en los que intervengan muchas personas o en portales corporativos, al menos así los veo yo, por supuesto configurándolos y instalado plugins adicionales se puede hacer absolutamente de todo con cada uno de ellos, el uso que le demos depende única y exclusivamente de nosotros, intervendrá lo cómodo que nos sintamos trabajando con un sistema en concreto y que es lo que queremos crear.

Aunque cada sistema es único, todos (y no me refiero solo a WordPress y a Drupal) trabajan de forma similar en cuanto a la forma de publicar, moderar y dar formato al contenido.

Los Sistemas de gestión de foros como SMF o PHPBB también se podrían considerar dentro de la categoría de CMS, puesto que hacen lo mismo, permiten publicar, editar y borrar contenido almacenado en la base de datos, ademas de poder cambiar el ‘theme‘ de forma separada al sistema.

Mucha gente usa sin saberlo CMS en Internet, en servicios de blogs ya configurados, en foros etc.. pero muchos de ellos no saben como funcionan o por donde empezar si quisieran hacer algo parecido, es por eso que voy a montar un CMS paso a paso, explicando detalladamente que hace falta y para que se usa cada cosa.

La idea principal (sujeta a cambios), es crear un CMS con foro incluido, que permita añadir módulos sin mucha complicación una vez creado todo el sistema base y que tenga roles para los usuarios donde se distinguirá Inactivo, Usuario, Administrador, y otros que se quieran añadir.

Para este proyecto voy a usar xhtml, css, php5, mysql, y Jquery que es un Framework javascript que me hace mas fácil muchas cosas, entre ellas el uso de ajax.

Si quieres seguir el proceso es conveniente que tengas conocimientos previos de SQL, PHP y XHTML, aunque intentare explicarlo todo para que les resulte fácil a los menos experimentados.

Para empezar debemos de montar la estructura de la base de datos, no sera ni por asomo la estructura definitiva, si no que esta ira sufriendo cambios a lo largo del proceso, pero por algo tenemos que empezar ¿verdad?. Yo la he llamado cms.

Primero necesitamos una tabla donde registrar a los usuarios y controlar quien puede y no puede escribir en nuestro gestor de contenidos ,esta debe de contener:

  • ID: Sera el numero de identificación del usuario
  • USER: El nombre que el usuario elegirá para entrar en el cms
  • NAME: El nombre que el usuario decidirá mostrar una vez dentro del cms
  • PASS: La contraseña del usuario (Estará en MD5 para evitar problemas de seguridad)
  • EMAIL: Un email de contacto
  • DATE: La fecha en la que se registro el usuario
  • LAST_DATE: Fecha del ultimo movimiento del usuario en el cms
  • IP: Dirección ip desde la que el usuario registro su cuenta
  • LAST_IP: Ultima dirección ip que tuvo el usuario
  • STATUS: Indicara si el usuario tiene su cuenta inactiva (0), si es un usuario normal (1) o que rol tiene (otro numero)

De momento con esos campos tenemos suficiente, en un futuro se puede ampliar para aportar mas datos, permitir avatares, etc…

Esta es la tabla:

CREATE TABLE IF NOT EXISTS `users` (
  `id` INT(5) NOT NULL AUTO_INCREMENT,
  `user` VARCHAR(33) NOT NULL,
  `name` VARCHAR(33) NOT NULL,
  `pass` VARCHAR(33) NOT NULL,
  `email` VARCHAR(50) NOT NULL,
  `date` INT(15) NOT NULL,
  `last_date` INT(15) NOT NULL,
  `ip` VARCHAR(128) NOT NULL,
  `last_ip` VARCHAR(128) NOT NULL,
  `status` SMALLINT(2) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `user` (`user`,`email`)
)

Si nos fijamos en el ultimo campo de la tabla users vemos que ahí controlamos si el usuario tiene su cuenta activa, y que rol tiene. ¿Que es un rol? Un rol vendría a ser un papel o funciones que una persona tiene que cumplir, adaptándolo a nuestro sistema el ser Administrador o moderador es tener un rol. Entonces ¿Quien define que tipo de roles hay y cuantos?, fácil, nosotros.

Creamos otra tabla con los siguientes campos:

  • ID: Identificación numérica del rol
  • NAME: El nombre que elijamos para ese rol, admin, administrador, colaborador, lo que queramos.
  • DESCRIPTION: Descripción de las funciones del rol, a nivel informativo solamente.
  • PERMISSION: Permisos que tendrá el Rol sobre el cms

El campo permission esta pensado de cara al futuro para posibles funciones que queramos añadir, pero no adelantemos acontecimientos aun.

Esta es la tabla:

CREATE TABLE IF NOT EXISTS `users_role` (
  `id` INT(5) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(30) NOT NULL,
  `description` text NOT NULL,
  `permission` VARCHAR(30) NOT NULL,
  PRIMARY KEY (`id`)
)

Bien, hasta el momento ya hemos pensado como va a ser la estructura de las tablas que controlan a los usuarios, pero ¿que es un CMS sin contenido que gestionar?, necesitamos pensar que tablas necesitamos para controlar el contenido.

Ahora necesitamos una con los campos correspondientes para almacenar el contenido, el usuario, y algunas cosas mas, estos serian los campos que vamos a usar:

  • ID: Identificación numérica de la revisión (¡Ojo! de la revisión no del contenido)
  • CID: Identificación numérica del contenido
  • TYPE: El tipo de contenido
  • REVISION: La revisión seria una copia de seguridad del contenido
  • USER: el usuario que envía el contenido
  • DATE: Fecha en la que se envía el contenido
  • DATE_PUBLISHED: Fecha de publicación del contenido
  • TITLE: Titulo del contenido
  • CONTENT: Cuerpo del contenido
  • CATEGORY: Categoría en la que se clasifica el contenido
  • TAGS: Etiquetas de referencia sobre el contenido
  • COMMENT_ALLOWED: Permitir los comentarios o no…

Esta es la tabla:

CREATE TABLE IF NOT EXISTS `content` (
  `id` INT(10) NOT NULL AUTO_INCREMENT,
  `cid` INT(10) NOT NULL,
  `type` INT(5) NOT NULL,
  `revision` INT(10) NOT NULL,
  `user` INT(5) NOT NULL,
  `date` INT(15) NOT NULL,
  `status` SMALLINT(1) NOT NULL,
  `date_published` INT(15) NOT NULL,
  `title` text NOT NULL,
  `content` longtext NOT NULL,
  `category` INT(10) NOT NULL,
  `tags` VARCHAR(150) NOT NULL,
  `comment_allowed` tinyint(1) NOT NULL,
  PRIMARY KEY (`id`)
)

Algo muy importante es poder elegir si el contenido que queremos introducir es una entrada del blog, una pagina, un post, o cualquier otra cosa que queramos, así que vamos a definir una tabla para controlar el tipo de contenido.

  • ID: Identificación numérica del tipo de contenido
  • NAME: Nombre del tipo, por ejemplo ‘Entrada’ o ‘Pagina’
  • DESCRIPTION: Breve descripción sobre el uso de este tipo de contenido
  • COMMENT_ALLOWED: Comentarios permitidos
  • ACCESS_ROLE: Que roles pueden acceder a los contenidos de este tipo
  • DEL_ROLE: Que roles pueden borrar los contenidos de este tipo
  • EDIT_ROLE: Que roles pueden editar los contenidos de este tipo

Aunque en la tabla content ya tengamos la opcion comment_allowed para permitir o no el envió de comentarios, es importante usarlo también aquí para darle un valor por defecto al tipo de contenido, luego puede ser que nosotros queramos desactivar los comentarios en alguna situación excepcional aunque el tipo de contenido lo permita.

Esta es la tabla

CREATE TABLE IF NOT EXISTS `content_type` (
  `id` INT(5) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(50) NOT NULL,
  `description` text NOT NULL,
  `comment_allowed` tinyint(1) NOT NULL,
  `access_role` VARCHAR(50) NOT NULL,
  `del_role` VARCHAR(50) NOT NULL,
  `edit_role` VARCHAR(50) NOT NULL,
  PRIMARY KEY (`id`)
)

Por ultimo nos quedan dos tablas, una para las categorías y otra para las etiquetas o tags, son ambas muy simples y no necesitan explicación.

CREATE TABLE IF NOT EXISTS `tags` (
  `id` INT(5) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(50) NOT NULL,
  PRIMARY KEY (`id`)
)
 
CREATE TABLE IF NOT EXISTS `category` (
  `id` INT(5) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(50) NOT NULL,
  PRIMARY KEY (`id`)
)

Con estas 6 tablas ya tenemos una estructura básica para seguir avanzando, pero eso sera en otra entrada.

Un Saludo!

22
eXtreM3

Esto lo has publicado en tu blog?

Nazari

Si claro, esta en mi blog tambien, lo voy a ir publicando aqui que le puede ser de utilidad a mas personas. ¿Por Que?

scumah

Ahí tienes mi manita, que parece que te lo estás currando :P

Anda que no ha mejorado nada el <code>... :D

Nazari

Muchas Gracias a los que lo veis util.

Continuando con el tutorial sobre como entender el funcionamiento de un CMS y ser capaz de crear uno propio muy básico, vamos a hablar de la instalación.

Normalmente cuando tenemos pensado instalar un sistema de gestión de contenidos lo primero que debemos hacer es descargarlo, ya sea de pago o libre lo normal es que lo encontremos en Internet en un archivo comprimido, por supuesto hay empresas con CMS propios o que usan sistemas libres y hacen el trabajo de instalarlo por nosotros, pero como el objetivo de la guía es aprender hacemos todo el trabajo nosotros ;) .

Una vez descargado vemos que el archivo comprimido contiene diferentes archivos y directorios, y nuestra labor consiste en descomprimir ese archivo en nuestro servidor, esos archivos son el corazón del CMS, todos y cada uno de ellos tiene una función, ya puede ser individual o relacionándose con otros archivos, cuando estén descomprimidos todos los archivos en nuestro servidor accedemos al dominio para comprobar que funciona correctamente el CMS, pero al entrar nos damos cuenta de que aun no esta todo hecho, el CMS ¡hay que instalarlo!, al acceder por primera vez al dominio suele aparecer una ventana de Instalación que nos pedirá ciertos datos, como nombre de la base de datos, usuario de la base de datos, etc… Al terminar de rellenar esos datos y darle a aceptar es cuando se ‘Instala’ el CMS, realmente lo que ha pasado es que ha volcado todos los datos necesarios para su funcionamiento en la Base de Datos que le hemos indicado, y en algunos casos puede haber realizado alguna pequeña modificación en algún Archivo.

En la primera parte de esta guía definimos la estructura de nuestra base de datos, puede sufrir modificaciones a lo largo del proceso del CMS, pero es una estructura básica que nos resultara útil de momento. Siguiendo a partir de ahí debemos de saber que al entrar a un dominio, por ejemplo http://www.mediavida.com o http://localhost/ el archivo que carga se llama index, este puede ser index.php, index.html, index.htm o otro que este indicado en la configuración del servidor, así que nosotros necesitaremos también un index.php. Ademas necesitamos otro archivo llamado install.php que contendrá todo el código que hará la instalación de nuestro sistema.

De momento nuestro index.php al ejecutarse deberá de comprobar que config.php existe, si la respuesta es negativa redireccionamos a install.php, en caso de que config.php exista significa que ya esta instalado el sistema y se mostrara el index normalmente.

index.php:

<?php 
 
//Comprobamos que existe el archivo config.php
if (!file_exists("config.php")){
  //Si existe redireccionamos al archivo install.php
  header("Location: install.php");
} else {
  //Si no existe ejecutamos el index normalmente
  echo "El sistema se ha instalado correctamente";
}
 
?>

El archivo install.php tiene que contar con varias partes para que se configure todo correctamente, un formulario ha de pedir los datos de la base de datos que vamos a usar y del usuario que hará de administrador de nuestro CMS, otra parte ha de comprobar que se han rellenado todos los datos y por ultimo si esta todo correcto deberá ejecutarse.

install.php:

<?php 
 
//Si el archivo config.php existe significa que el sistema ya fue instalado
if (file_exists("config.php")){
  die("<strong>El sistema ya esta instalado</strong>");
}
 
//Si se envió el formulario comprobamos que todos los campos estén rellenos
if ($_POST['submit']){
  if (empty($_POST["db-host"])){
    echo "Debes de rellenar el campo 'Host de la base de datos'<br />";
    $return = 1;
  } 
  if (empty($_POST["db-name"])){
    echo "Debes de rellenar el campo 'Nombre de la base de datos'<br />";
    $return = 1;
  } 
  if (empty($_POST["db-user"])){
    echo "Debes de rellenar el campo 'Usuario de la base de datos'<br />";
    $return = 1;
  } 
  if (empty($_POST["db-pass"])){
    echo "Debes de rellenar el campo 'Contraseña de la base de datos'<br />";
    $return = 1;
  } 
  if (empty($_POST["admin-name"])){
    echo "Debes de rellenar el campo 'Usuario'<br />";
    $return = 1;
  }
  if (empty($_POST["admin-pass"])){
    echo "Debes de rellenar el campo 'Contraseña'<br />";
    $return = 1;
  } 
  if (empty($_POST["admin-email"])){
    echo "Debes de rellenar el campo 'Correo Electrónico'<br />";
    $return = 1;
  }
  if ($_POST["admin-pass"] != $_POST["admin-2pass"]){
    echo "Las Contraseñas de usuario no coinciden<br />";
    $return = 1;
  } 
 
  //En el caso de que algún campo no este relleno no se continua con la instalación
  if (isset($return)){
    echo "<br /><br /><a href=\"install.php\">Volver al Formulario</a>";
  } else {
    echo "Comenzando Instalación<br />";
 
//Conectamos con la base de datos que el usuario nos ha proporcionado
$con=mysql_connect($_POST["db-host"],$_POST["db-user"],$_POST["db-pass"]);
mysql_select_db($_POST["db-name"],$con);
if (!$con){
  die("No se puede establecer conexión con la base de datos, por favor comprueba los datos. <br /><br /><a href=\"install.php\">Volver al Formulario</a>");
}
 
//Definimos las tablas en un array(), creamos roles básicos y usuario admin
$db_tables = array(
"CREATE TABLE IF NOT EXISTS `category` (
`id` int(5) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
)",
"CREATE TABLE IF NOT EXISTS `content` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`cid` int(10) NOT NULL,
`type` int(5) NOT NULL,
`revision` int(10) NOT NULL,
`user` int(5) NOT NULL,
`date` int(15) NOT NULL,
`status` smallint(1) NOT NULL,
`date_published` int(15) NOT NULL,
`title` text NOT NULL,
`content` longtext NOT NULL,
`category` int(10) NOT NULL,
`tags` varchar(150) NOT NULL,
`comment_allowed` tinyint(1) NOT NULL,
PRIMARY KEY (`id`)
)",
"CREATE TABLE IF NOT EXISTS `content_type` (
`id` int(5) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`description` text NOT NULL,
`comment_allowed` tinyint(1) NOT NULL,
`access_role` varchar(50) NOT NULL,
`del_role` varchar(50) NOT NULL,
`edit_role` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
)",
"CREATE TABLE IF NOT EXISTS `tags` (
`id` int(5) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
)",
"CREATE TABLE IF NOT EXISTS `users` (
`id` int(5) NOT NULL AUTO_INCREMENT,
`user` varchar(33) NOT NULL,
`name` varchar(33) NOT NULL,
`pass` varchar(33) NOT NULL,
`email` varchar(50) NOT NULL,
`date` int(15) NOT NULL,
`last_date` int(15) NOT NULL,
`ip` varchar(128) NOT NULL,
`last_ip` varchar(128) NOT NULL,
`status` smallint(2) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `user` (`user`,`email`)
)",
"CREATE TABLE IF NOT EXISTS `users_role` (
`id` int(5) NOT NULL AUTO_INCREMENT,
`name` varchar(30) NOT NULL,
`description` text NOT NULL,
`permission` varchar(30) NOT NULL,
PRIMARY KEY (`id`)
)",
"INSERT INTO users (user, pass, email, date, ip, status) values (
'".$_POST["admin-name"]."',
'".md5($_POST["admin-pass"])."',
'".$_POST["admin-email"]."',
'".time()."',
'host',
'2'
)",
"INSERT INTO users_role (name, description) values (
'Usuario',
'Usuario registrado'
)",
"INSERT INTO users_role (name, description) values (
'Admin',
'Usuario Administrador'
)" );
 
//Mediante un foreach ejecutamos todas sentencias almacenadas en el array anterior
foreach($db_tables as $x){
  $query = mysql_query($x)or die(mysql_error());
}
 
//Cerramos la conexión con la base de datos
mysql_close($con);
 
//Montamos en una variable el contenido del archivo config.php
$config_info = "<?php\n/* Archivo config.php generado por el sistema */\n\n\$bdhost = \"".$_POST["db-host"]."\";\n\$bdname = \"".$_POST["db-name"]."\";\n\$bduser = \"".$_POST["db-user"]."\";\n\$bdpass = \"".$_POST["db-pass"]."\";\n?>";
 
//Comprobamos que podemos escribir en la carpeta raíz del sistema
if (is_writable("./") != 1) {
  echo "No se ha podido crear config.php por un problema con los permisos de la carpeta raíz, tu mismo puedes crear el archivo config.php con el siguiente contenido para terminar la instalación, una vez hecho <a href=\"index.php\">Vuelve al Inicio</a>. <br />";
  echo "<textarea cols=\"40\" rows=\"10\">".$config_info."</textarea>";
} else {
 
  //Creamos y rellenamos el archivo config.php con la información que nos ha dado el usuario
  $keys_file = fopen("config.php","w");
  fwrite($keys_file, $config_info);
  fclose($keys_file);
  echo "<br />Se ha completado la instalación con éxito<br /><a href=\"index.php\">Volver al Inicio</a>";
}
  }
 
} else {
 
  //Este es el formulario de instalación
  ?>
  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
      <link type="text/css" rel="stylesheet" media="all" href="style.css" />
      <title>Instalador de Capricorn CMS</title>
    </head>
    <body>
    <div id="install">
      <form action="install.php" method="post">
      <div id="install-title">Instalador</div>
      <table>
        <tr><td colspan="2" class="install-td">Información sobre la Base de datos</td></tr>
        <tr><td>Host de la base de datos:</td><td><input type="text" name="db-host" value="localhost" /></td></tr>
        <tr><td>Nombre de la base de datos:</td><td><input type="text" name="db-name" value="Nombre" /></td></tr>
        <tr><td>Usuario de la base de datos:</td><td><input type="text" name="db-user" value="Usuario" /></td></tr>
        <tr><td>Contraseña de la base de datos:</td><td><input type="password" name="db-pass" /></td></tr>
        <tr><td colspan="2" class="install-td">Información sobre el Administrador</td></tr>
        <tr><td>Usuario:</td><td><input type="text" name="admin-name" value="Nombre" /></td></tr>
        <tr><td>Contraseña:</td><td><input type="password" name="admin-pass" /></td></tr>
        <tr><td>Repetir Contraseña:</td><td><input type="password" name="admin-2pass" /></td></tr>
        <tr><td>Correo Electrónico:</td><td><input type="text" name="admin-email" value="Email" /></td></tr>
        <tr><td colspan="2" class="install-td"><input type="submit" name="submit" value="Instalar" /></td></tr>
      </table>
      </form>
    </div>
    </body>
  </html>
<?php
}
?>

De momento no entraremos en cuestiones de seguridad ni de requisitos, pero hay que tener en cuenta que el programador no puede fiarse del uso que le darán los Usuarios a su código, así que todo contenido que entre al CMS para ser almacenado en la BD o mostrado en pantalla debería de pasar por unas funciones para limpiar cualquier código malicioso que pueda contener. Sobre los requisitos hablaremos también en otra entrada, ya que por el momento no es necesario complicar mas esta parte, pero tendremos que definir unas reglas para elegir nombre de usuario, contraseña, email, etc… como por ejemplo el numero de caracteres permitidos.

Algo muy importante de install.php es que maneja archivos y directorios, esto conlleva varias dificultades sobretodo relacionadas con los permisos de estos. En caso de que el instalador no pueda crear config.php nos dará su contenido para que lo creemos a mano, aunque si el directorio tiene los permisos adecuados se creara sin que tengamos que intervenir.

Una ultima cosa, si os fijáis he almacenado las sentencias SQL en un array(), y luego las ejecuto mediante un foreach(), estoy seguro de que hay una forma mejor de hacer esto, pero por mucho que intente cargar un *.sql externo o almacenar las sentencias en una sola variable, siempre me daba error de Sintaxis, pero si las ejecuto de forma separada no me da ningún error, si alguien sabe como hacerlo estaré muy agradecido.

Os dejo un archivo rar que contiene los script de la entrada. He añadido una hoja de estilo para que no se vea tan feo el instalador.

Un Saludo

3
B

y pa q no se diga q viene a hacer spam ya se lo hago yo

http://www.nazariglez.com/

muy buen proyecto, y del carajo explicado, congratz!

(en su web hay más)

pd. no lo conozco de na, ajin q no mamoneeis xD

otra pd. de verdad q ha mejorao el <code> xD

1 respuesta
tOWERR

#1
Impresionante, te doy un +1.

DarkSoldier

te añado favoritos el post para ver como sigue ;) animo #1

pd:+1

FaLLeN

tiene buena pinta, si. +1 para ti

Nazari

Muchas gracias a todos, no me esperaba tan buena aceptación la verdad, mas bien esperaba que esto pasara inadvertido, #6 me he reído un motón gracias por tu spam gratuito jaja

Sigo con otra parte mas.

Ahora que tenemos nuestra base de datos instalada, y un index.php (semi vacio) es el momento de preparar algunas cosas básicas.

Un CMS nos permite movernos entre sus paginas de una forma paginada y ordenada, así podemos cambiar a la sección de registrar usuarios, ver entradas, comentar, etc.. sin necesidad de cambiar de script continuamente, esto se hace mediante includes y peticiones _GET. Para el programador es una gran ventaja, puesto que no tiene que repetir código en todos los scripts, y si surge un fallo sabe que lo tiene en un solo script y que no tendrá que modificar todos los demás. De esta forma el mantenimiento y modificación del sistema es mas ordenado y fácil. Esto es llamado comúnmente como programación modular.

Puesto que el desarrollo del sistema se va complicado poco a poco, vamos a preparar y crear diferentes scripts que nos sirvan de cara al futuro.

Primero nos dirigimos al index.php y en la parte superior definimos una constante que nos servirá para evitar que se ejecuten scripts de forma separada al index.

define("CAPRICORN", 1);

Podemos usar el nombre de la constante que nosotros queramos, yo he usado CAPRICORN, mi consejo es que pongamos en mayúsculas siempre las constantes, de esta forma las diferenciaremos rápidamente.

Ahora incluimos un archivo llamado sec.php (el cual aun no hemos creado) que estará dentro de un directorio llamado includes, se encargara de mostrarnos la sección que le pidamos mediante _GET, también debemos incluir el script config.php que nos genero el instalador, pero ambos han de ser incluidos dentro del if() que tiene que comprobar si el sistema ya esta instalado, ademas, en caso de fallo al incluir el script el resto de la pagina no debe de cargar, para esto usaremos require(); y una vez incluido config.php hacemos conexión a la base de datos usando las variables que contiene, el index.php debe de quedar así:

<?php 
 
define("CAPRICORN", 1); //Definimos una constante para evitar que se ejecuten scripts por separado.
 
//Comprobamos que existe el archivo config.php
if (!file_exists("config.php")){
 
  //Si existe redireccionamos al archivo install.php
  header("Location: install.php");
 
} else {
 
  //Si no existe ejecutamos el index normalmente
  require("config.php"); //Requerimos config.php
  require("includes/sec.php"); //Requerimos pag.php
 
  //Conectamos con la base de datos
  $con=mysql_connect($bdhost,$bduser,$bdpass);
  mysql_select_db($bdname,$con);
 
  //Incluimos el contenido definido en sec.php
  include($include_scp);
 
  //Cerramos la conexion
  mysql_close($con);
 
}
 
?>

Lo siguiente es crear el directorio includes y el script sec.php en su interior.

El script sec.php debe ‘morir‘ o redirigirse a el index si alguien entra directamente al directorio http://localhost/includes/sec.php, para eso ya definimos una constante en el index.php, ahora solamente tenemos que comprobar que la constate esta definida cuando se ejecuta sec.php, para eso lo primero que debe contener sec.php es lo siguiente:

if (!defined('CAPRICORN')){
header('Location: index.php');
}

De esta forma evitamos que sea ejecutado de forma separada del index.php.

Ahora usaremos la función switch(), la cual es muy similar a if,else, para comparar la variable que entra por _GET y incluir el archivo necesario. El archivo sec.php debe de quedar asi:

<?php
 
//Si el script se ejecuta de forma separada a index.php redireccionamos al index.
if (!defined('CAPRICORN')){
  header('Location: index.php');
}
 
//Comprobamos que _GET tiene el valor sec
if(isset($_GET['sec'])){
 
  //Si lo tiene definimos scripts a incluir segun su valor.
  switch ($_GET['sec']){
    case "registro":
      $include_scp = "includes/register.php";
    break;
    default:
      $include_scp = "includes/index.php";
    break;
  }
 
}else{
 
  //Si _GET no tiene valor o no es sec, definimos una script por defecto a incluir.
  $include_scp = "includes/index.php";
 
}
?>

Como se ve he incluido una referencia a un script por defecto y otro a ‘registro‘ que dirige a la script register.php, de momento quedan esos dos, pero se añadirán muchos mas que formaran la base de nuestro sistema, y en un futuro inventamos algo para añadir módulos o plugins adicionales sin necesidad de modificar la script.

Ahora lo que tenemos que hacer es crear dentro del directorio includes los archivos index.php y register.php, a los cuales hay que añadirle que compruebe que son ejecutados a través del index principal, podrían ser estos:

register.php:

<?php
 
//Si el script se ejecuta de forma separada a index.php redireccionamos al index.
if (!defined('CAPRICORN')){
  header('Location: index.php');
}
 
echo "Te encuentras ahora mismo en register.php";
 
?>

index.php:

<?php
 
//Si el script se ejecuta de forma separada a index.php redireccionamos al index.
if (!defined('CAPRICORN')){
  header('Location: index.php');
}
 
echo "Te encuentras ahora mismo en el index.php";
 
?>

Así al entrar en http://localhost/index.php nos estará mostrando includes/index.php y al entrar en http://localhost/index.php?sec=registro nos mostrara includes/register.php.

De momento ya tenemos montada la base de un sistema de gestión modular, podeis encontrar todos los script usados hasta ahora aquí.

Un Saludo

3
NeB1

Muy bueno tu tutorial. Tengo un par de comentarios al respecto.

Yo prefiero hacer

if(!defined('CAPRICORN')) die('No se permite el acceso directo');

Por que de la otra forma no evitas la ejecución del script y puede tener algún bug de seguridad. Aunque para un usuario normal automaticamente vayas al index, yo puedo dedicarme a hacer un script para hacerle peticiones al archivo en cuestión y intentar inyectarle código mediante get o post.

Por otro lado, cuando hago un gestor de contenido, no suelo usar mysql_query directamente. Al igual que en muchas otras funciones creo funciones aposta a las que les incluyo código relativo a la seguridad o a lo que sea.

Porque si de repente aparece un nuevo método de seguridad (como cuando apareció mysql_real_escape_string en la versión 4.3 de PHP), te tocaría aplicarlo en todos los sitios donde has usado mysql_query.

así que hago cosas del estilo (código hecho a ojo, no probar en casa xD):

function db_query($query) {
    $args = array_shift(func_get_args());
    return vsprintf(preg_replace('/%([1-9]):(d|s)/','%$1$$2',$query), array_map('mysql_real_escape_string',$args));
}

para luego llamar a la consulta así:

$query = db_query("SELECT * FROM TABLA WHERE `columna`= '%s' AND `columna2` = '%d' DESC", "hola", 3); 

De todas formas es una pasada lo que te lo estás currando!

2
Nazari

Mi madre NetB1 llevo un rato analizando tu función para saber que hacia exactamente porque nunca he usado vsprintf ni array_shift, ni func_get_args y he de decir que me has enseñado como mejorar muchísimo algunas funciones que tengo de otros proyectos, tengo dos preguntas, la primera es preguntarte si ves factible usar:

if (!defined('CAPRICORN')){
header('Location: index.php');
exit; //<--------
}

La otra pregunta es, pasar el query mediante otra función externa no podría afectar al rendimiento de las consultas?

Si es posible me gustaría que me ayudaras a montar unas funciones para hacer los querys de forma eficiente, (solamente si te apetece y dispones de tiempo) puesto que en algunas se pueden pasar muchos parámetros como orden limites diferentes tablas, etc... debería de montar una función para cada una de las posibilidades? O una sola que contemple todas estas cosas?

1 respuesta
NeB1

#12 sí, es una buena solución lo que has puesto.

También es cierto que se pierde rendimiento al hacer las consultas, no rendimiento de la consulta en sí, pero obviamente si antes de cada consulta primero parseas la cadena pues eso afecta un poco al tiempo de ejecución del script (además del tiempo de llamada a una función, que creo que es despreciable).

Hombre, montar una función para cada posible caso me parece como poco una locura máxima xDD ¿que quieres exactamente hacer de forma eficiente? la función que yo he puesto solo es por temas de seguridad. Por un lado ejecuta un mysql_real_escape_string() a cada variable y por otro, si en un campo el tipo es entero, te aseguras que no metan nada que no sea un entero, y viceversa.

DarkSoldier

explica paso por paso la funcion db_query xDDDDDD

1 respuesta
NeB1

#14 La había hecho mal y además me había dejado una cosa, pongo una versión corregida y comentada, y he quitado el preg_replace porque he visto que tampoco hacía falta y justo el preg_replace es algo que realentiza bastante la ejecución.

New version xD

<?php
function db_query() 
{
        // Pillo los argumentos de la función (pueden ser tantos como querais)
        $args = func_get_args();
        // Quito el primero que es la consulta mysql, el resto son las variables.
	$query = array_shift($args);
        // Hago un mysql_real_escape_string a cada variable, y hago un vsprintf que es igual que un sprintf pero  pasandole un array de parámetros y luego ejecuto la consulta y la devuelvo. 
        return mysql_query(vsprintf($query, array_map('mysql_real_escape_string',$args)));
}

// Usamos el típico formato de printf. %s para variables de tipo string, %d de tipo entero, etc. esto hace que si en el segundo parámetro, en lugar de haber un número como queremos inyectan una cadena, la sustituiria por un valor entero (0)
$query = db_query("SELECT * FROM TABLA WHERE `columna`= '%s' AND `columna2` = '%d' DESC", "hola", 3);
?>
2 1 respuesta
bLaKnI

Flipa con el "func_get_args"...
No tenia ni la mas remota idea joder! :O

1 respuesta
Diabloskuro

#1 Grande, a favoritos va :D

1 respuesta
DarkSoldier

yo sabia que hacia pero no sabia darle ninguna funcion xDD

oye neb1, lo empezaria yo pero no se si soy el mas indicado, crear un post con funciones "interesantes" propias ?? del palo como la que has puesto y bueno, si pueden ser comentadas mejor asi aprendemos todos de todos un poco!

2 1 respuesta
Nazari

#16 Yo había hecho funciones en las que el numero de variables no era fijo y era una putada montarlas, pero es que con esa función es mucho mas fácil, oye neb1 te importa que incluya la función en el desarrollo del CMS? Por supuesto con la referencia de que es tuya, pero es que me parece muy útil, actualmente tengo el tutorial desarrollado hasta el sistema de login, pero puedo incluirla en adelante si me das permiso.

#17 Muchas gracias!

1 respuesta
Igneus

#15
Almacenas en un vector -> ($args), los argumentos que le pasas a una funcion sin argumentos predefinidos.

Almacenas el primer elemento de ($args) en la cadena ($query) y le eliminas del vector ($args).

A cada elemento del vector ($args), le aplicas la funcion mysql_real_escape_string() (¿¿que es como un addslashes() pero para \x00, \n, \r, \, ', " y \x1a ???)

Das formato con la función vsprintf('formato', array) pasandole como vector -> ($args) ya chequeado con la funcion, y de formato -> ($query).

Haces la consulta con lo que sale.

Bien, tengo empolvado mi php y he tenido que escribirlo y mirar algunas funciones pero lo he pillao xD. Esta bien, es original yo por lo menos no lo habia visto nunca.

Buen post. Todo lo que sea enseñar algo es bien recibido :D, nunca se sabe demasiado de algo.

NeB1

Esta muy bien el post, todos los que se dedican al desarrollo web, yo creo que alguna vez en su vida les toca desarrollar un CMS, y algunas técnicas que comenta aquí nuestro compañero, son muy interesantes.

#18 como veas xD yo aportaré lo que sepa, por aquí por el foro hay gente muy crack que puede aportar cosas muy interesantes..
#19 jajaja, no hace falta hombre, no le he puesto patente todavía. Seguro que si lo piensas un rato se te ocurren formas de mejorar esa función!

7 días después
Nazari

Siento haber desparecido estos días! Ya sabéis... el trabajo, estudios, parienta, proyectos y mil y una mas responsabilidades que nos exprime el tiempo, y el poco que me queda no me han dado muchas ganas de hacer nada la verdad, bueno sigo con otro post mas.


A estas alturas ya tenemos instalado nuestro CMS y preparada la mecánica de nuestro entorno modular, ademas en la instalación definimos un único usuario, que hará de administrador, pero ¿que hacemos si queremos permitir registrar mas usuarios? Pues haremos un sistema de registro :P .

Anteriormente creamos una script en includes/ llamada register.php, nos dirigimos a ella y empezamos a preparar nuestro sistema de registro.

La script register.php va a tener dos partes, una de ellas es el formulario de registro en xhtml y la otra parte servirá para procesar el envío, para esto usaremos if().
Yo estoy a favor de usar todas las script que necesite de forma que las llames cuando te hagan falta siempre dentro de un limite, pero en este caso crear dos archivos separados para un registro no lo veo realmente útil.

Dentro de script.php y siempre debajo de

if (!defined('CAPRICORN')){
  header('Location: index.php');
}

comenzamos nuestro script, siempre debajo porque lo primero que se debe de comprobar es que la constante esta definida para evitar problemas de seguridad.

Añadimos un if() muy simple que controle el valor ‘submit‘ que entra por POST, si $POST['submit'] existe procesamos el envío, si no es así presentamos el formulario de registro, seria algo así:

if(isset($_POST['submit'])){
 
  //Ahora procesamos el envio
 
}else{
 
  //Mostramos el formulario
 
}

Ahora dentro del if() en la primera parte que es la que se muestra si se envió el formulario añadimos diferentes parámetros que usaremos de requisitos para el registro, yo lo usare para controlar el máximo y el mínimo de caracteres del usuario y contraseña, pero hay muchas mas posibilidades dependiendo de lo que quieras controlar, también habría que comprobar si el email es valido mediante expresiones regulares, pero por el momento lo dejamos así.

  $user_min_len = 4;
  $user_max_len = 32;
  $pass_min_len = 6;
  $pass_max_len = 32;

Una vez definidas las variables pasamos a comprobar que al enviar el formulario se rellenaron todos los campos y que ademas de estar rellenos cumplen con nuestros requisitos, para eso usaremos las funciones empty() y la función strlen() la cual cuenta el numero de caracteres, y en caso de no cumplir lo que buscamos definimos la variable $return = 1. Algo así:

  //Comprobamos que el campo usuario no este vacio.
  if(!empty($_POST['user_name'])){
 
//Comprobamos que cumplan los requisitos que definimos
if((strlen($_POST['user_name']) < $user_min_len)||(strlen($_POST['user_name']) > $user_max_len)){
  echo "El usuario no puede ser menor de ".$user_min_len." ni mayor de ".$user_max_len." letras. <br />";
  $return = 1;
}
  }else{
 
//En caso de que este vacío
echo "Tienes que rellenar el campo de usuario <br />";
$return = 1;
 
  }

Debemos de hacer igual pero para el campo password, que sera el que almacene la contraseña, y para el campo email, pero en el caso del correo electrónico solamente comprobaremos que no esta vacío.

Lo siguiente sera comprobar que el nombre de usuario no este ya registrado, asi evitamos errores con la base de datos, para ello hacemos una consulta a la base de datos de la siguiente forma:

  //Comprobamos en la base de datos que no existe ya el usuario
  $sql = mysql_query("SELECT * FROM users WHERE user='".$_POST['user_name']."'");
  if($user_ok = mysql_fetch_array($sql)) //si existe pedimos uno nuevo.
  {
      echo "El usuario ya existe <br />";
      $return = 1;
  }

La ultima comprobación que hacemos aquí es si la variable $return fue definida, puesto que si se definió significa que hubo alguna incidencia, si no la hubo registramos al usuario, pasamos por md5() la contraseña y terminamos con el procesos del formulario.

//Nos aseguramos de que la variable return no se haya definido para poder continuar
  if(isset($return)){
    echo "<br /><a href=\"index.php?sec=registro\">Volver al Formulario</a>";
  }else{
 
//Introducimos los datos del nuevo usuario en la base de datos
mysql_query("INSERT INTO users (user, pass, email, date, status) values ('".$_POST['user_name']."','".$_POST['password']."','".$_POST['email']."','".time()."','1')")or die(mysql_error());
echo "Usuario registrado correctamente";
echo "<br /><a href=\"index.php\">Volver al Indice</a>";
  }

Ya podemos olvidarnos de esta parte del script, ahora nos centramos en el formulario, el cual ha de ser muy simple, solamente pedimos usuarios, contraseña y email, y enviamos el resultado a index.php?sec=registro, este podría ser el resultado final de nuestro script de registro:

register.php:

<?php
 
//Si el script se ejecuta de forma separada a index.php redireccionamos al index.
if (!defined('CAPRICORN')){
  header('Location: index.php');
}
 
//Comprobamos que se envió el fomulario
if(isset($_POST['submit'])){
 
  //Definimos requisitos de maximo y minimo de usuario y contraseña
  $user_min_len = 4;
  $user_max_len = 32;
  $pass_min_len = 6;
  $pass_max_len = 32;
 
  //Comprobamos que el campo usuario no este vacio.
  if(!empty($_POST['user_name'])){
 
//Comprobamos que cumplan los requisitos que definimos
if((strlen($_POST['user_name']) < $user_min_len)||(strlen($_POST['user_name']) > $user_max_len)){
  echo "El usuario no puede ser menor de ".$user_min_len." ni mayor de ".$user_max_len." letras. <br />";
  $return = 1;
}
  }else{
 
//En caso de que este vacio
echo "Tienes que rellenar el campo de usuario <br />";
$return = 1;
 
  }
 
  //Comprobamos que el campo contraseña no este vacio
  if(!empty($_POST['password'])){
 
//Comprobamos que cumplan los requisitos que definimos
if((strlen($_POST['password']) < $pass_min_len)||(strlen($_POST['password']) > $pass_max_len)){
  echo "La Contraseña no puede ser menor de ".$user_min_len." ni mayor de ".$user_max_len." letras. <br />";
  $return = 1;
}
  }else{
 
//En caso de que este vacio
echo "Tienes que rellenar el campo de Constraseña <br />";
$return = 1;
 
  }
 
  //Comprobamos que el campo email este vacio
  if(empty($_POST['email'])){
 
//En caso de que este vacio
echo "Tienes que rellenar el campo de Email <br />";
$return = 1;
 
  }
 
  //Comprobamos en la base de datos que no existe ya el usuario
  $sql = mysql_query("SELECT * FROM users WHERE user='".$_POST['user_name']."'");
  if($user_ok = mysql_fetch_array($sql)) //si existe pedimos uno nuevo.
  {
      echo "El usuario ya existe <br />";
      $return = 1;
  }
 
 
  //Nos aseguramos de que la variable return no se haya definido para poder continuar
  if(isset($return)){
    echo "<br /><a href=\"index.php?sec=registro\">Volver al Formulario</a>";
  }else{
 
//Introducimos los datos del nuevo usuario en la base de datos
mysql_query("INSERT INTO users (user, pass, email, date, status) values ('".$_POST['user_name']."','".$_POST['password']."','".$_POST['email']."','".time()."','1')")or die(mysql_error());
echo "Usuario registrado correctamente";
echo "<br /><a href=\"index.php\">Volver al Indice</a>";
  }
 
 
}else{  //Mostramos el formulario de envio
 
  ?>
  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
      <link type="text/css" rel="stylesheet" media="all" href="style.css" />
      <title>Registrar nuevo Usuario</title>
    </head>
    <body>
    <div id="install">
      <form action="index.php?sec=registro" method="post">
      <div id="install-title">Registro de Nuevos Usuarios</div>
      <table>
        <tr><td colspan="2" class="install-td">Datos del Usuario</td></tr>
        <tr><td>Nombre de Usuario:</td><td><input type="text" name="user_name" value="Usuario" /></td></tr>
        <tr><td>Contraseña:</td><td><input type="text" name="password" value="Contraseña" /></td></tr>
        <tr><td>Correo Electrónico:</td><td><input type="text" name="email" value="Email" /></td></tr>
        <tr><td colspan="2" class="install-td"><input type="submit" name="submit" value="Registrar" /></td></tr>
      </table>
      </form>
    </div>
    </body>
  </html>
 
  <?
 
}
 
?>

Ahora que tenemos como registrar usuarios, necesitaremos donde hacer ‘Login‘ y ‘Logout‘ del sistema, pero eso lo explicare en otra entrada. Podéis bajar todos los script hasta el momento aquí.

Un Saludo a tod@s!

NeB1

Ya vengo a tocarte las narices XD

En la comprobación del e-mail deberías usar un preg_match para asegurarte que es un e-mail válido, no??

En plan

 if (!preg_match("/^[^@]*@[^@]*\.[^@]*$/", $email)) {
        return "La dirección introducida no es una dirección válida!";       
}

igual la expresión regular que he usado no es la mejor para ello...

Btw, genial como siempre.

Nazari

Buenas NeB1, para nada me tocas las narices aprendo mucho de ti, si puse que 'hay que usar expresiones regulares, pero de momento lo dejamos así' porque la verdad que se esconde tras eso es que no se usar expresiones regulares!! es una de las cosas que nunca he usado, siempre busco alguna chuleta de internet para salir al paso, y nunca he aprendido, como siempre are una revisión próximamente a todo el cms y revisare cambios, fallos y sugerencias que me habéis dicho!

Un Saludo

1 respuesta
Igneus

se te olvido añadir mysql_connect() antes de enviar la consulta a la db.

1 respuesta
Nazari

#25 Buenas Igneus, no hace falta porque viene ya en el index.php, el resto de scripts se llaman mediante un include con la conexión ya abierta.

Un Saludo

Igneus

ahh, ok fallo mio perdona. Pense k actuaba de forma independiente, el index le mire un poco por encima.

NeB1

#24 a es verdad, no lo había visto

SeiYa

Cambia lo de IP y LAST_IP y mete una trabla intermedia que guarde la IP, la id del usuario y la fecha para poder conocer más datos no solo la ip de registro y la última ip de acceso, de esa forma tendrás mucho más control y podrás banear un usuario prohibiendo todas sus IP's o simplemente logs y seguimiento.

1 respuesta
Nazari

#29 Me gusta la idea, ya habia pensaod algo parecido, pero no habia querido complicar mucho el desarrollo, visto lo visto la mejor forma de hacerlo es irlo complicando poco a poco. Un Saludo