Ограничение голосования по IP для easyComm Решено

Дополнение: easyComm

Хотел разместить в Готовые решения — но не хватает рейтинга. ЛожуОставлю тут — если покажется нужным — прошу перенести куда следует.


У клиента система оценки товаров/услуг построена на компоненте — easyComm.
Из-за частых «неположительных» оценках было поручено мне создать нечто, что ограничит голосование на N-ое количество дней. Предмодерация не подошла (как заявили — «предмодерация — говорит о ложных отзывах».

Я создал «это» на основе нескольких подсмотренных в сети кусков. Самое главное что работает)

Не судите строго — первый опыт написания сниппетов.

Создал сниппет — voting_ban:
<?php
// очищаем кеш 
$id = $modx->resource->get('id');
$docObj = $modx->getObject('modResource',$id);// $docObj - объект ресурса, кеш которого надо почистить
$key = $docObj->getCacheKey(); 
$cache = $modx->cacheManager->getCacheProvider($modx->getOption('cache_resource_key', null, 'resource'));
$cache->delete($key, array('deleteTop' => true));
$cache->delete($key);

//Получаем IP посетителя
$ip = $_SERVER['REMOTE_ADDR'];
//Получаем ID текущего ресурса
$id = $modx->resource->get('id');

//текущая дата
$today =  date('Y-m-d');

// сколько дней нельзя голосовать
$day = (isset($day)) ? $day : '7';

//действие при разрешённом голосовании
$unlock = (isset($unlock)) ? $unlock : '';


//вычисляем ПЕРВУЮ дату в периоде в котором нельзя голосовать повторно (вторая дата - текущая дата)
$date = date('Y-m-d',strtotime("-$day day", strtotime(preg_replace('~^(\d+)\.(\d+)\.(\d+)$~', '$3-$2-$1', $today))));

// отбираем записи  IP всех голосовавших с датой публикации больше ПЕРВОЙ для текущего ресурса
$sql = "SELECT C.ip AS ip FROM modx_ec_messages C LEFT JOIN modx_ec_threads D ON C.thread=D.id AND C.date>'$date'
WHERE D.resource =".$id ;
$q = $modx->prepare($sql);
$q->execute(array(0));
$arr = $q->fetchAll(PDO::FETCH_ASSOC);
foreach ($arr as $arr_one) {
    $tmpip = $arr_one["ip"];
    $allip[] = $tmpip;
}
//сравниваем IP посетителя с полученными 
 if (in_array("$ip", $allip)) {
    //Действие если запрещено
}
else{
    //Действие если разрешено
    print $unlock;
}
Параметры:
&unlock — действие при разрешённом голосовании (я поставил вызов формы для голосования)
&day — Количество дней в течении которого нельзя повторно голосовать

Я использую так:
[[!voting_ban? &unlock=`[[!ecForm?]]` &day=`7`]]

Надеюсь кому нибудь пригодится)

ps. Если профессионалы найдут ошибки, либо просто подскажу как улучшить/сократить код — буду рад)

UPD:
Всё что делает сниппет можно было прочесть по комментариям в коде, но продублирую (по просьбе трудящихся)
1) Определяем IP посетителя,
2) На основании параметра &day (по умолчанию 7 дней) вычисляем период в течении которого запрещено голосовать
3) Получаем список IP которые участвовали в голосовании в данный период времени для данного ресурса
4) Сравниваем текущий IP с выборкой — если не находим — показываем форму для голосования &unlock
03 сентября 2016, 00:07    Сергей   
0    287 0

Комментарии (8)

  1. Сергей Шлоков 03 сентября 2016, 08:46 # 0
    Зачем это нужно понятно из названия. А что делает данный сниппет? Потенциальный пользователь данного решения должен копаться в коде, чтобы понять?
    И ещё вопрос.
    Ложу тут — если покажется нужным — прошу перенести куда следует.
    В данном предложении как правильно читать — лОжу или ложУ?

    П.С. Данное решение никак не тянет на готовое.
    1. Сергей 03 сентября 2016, 09:25 # 0
      Ок, пусть лежит тут — думаю всё же кому нибудь пригодится
    2. Сергей 03 сентября 2016, 09:28 # 0
      оффтоп
      В данном предложении как правильно читать — лОжу или ложУ?
      Как удобно так и читайте.
      1. Сергей Шлоков 03 сентября 2016, 09:32 # 0
        Если вам интересно, могу объяснить почему данное решение вряд ли кто будет использовать.
        1. Сергей 03 сентября 2016, 09:39 # 0
          Если Вас не затруднит
          1. Сергей Шлоков 03 сентября 2016, 10:44 # 0
            Смотрите, Сергей, если задача — разрешить добавлять комментарий 1 раз в n дней, то решается она несколькими строчками.
            Вариант 1. Через сниппет как у вас.
            $sql = "SELECT 1 FROM modx_ec_messages C LEFT JOIN modx_ec_threads D ON C.thread=D.id
            WHERE D.resource = {$modx->resource->id} AND C.date>DATE_SUB(NOW(),INTERVAL {$day} DAY) AND ip = ?" ;
            $stmt = $modx->prepare($sql);
            $stmt ->execute($_SERVER['REMOTE_ADDR']);
            if ($stmt->rowCount() ) {
            	// Запрещено
            	return '';
            }
            // Выполняем указанные сниппет
            return $modx->runSnippet($snippet, $scriptProperties);
            
            Ну и вызывать так
            // Вместо unlock указываем snippet и любые другие параметры указанного сниппета
            [[!voting_ban? &snippet=`ecForm` &day=`7`]]
            
            Вариант 2. Через плагин.
            Если у этого компонента есть событие добавления комментария по аналогии с Tickets, то форма показывается, но при добавлении комментария плагин проверяет возможность добавления.

            Вот как минимум 2 варианта.
            П.С. Ещё было бы логично проверять почту. У пользователя ip может быть динамическим. А лучше разрешать оставлять отзыв только авторизованным пользователям. Тогда ещё проще.
            1. Сергей 03 сентября 2016, 10:55 # 0
              Спасибо за объяснение. Повторюсь — писал впервые из нескольких кусков.
              1. Сергей Шлоков 03 сентября 2016, 11:31 # +1
                писал впервые из нескольких кусков
                Это очень заметно. Чисто для понимания давайте пробежимся по коду.
                // очищаем кеш
                Зачем?
                П.С. Вообще это можно сделать непрограммно — просто создавать некэшированные страницы. Но в данном случае не понятно, зачем это нужно.

                //Получаем ID текущего ресурса
                $id = $modx->resource->get('id');
                Зачем? В первой строчке уже получали.

                //текущая дата
                $today = date('Y-m-d');
                Зачем? В strtotime и так по умолчанию берется текущая дата.

                $date = date('Y-m-d',strtotime("-$day day", strtotime(preg_replace('~^(\d+)\.(\d+)\.(\d+)$~', '$3-$2-$1', $today))));
                Это вообще что за конструкция? Явно откуда-то скопирована без понимания. Хватило бы
                $date = date('Y-m-d',strtotime("-$day day"));
                Но и это лишнее, так как в mySql есть готовые функции для работы с датами.

                $tmpip = $arr_one[«ip»];
                $allip[] = $tmpip;
                А почему нельзя было написать так $allip[] = $arr_one[«ip»]?

                if (in_array("$ip", $allip)) {
                $ip нужно указывать без кавычек.

                print $unlock;
                Возврат значений из сниппета должен быть через return.

                Явно есть над чем поработать.
        Вы должны авторизоваться, чтобы оставлять комментарии.