Vinculando dos tipos de posts personalizados diferentes

5 may 2014, 15:15:14
Vistas: 20K
Votos: 3

Espero que alguien pueda ayudar con esto ya que es muy frustrante...

Estoy tratando de construir un sitio de propiedades en WordPress y me gustaría vincular dos tipos de posts personalizados -

Uno sería para áreas de desarrollo y el otro para propiedades individuales dentro de esa área de desarrollo

Entiendo que puedo crear un CPT llamado 'Desarrollos' y otro llamado 'Propiedades', pero ¿cómo puedo vincularlos? Por ejemplo, si creo una propiedad y trato de adjuntarla a un desarrollo, ¿cómo funcionaría esto si son tipos de posts personalizados separados?

Leí una pregunta similar - ¿Cómo conectar diferentes CPTs entre sí? y la respuesta de Scuba Kay era casi lo que necesitaba, pero no sé cómo consultar una propiedad específica que pertenece a un determinado Desarrollo

¡Gracias de antemano!

1
Todas las respuestas a la pregunta 3
0
16

Si entiendo lo que estás haciendo, estás buscando establecer una relación uno a muchos entre áreas de desarrollo y propiedades.

Aquí hay una clase que, si se carga y luego se instancia, hará:

  • crear un meta box para un tipo de post llamado development_area que contiene una lista de casillas de verificación de todas las propiedades disponibles para que se pueda actualizar fácilmente la relación entre ambos.
  • crear un meta box para un tipo de post llamado property que contiene un cuadro de selección para elegir el área de desarrollo relacionada para una propiedad.
  • asegurar que exista una relación 1 a muchos entre áreas de desarrollo y propiedades, lo que significa que un área de desarrollo puede estar relacionada con muchas propiedades diferentes, pero cualquier propiedad solo tendrá un área de desarrollo relacionada.

Nota:

Este código ilustra cómo hacer esto. Si bien lo he revisado en busca de errores tontos y verificado la lógica, es posible que tengas que ajustar cosas ya que no lo he probado en tiempo de ejecución. Hago esto todo el tiempo y funciona muy bien.

Después de la clase hay un ejemplo de cómo hacer que funcione y luego un par de ejemplos sobre cómo usar las relaciones que establece la clase.

class One_To_Many_Linker {

    protected $_already_saved = false;  # Usado para evitar guardar dos veces

    public function __construct() {
        $this->do_initialize();
    }

Configuración de los meta boxes y funcionalidad de guardado

    protected function do_initialize() {

        add_action(
            'save_post',
            array( $this, 'save_meta_box_data' ),
            10,
            2
        );

        add_action(
            "add_meta_boxes_for_development_area",
            array( $this, 'setup_development_area_boxes' )
        );

        add_action(
            "add_meta_boxes_for_property",
            array( $this, 'setup_property_boxes' )
        );

    }

Creando nuestro meta box necesario También podrían configurarse fácilmente otros metaboxes aquí.

    # Tipo de post development area
    # - este tipo de post puede tener múltiples posts de propiedades relacionados
    public function setup_development_area_boxes( \WP_Post $post ) {
        add_meta_box(
            'area_related_properties_box',
            __('Propiedades Relacionadas', 'language'),
            array( $this, 'draw_area_related_properties_box' ),
            $post->post_type,
            'advanced',
            'default'
        );
    }

Dibujando las Propiedades Relacionadas Este código dibuja las propiedades relacionadas como una serie de casillas de verificación.

    public function draw_area_related_properties_box( \WP_Post $post ) {

        $all_properties = $this->get_all_of_post_type( 'property' );

        $linked_property_ids = $this->get_linked_property_ids( $post->ID );

        if ( 0 == count($all_properties) ) {
            $choice_block = '<p>No hay propiedades actualmente en el sistema.</p>';
        } else {
            $choices = array();
            foreach ( $all_properties as $property ) {
                $checked = ( in_array( $property->ID, $linked_property_ids ) ) ? ' checked="checked"' : '';

                $display_name = esc_attr( $property->post_title );
                $choices[] = <<<HTML
<label><input type="checkbox" name="property_ids[]" value="{$property->ID}" {$checked}/> {$display_name}</label>
HTML;

            }
            $choice_block = implode("\r\n", $choices);
        }

        # Asegurarse de que el usuario pretendía hacer esto.
        wp_nonce_field(
            "updating_{$post->post_type}_meta_fields",
            $post->post_type . '_meta_nonce'
        );

        echo $choice_block;
    }

Obteniendo listas de posts Esto obtiene todos los posts de un tipo específico. Si usas posts sticky, querrás desmarcar la bandera sticky en el argumento args.

    # Obtener todos los posts del tipo especificado
    # Devuelve un array de objetos post
    protected function get_all_of_post_type( $type_name = '') {
        $items = array();
        if ( !empty( $type_name ) ) {
            $args = array(
                'post_type' => "{$type_name}",
                'posts_per_page' => -1,
                'order' => 'ASC',
                'orderby' => 'title'
            );
            $results = new \WP_Query( $args );
            if ( $results->have_posts() ) {
                while ( $results->have_posts() ) {
                    $items[] = $results->next_post();
                }
            }
        }
        return $items;
    }

Obteniendo IDs de propiedades vinculadas para un área de desarrollo

Dado un ID de área de desarrollo, esto devolverá un array de todos los IDs de posts de propiedades vinculadas.

    protected function get_linked_property_ids( $area_id = 0 ) {
        $ids = array();
        if ( 0 < $area_id ) {
            $args = array(
                'post_type' => 'property',
                'posts_per_page' => -1,
                'order' => 'ASC',
                'orderby' => 'title',
                'meta_query' => array(
                    array(
                        'key' => '_development_area_id',
                        'value' => (int)$area_id,
                        'type' => 'NUMERIC',
                        'compare' => '='
                    )
                )
            );
            $results = new \WP_Query( $args );
            if ( $results->have_posts() ) {
                while ( $results->have_posts() ) {
                    $item = $results->next_post();
                    $ids[] = $item->ID;
                }
            }
        }
        return $ids;
    }

Configuración de los meta boxes de Propiedad

Podrías agregar fácilmente más meta boxes aquí si lo deseas.

    # Configuración de metabox para tipo de post
    public function setup_property_boxes( \WP_Post $post ) {
        add_meta_box(
            'property_linked_area_box',
            __('Área de Desarrollo Relacionada', 'language'),
            array( $this, 'draw_property_linked_area_box' ),
            $post->post_type,
            'advanced',
            'default'
        );
    }

Dibujando el meta box del editor de Propiedad

Esto dibuja un elemento select (cuadro desplegable) que permite al usuario elegir a qué área de desarrollo se relaciona la propiedad. Usamos un cuadro de selección para asegurar que solo se pueda especificar una propiedad, pero una lista de botones de radio también funcionaría.

    public function draw_property_linked_area_box( \WP_Post $post ) {

        $all_areas = $this->get_all_of_post_type('development_area');

        $related_area_id = $this->get_property_linked_area_id( $post->ID );

        if ( 0 == $all_areas ) {
            $choice_block = '<p>No hay áreas de desarrollo para elegir todavía.</p>';
        } else {
            $choices = array();
            $selected = ( 0 == $related_area_id )? ' selected="selected"':'';
            $choices[] = '<option value=""' . $selected . '> -- Ninguno -- </option>';
            foreach ( $all_areas as $area ) {
                $selected = ( $area->ID == (int)$related_area_id ) ? ' selected="selected"' : '';

                $display_name = esc_attr( $area->post_title );
                $choices[] = <<<HTML
<option value="{$area->ID}" {$selected}>{$display_name}</option>
HTML;

            }
            $choice_list = implode("\r\n" . $choices);
            $choice_block = <<<HTML
<select name="development_area_id">
{$choice_list}
</select>
HTML;

        }

        wp_nonce_field(
            "updating_{$post->post_type}_meta_fields",
            $post->post_type . '_meta_nonce'
        );

        echo $choice_block;
    }

El método de vinculación

Nota que establecemos nuestros enlaces configurando una clave _development_area_id en cada propiedad.

  • las áreas de desarrollo pueden consultar propiedades con esta clave para mostrarlas
  • las propiedades pueden extraer sus propios meta o tener su consulta filtrada para extraer datos de desarrollo

    protected function get_property_linked_area_id( $property_id = 0 ) {
        $area_id = 0;
        if ( 0 < $property_id ) {
            $area_id = (int) get_post_meta( $property_id, '_development_area_id', true );
        }
        return $area_id;
    }
    

Guardando los meta datos

Nos esforzamos por solo guardar cuando sea necesario y adecuado. Ver comentarios en el código.

    public function save_meta_box_data( $post_id = 0, \WP_Post $post = null ) {

        $do_save = true;

        $allowed_post_types = array(
            'development_area',
            'property'
        );

        # No guardar si ya hemos guardado nuestras actualizaciones
        if ( $this->_already_saved ) {
            $do_save = false;
        }

        # No guardar si no hay ID de post o post
        if ( empty($post_id) || empty( $post ) ) {
            $do_save = false;
        } else if ( ! in_array( $post->post_type, $allowed_post_types ) ) {
            $do_save = false;
        }

        # No guardar para revisiones o autoguardados
        if (
            defined('DOING_AUTOSAVE')
            && (
                is_int( wp_is_post_revision( $post ) )
                || is_int( wp_is_post_autosave( $post ) )
            )
        ) {
            $do_save = false;
        }

        # Asegurarse de que se está trabajando en el post correcto
        if ( !array_key_exists('post_ID', $_POST) || $post_id != $_POST['post_ID'] ) {
            $do_save = false;
        }

        # Asegurarse de que tenemos los permisos necesarios para guardar [ asume que ambos tipos usan edit_post ]
        if ( ! current_user_can( 'edit_post', $post_id ) ) {
            $do_save = false;
        }

        # Asegurarse de que el nonce y el referente son correctos.
        $nonce_field_name = $post->post_type . '_meta_nonce';
        if ( ! array_key_exists( $nonce_field_name, $_POST) ) {
            $do_save = false;
        } else if ( ! wp_verify_nonce( $_POST["{$nonce_field_name}"], "updating_{$post->post_type}_meta_fields" ) ) {
            $do_save = false;
        } else if ( ! check_admin_referer( "updating_{$post->post_type}_meta_fields", $nonce_field_name ) ) {
            $do_save = false;
        }

        if ( $do_save ) {
            switch ( $post->post_type ) {
                case "development_area":
                    $this->handle_development_area_meta_changes( $post_id, $_POST );
                    break;
                case "property":
                    $this->handle_property_meta_changes( $post_id, $_POST );
                    break;
                default:
                    # No hacemos nada con otros tipos de post
                    break;
            }

            # Notar que hemos guardado nuestros datos
            $this->_already_saved = true;
        }
        return;
    }

Actualizando Propiedades de Desarrollo

Leemos la lista de tipos de post de propiedades marcadas, obtenemos la lista de tipos de post de propiedades relacionadas actualmente y luego usamos esas listas para determinar qué actualizar.

Nota: Aquí estamos actualizando meta datos en tipos de post de propiedades, no en nuestra área de desarrollo editada.

    # Las áreas de desarrollo pueden enlazar a múltiples propiedades
    # pero cada propiedad solo puede enlazar a un área de desarrollo
    protected function handle_development_area_meta_changes( $post_id = 0, $data = array() ) {

        # Obtener los IDs de propiedades vinculadas actualmente para esta área de desarrollo
        $linked_property_ids = $this->get_linked_property_ids( $post_id );

        # Obtener la lista de IDs de propiedades marcadas cuando el usuario guardó cambios
        if ( array_key_exists('property_ids', $data) && is_array( $data['property_ids'] ) ) {
            $chosen_property_ids = $data['property_ids'];
        } else {
            $chosen_property_ids = array();
        }

        # Construir una lista de propiedades para vincular o desvincular de esta área
        $to_remove = array();
        $to_add = array();

        if ( 0 < count( $chosen_property_ids ) ) {
            # El usuario eligió al menos una propiedad para vincular
            if ( 0 < count( $linked_property_ids ) ) {
                # Ya teníamos al menos una propiedad vinculada

                # Recorrer las existentes y notar las que el usuario no marcó
                foreach ( $linked_property_ids as $property_id ) {
                    if ( ! in_array( $property_id, $chosen_property_ids ) ) {
                        # Actualmente vinculada, pero no elegida. Removerla.
                        $to_remove[] = $property_id;
                    }
                }

                # Recorrer las marcadas y notar las que no están actualmente vinculadas
                foreach ( $chosen_property_ids as $property_id ) {
                    if ( ! in_array( $property_id, $linked_property_ids ) ) {
                        # Elegida pero no en las actualmente vinculadas. Agregarla.
                        $to_add[] = $property_id;
                    }
                }

            } else {
                # No hay IDs elegidos previamente, simplemente agregarlos todos
                $to_add = $chosen_property_ids;
            }

        } else if ( 0 < count( $linked_property_ids ) ) {
            # No se eligieron propiedades para vincular. Eliminar todas las vinculadas actualmente.
            $to_remove = $linked_property_ids;
        }

        if ( 0 < count($to_add) ) {
            foreach ( $to_add as $property_id ) {
                # Esto sobrescribirá cualquier valor existente para la clave de vinculación
                # para asegurar que mantengamos solo un área de dev vinculada por cada propiedad.
                update_post_meta( $property_id, '_development_area_id', $post_id );
            }
        }

        if ( 0 < count( $to_remove ) ) {
            foreach ( $to_remove as $property_id ) {
                # Esto eliminará todas las áreas vinculadas existentes para la propiedad
                # para asegurar que solo tengamos un área vinculada por propiedad
                delete_post_meta( $property_id, '_development_area_id' );
            }
        }
    }

Guardando nuestros Cambios de Propiedad

Como nuestra meta clave está en cada propiedad, simplemente actualizamos nuestros meta datos si es necesario. Como leer es casi siempre más rápido en mysql que escribir, solo actualizamos si es absolutamente necesario.

    # Las propiedades solo se relacionan con un área de desarrollo
    protected function handle_property_meta_changes( $post_id = 0, $data = array() ) {

        # Obtener cualquier área de desarrollo vinculada actualmente
        $linked_area_id = $this->get_property_linked_area_id( $post_id );
        if ( empty($linked_area_id) ) {
            $linked_area_id = 0;
        }

        if ( array_key_exists( 'development_area_id', $data ) && !empty($data['development_area_id'] ) ) {
            $received_area_id = (int)$data['development_area_id'];
        } else {
            $received_area_id = 0;
        }

        if ( $received_area_id != $linked_area_id ) {
            # Esto sobrescribirá todas las copias existentes de nuestra meta clave
            # para asegurar que solo tengamos un área vinculada por propiedad
            update_post_meta( $post_id, '_development_area_id', $received_area_id );
        }
    }
}

Cómo Usar La Clase

Siempre que cargues la clase en tu archivo de funciones del tema o en un plugin, puedes usar lo siguiente para que funcione:

if ( is_admin() ) {
    new One_To_Many_Linker();
}

Algunos Casos de Uso Siguientes A continuación, he proporcionado un par de casos de uso en el front end.

  • Mostrando todas las propiedades para el área de desarrollo actual
  • Mostrando el área de desarrollo para una propiedad en un archivo o propiedad única

Mostrando todas las propiedades relacionadas con el área de desarrollo mostrada actualmente

global $wp_query;
$area_id = $wp_query->get_queried_object_id();
$args = array(
    'post_type' => 'property',
    'posts_per_page' => -1,
    'meta_query' => array(
        array(
            'key' => '_development_area_id',
            'value' => $area_id,
            'compare' => '=',
            'type' => 'NUMERIC'
        )
    )
);
$properties = new \WP_Query( $args );
if ( $properties->have_posts() ) {
    while( $properties->have_posts() ) {
        $property = $properties->next_post();
        # hacer algo con la propiedad
        $property_link = get_permalink( $property->ID );
        $property_name = esc_attr( $property->post_title );
        echo '<a href="' . $property_link . '">' . $property_name . '</a>';
    }
}

Mostrando Áreas de Desarrollo Vinculadas

Método 1: Obteniendo el post meta, cargando el área y usando los datos

  • funciona en páginas donde is_singular('property') es verdadero
  • funciona en páginas donde is_post_type_archive('property') es verdadero

global $post;
while ( have_posts() ) {
    the_post();
    $post_id = get_the_ID(); # podría usar $post->ID

    $dev_area_id = get_post_meta( $post_id, '_development_area_id', true);
    if ( !empty( $dev_area_id ) ) {
        $development_area = get_post( $dev_area_id );
        # hacer algo...
        $dev_area_link = get_permalink ( $development_area->ID );
        $dev_area_title = $development_area->post_title;
        $dev_area_content = $development_area->post_content;
        echo '<a href="' . $dev_area_link . '">' . $dev_area_title . '</a><br />' . $dev_area_content;
    }
}

Método 2: Usando un filtro de consulta

  • funciona en páginas donde is_singular('property') es verdadero
  • funciona en páginas donde is_post_type_archive('property') es verdadero

Nota que esto evita tener que extraer el meta del tipo de post y hacer una consulta extra para los datos del área de desarrollo. A medida que muestras más propiedades en una sola página, esto te ahorra más y más poder de procesamiento.

Dónde colocar el siguiente código:

  • En un nuevo plugin que crees recomendado
  • En tu archivo functions.php del tema

add_filter('posts_clauses', 'do_my_maybe_modify_queries', 10, 2);
function do_my_maybe_modify_queries( $pieces, \WP_Query $wp_query ) {

    if ( !is_admin() && $wp_query->is_main_query() ) {
        # No estamos en los paneles de administración y somos la consulta principal para la plantilla

        if ( array_key_exists('post_type', $wp_query->query_vars) ) {
            # Se consultó algún tipo de post.
            # Obtenerlo como un array de los tipos especificados
            $value = $wp_query->query_vars['post_type'];
            if ( !is_array( $value ) ) {
                if ( empty( $value ) ) {
                    $post_types = array();
                } else {
                    $post_types = array( $value );
                }
            } else {
                $post_types = $value;
            }

            if ( in_array('property', $post_types) ) {
                # Se solicitó una propiedad
                if ( $wp_query->is_post_type_archive || $wp_query->is_singular ) {
                    # Mostrando el archivo del tipo de post de propiedad o una propiedad singular.
                    # Queremos agregar nuestro ID de área de desarrollo, título y contenido a los campos devueltos
                    global $wpdb;

                    # Vincular el post de desarrollo a cada propiedad a través de su clave postmeta
                    # Como solo hay 1 área de desarrollo por propiedad, esto funciona bien
                    $pieces['join'] .= <<<SQL
 LEFT JOIN {$wpdb->prefix}postmeta AS dev_pm ON {$wpdb->prefix}posts.ID = dev_pm.post_id AND dev_pm.meta_key = '_development_area_id'
    LEFT JOIN {$wpdb->prefix}posts AS dev_post ON dev_post.ID = dev_pm.meta_value 
SQL;

                    # Agregar nuestros campos deseados de post de desarrollo a los devueltos por la consulta
                    $pieces['fields'] .= ", IFNULL( dev_pm.meta_value, 0 ) as development_area_id";
                    $pieces['fields'] .= ", IFNULL( dev_post.post_title, '') as development_area_title";
                    $pieces['fields'] .= ", IFNULL( dev_post.post_content, '') as development_area_content";

                }
            }
        }
    }
    return $pieces;
}

Con lo anterior en su lugar, ahora puedes acceder a los datos de la siguiente manera en una página de propiedad única o archivo de propiedades. Dejo como ejercicio para el lector hacer esto para taxonomías relacionadas con propiedades.

if ( have_posts() ) {
    global $post;
    while ( have_posts() ) {
        the_post();
        if ( property_exists( $post, 'development_area_id' ) ) {
            $dev_area_id = $post->development_area_id;
            $dev_area_title = $post->development_area_title;
            $dev_area_content = $post->development_area_content;
            $dev_area_link = get_permalink( $dev_area_id );
            echo '<a href="' . $dev_area_link . '">' . $dev_area_title . '</a><br />' . $dev_area_content;
        }
    }
}

Método 3: Filtrando WP_Query

Similar al método de filtro anterior, pero utilizable para consultas personalizadas usando WP_Query. Genial si quieres escribir un shortcode o widget que muestre un montón de propiedades.

Primero, creamos nuestro filtro (muy similar al mostrado en el método 2)

function add_dev_data_to_wp_query( $pieces, \WP_Query $wp_query ) {
    global $wpdb;

    if ( !is_admin() && !$wp_query->is_main_query() ) {
        # Vincular el post de desarrollo a cada propiedad a través de su clave postmeta
        # Como solo hay 1 área de desarrollo por propiedad, esto funciona bien
        $pieces['join'] .= <<<SQL
 LEFT JOIN {$wpdb->prefix}postmeta AS dev_pm ON {$wpdb->prefix}posts.ID = dev_pm.post_id AND dev_pm.meta_key = '_development_area_id'
    LEFT JOIN {$wpdb->prefix}posts AS dev_post ON dev_post.ID = dev_pm.meta_value 
SQL;

        # Agregar nuestros campos deseados de post de desarrollo a los devueltos por la consulta
        $pieces['fields'] .= ", IFNULL( dev_pm.meta_value, 0 ) as development_area_id";
        $pieces['fields'] .= ", IFNULL( dev_post.post_title, '') as development_area_title";
        $pieces['fields'] .= ", IFNULL( dev_post.post_content, '') as development_area_content";
    }
    return $pieces;
}

Luego, aplicamos nuestro filtro antes de crear la consulta y lo eliminamos justo después para asegurarnos de no alterar otras consultas.

$args = array(
    'post_type' => 'property',
    'posts_per_page' => -1
);

# Aplicar nuestro filtro
add_filter('posts_clauses', 'add_dev_data_to_wp_query', 10, 2);

# Ejecutar la consulta
$properties = new \WP_Query( $args );

# Eliminar nuestro filtro
remove_filter('posts_clauses', 'add_dev_data_to_wp_query', 10);

if ( $properties->have_posts() ) {
    while( $properties->have_posts() ) {
        $property = $properties->next_post();

        # Hacer cosas con tus propiedades
        echo '<p>El nombre de la propiedad es ' . $property->post_title . '</p>';

        # Hacer cosas con áreas de desarrollo relacionadas si esos datos están disponibles
        if ( property_exists( $property, 'development_area_id' ) ) {
            echo '<p>Parte del área de desarrollo llamada ' . $property->development_area_title . '</p>';
        }
    }
}

Si bien requiere un poco más de trabajo por adelantado hacer cosas como esta, proporciona un par de beneficios agradables

  • Estás haciendo solo 1 consulta en lugar de 2 a 3 consultas para mostrar información relacionada. Esto se acumula rápido.
  • Una vez que te sientas cómodo con ello, puedes usarlo con relaciones a múltiples objetos que te ahorrarán múltiples consultas adicionales cada una
  • Usando una variación de esto, puedes hacer lo mismo para agregar títulos (y enlaces de vista y edición) a las columnas de listado del editor. Por ejemplo, con el escenario anterior podrías listar el desarrollo para cada propiedad en la lista de propiedades en el admin.

Por supuesto, esto ya es suficientemente largo.

11 oct 2015 01:35:01
0

Puedes usar el fantástico y gratuito plugin Advanced Custom Fields, que te permite crear un campo personalizado de tipo Relación, que relaciona un post con otro post.

De todos modos, antes que nada deberías asegurarte de que realmente necesitas dos tipos de contenido personalizados, y que no puedes usar taxonomías o simplemente campos personalizados. Por ejemplo, una Propiedad individual podría ser un tipo de contenido, y la Zona de Desarrollo a la que pertenece podría ser una Taxonomía o un Campo Personalizado que contenga el nombre de esa Zona de Desarrollo... Básicamente depende de cuánta información necesites guardar sobre ambas Zonas de Desarrollo y Propiedades...

5 may 2014 15:59:51
2

Mantendría las cosas simples y haría que las propiedades dentro de un desarrollo sean entradas hijas de la entrada principal del desarrollo. También podrías usar la taxonomía de categorías estándar y marcar cada propiedad "secundaria" como parte del desarrollo más grande. Solo tendrías que ingresar estos desarrollos como categorías en la taxonomía. De esta manera, mantienes todas las propiedades en una lista larga y fácil de administrar. Puedes incluir o excluir lo que necesites cuando llames a esta lista usando el parámetro de profundidad o la taxonomía.

5 may 2014 16:39:03
Comentarios

Gracias por tu ayuda, ¿cómo haría para que las propiedades encontradas dentro de un desarrollo sean publicaciones hijas de la publicación padre del desarrollo?

Kieran Kieran
6 may 2014 00:50:03

Primero crea la página/publicación padre (desarrollo). Luego añade una nueva página y en el lado derecho del editor deberías ver un cuadro de atributos de página y uno de los campos se llama Padre con un menú desplegable. Utilízalo para seleccionar el desarrollo. ¡Eso debería funcionar!

campatsky campatsky
6 may 2014 18:52:49