Вы не зашли.
а дату хранить в формате datetime (либо int), при insert'е туда просто писать now() (либо time() из php)
Если написать echo now(); то я вижу
Fatal error: Call to undefined function now() in Z:\home\c2.ru\www\engine\plugins\rating\rating.php on line 44
Нету такой функции
Походу версии PHP не сошлись, а time() - естественно работает, его и заюзаю.
Wolverine, вот по моему это мне и нужно
WHERE `lasttime` < '.$tm.';'
Я просто не знал что так, с условием "больше меньше или равно", можно сделать запрос к базе данных!
Попробую реализовать...
На счет "<?php" и "?>" - убедили, учту на будущее, спасибо!
Imho, правильней хранить данные о каждом голосе.
newsID, userID, IP, voteDate
для незарегенного юзера userID будет нулевым.
тогда и проблема очистки будет решаться намного проще и конфликтов вида "двое нажали одновременно на <голосовать>, но только один из голосов появился в таблице" (если несколько человек сидят на одном сайте за NAT'ом) не будет
Вот, озадачил... У меня просто возможность регистрации запрещена, по этому я не обращаю внимание на залогиненность юзера
Допустим.. Если мы храним в voteDate timestamp.
Если я правильно понял - в таблице мы имеем такие строки(timestamp и IP - для наглядного примера):
______________________________________
| newsID | userID | IP | voteDate |
| 45 0 127.0.0.1 789000001 |
| 49 0 127.0.0.1 789000004 |
| 41 67 127.0.0.1 789000007 |
| 41 0 127.0.0.8 789000008 |
|_____________________________________|
Хорошо, допустим я сделал скрипт, который сохраняет инфу в БД о каждом голосе, как я показал выше..
Если, например условно считать что timestamp равный 789000008 - это сегодня и сейчас, а 789000004 - это ровно 2 месяца назад, соответственно 789000001 - это более 2-х месяцев назад, 789000007 - это около месяца.
Скрипт должен найти и удалить все строки с меньшим или равным 789000004 timestamp в поле voteDate.
Вопрос: как составить такой запрос к БД??
Без него проще...
Сэкономить лишние 2 символа в принципе тоже правильно, хоть и бессмысленно
Ладно, с этим разобрались.
Для IP подойдет varchar(15), а для News_ID text должно хватить...
text точно хватит? Даже если, допустим при 1000 новостей, у меня просто голова кругом от этих типов данных
Добавить ещё одно поле date куда при добавлении будет добавляться время и сравнивать разницу и по ней удалять, но при твоей структуре с этим проблематично получится)
А как по другому? Так вроде бы оптимальнее всего, но я согласен, придется всё переписывать, и создавать новые поля, и куда-нибудь вставлять даты. Надо подумать...
Я думаю лучше добавить кнопку чтобы можно было нажать и вся таблица очиститься)
А кнопку нажимать после того как сервер упадет от высокой нагрузки, и хостер забанит? Шучу я конечно
Нет, надо как-то автоматически это реализовать.
Поле date.. это мысль конечно. Синхронно с ID новости в другое поле через разделитель писать timestamp или сразу дд-мм-гггг? И синхронно читать? А размер БД увеличится в таком случае, это же сколько timestamp`ов придется писать в таблицу...
Если делать так, то как связать IP+ID с датой?
А как считать, а как сравнить даты чтобы узнать что прошло больше 2-х месяцев, я не пойму.
Буду думать, может еще кто что подскажет
Ах да, забыл.. Все строки return "ok"; из функции ipcheck() можно удалить, оно не надо
Так, ну это я делал для себя, а теперь для сообщества
Вот цивильный, модифицированный rating.php:
(для использования, просто замените содержимое rating.php на это)
<?php
// Protect against hack attempts
if (!defined('NGCMS')) die ('HAL');
/* проверка IP - START */
function ipcheck ($post_id, $upd) {
global $ip, $mysql;
/* Сначала ищем IP в базе, голосовал-ли юзер вообще */
if($mysql->record("select ip from ".prefix."_rating where ip = '".$ip."'")) {
/* IP найден > Далее ищем ID новости в базе, по найденному IP */
if($mysql->record("select ip from ".prefix."_rating where ip = '".$ip."' and (locate('|".str_replace("'", "", $post_id)."|', News_ID)>0)")) {
return "fail";
}
else { /* ID новости не найден, юзер с этим IP голосует за эту новость */
if($upd){$mysql->query("update ".prefix."_rating set News_ID=concat(News_ID, '".str_replace("'", "", str_replace("'", "", $post_id))."|') where IP = '".$ip."'");}
return "ok";
}
}
else { /* Юзер еще ни разу не голосовал, его IP не найден. Создаем новую строку в таблице с его IP и новостью */
if($upd){$mysql->query("INSERT INTO ".prefix."_rating set ip='".$ip."', News_ID = '|".str_replace("'", "", $post_id)."|'");}
return "ok";
}
}
/* проверка IP - END */
function plugin_rating_update(){
global $mysql, $tpl, $userROW;
LoadPluginLang('rating', 'site');
// Security protection - limit rating values between 1..5
$rating = intval($_REQUEST['rating']);
$post_id = intval($_REQUEST['post_id']);
if (($rating <1)||($rating >5)) {
return 'incorrect rating';
}
// Check if referred news exists
if (!is_array($row = $mysql->record("select * from ".prefix."_news where id = ".db_squote($post_id)))) {
return 'referred news not found';
}
// Check if we try to make a duplicated rate
if ($_COOKIE['rating'.$row['id']] || ipcheck(db_squote($post_id), 0)=="fail") // изменение 1
return 'you already made your rate';
// Check if we feet "register only" limitation
if (extra_get_param('rating','regonly') && !is_array($userROW)) {
return 'only registered users can rate news';
}
// Ok, everything is fine. Let's update rating.
@setcookie('rating'.$post_id, 'voted', (time() + 31526000), '/');
$mysql->query("update ".prefix."_news set rating=rating+".$rating.", votes=votes+1 where id = ".db_squote($post_id));
$data = $mysql->record("select rating, votes from ".prefix."_news where id = ".db_squote($post_id));
ipcheck (db_squote($post_id), 1); // изменение 2
$localskin = extra_get_param('rating', 'localskin');
if (!$localskin) $localskin='basic';
$tpath = locatePluginTemplates(array('rating', ':rating.css'), 'rating', extra_get_param('rating', 'localsource'), $localskin);
register_stylesheet($tpath['url::rating.css'].'/rating.css');
$tvars['vars']['tpl_url'] = $tpath['url::rating.css'];
$tvars['vars']['home'] = home;
$tvars['vars']['rating'] = ($data['rating'] == 0) ? 0 : round(($data['rating'] / $data['votes']), 0);
$tvars['vars']['votes'] = $data['votes'];
$tpl -> template('rating', $tpath['rating']);
$tpl -> vars('rating', $tvars);
return $tpl -> show('rating');
}
function rating_show($newsID, $rating, $votes){
global $tpl, $userROW;
LoadPluginLang('rating', 'site');
$localskin = extra_get_param('rating', 'localskin');
if (!$localskin) $localskin='basic';
$tpath = locatePluginTemplates(array('rating', 'rating.form', ':rating.css'), 'rating', extra_get_param('rating', 'localsource'), $localskin);
register_stylesheet($tpath['url::rating.css'].'/rating.css');
$tvars['vars']['tpl_url'] = $tpath['url::rating.css'];
$tvars['vars']['home'] = home;
$tvars['vars']['ajax_url'] = generateLink('core', 'plugin', array('plugin' => 'rating'), array());
$tvars['vars']['post_id'] = $newsID;
$tvars['vars']['rating'] = (!$rating || !$votes) ? 0 : round(($rating / $votes), 0);
$tvars['vars']['votes'] = $votes;
if ((isset($_COOKIE['rating'.$newsID]) && $_COOKIE['rating'.$newsID]) || (extra_get_param('rating','regonly') && !is_array($userROW)) || ipcheck($newsID, 0)=="fail") { // изменение 3
// Show
$tpl -> template('rating', $tpath['rating']);
$tpl -> vars('rating', $tvars);
return $tpl -> show('rating');
} else {
// Edit
$tpl -> template('rating.form', $tpath['rating.form']);
$tpl -> vars('rating.form', $tvars);
return $tpl -> show('rating.form');
}
return;
}
function plugin_rating_screen(){
global $SUPRESS_TEMPLATE_SHOW, $template;
@header('Content-type: text/html; charset="windows-1251"');
if ($_REQUEST['post_id']) {
$template['vars']['mainblock'] = plugin_rating_update();
$SUPRESS_TEMPLATE_SHOW = 1;
} else {
$template['vars']['mainblock'] = 'unsupported action';
}
}
//
// ”Ё«мва ®ў®б⥩ (¤«п Ї®Є § ३⨣ )
//
class RatingNewsFilter extends NewsFilter {
function showNews($newsID, $SQLnews, &$tvars, $mode = array()) {
global $tpl, $mysql, $userROW;
$tvars['vars']['plugin_rating'] = rating_show($SQLnews['id'],$SQLnews['rating'],$SQLnews['votes']);
}
}
register_filter('news','raing', new RatingNewsFilter);
register_plugin_page('rating','','plugin_rating_screen',0);
Тестировал на NG CMS 0.9.3 Release Candidate 0 [SVN834], но и в 0.9.2 у меня тоже всё работает
Если кому не трудно, протестируйте на хосте, а то на локалке не интересно!! И киньте ссылку пожалуйста, у меня IP динамический, я потестирую тоже, с отключенными куками.
--------------------------
А теперь вопросы...
Во первых, почему в файле <?php в конце не закрыт(?>), это так задумано, или просто забыли? На сколько я знаю, <?php закрывать не обязательно, но надо-же, наверное, для цивильности
Во вторых, вопрос про БД... Я конечно извиняюсь, но какие типы данных использовать для полей IP и News_ID, если учитывать что в IP будет храниться IP, а в News_ID может храниться длиннющий список идентификаторов новостей с разделителями? (прикрепил скриншот)
И третий вопрос, один из самых важных, как организовать удаление старых записей из БД? Если юзер проголосовал, ему ставится печенька на год, я правильно понял? Так я думаю за год в базе скопится слишком много, СЛИШКОМ МНОГО IP-адресов с идентификаторами... Так-что я думаю, месяца 2-3 вполне хватит, потом ID у этого IP можно удалять, а если кончились ID, то и IP мы удаляем. Мысль ясна?
Как это реализовать - непонятно.. Помогите!
Далее, я конечно дико извиняюсь за то что не в тему, но после чистой установки NG CMS 0.9.3 Release Candidate 0 [SVN834] на денвер, сразу заметил несколько багов..
1) Внизу в админке не работают кнопки добавить новость и редактировать
2) Ссылки Сделать стартовой | Добавить в избранное в опере 11.51 не работают, но у меня есть скрипт для этого дела, работает в осле, лисе, опере, но в хроме не работает, а просто выводит алерт с текстом "нажмите ctrl+d", если что - могу найти
3) В админке при добавлении новости, или при добавлении коммента на сайте смайлы не вставляются, хотя ББ-коды вставляются нормально
У меня в 0.9.2 смайлы и бб-коды тоже отвалились почему-то только в комментах, я отключил комменты, и заюзал социальный плагин "комментарии" от вконтакте, и нормально
Вот так..
Добавь global $ip; И тогда не придется писать $_SERVER['REMOTE_ADDR'], а просто $ip
Не знал, исправлю
Индексы добавил на News_ID, IP? И хорошо бы очищать эту таблицу от старых IP чтобы не перегружать.
Я об этом тоже подумал, надо это обязательно как-то реализовать, но как - пока непонятно...
Wolverine, да, через разделитель. При первом запросе с нового IP, функция создает в индексе
IP=127.0.0.1, News_ID=|24| где 24 - id новости, за который голосует юзер с IP 127.0.0.1, т.е. добавляет в ячейку News_ID идентификатор новости с символами "|" по бокам - |24|.
А если же юзер с этим-же IP голосует за другую новость, информация в таблице обновляется таким образом - добавляется ID текущей новости, и символ "|" только справа 25| и получается |24|25|26|139|4| и т.д.
И по этому в таблице все ID новостей будут иметь по бокам эти символы "|" - |139|, |25|, и т.д.
Это как-бы справка для всех новых посетителей форума, чтобы было понятно как это работает, может кому пригодится.
С разделителем придумал сам, а вот решение с locate и concat я нагло слизал с какого-то сайта, и даже не допер до конца зачем оно
Пока сам не сделаешь - никто не поможет
Короче, я создал таблицу с названием ng_rating, в ней создал две строки, IP и News_ID.
Чуть-чуть подшаманил файл rating.php, теперь он выдает в шаблон что-то подобное: Рейтинг: 82%. 42 голоса. Изменения слов - голоса, голосов, голос, это я реализовал прямо в rating.php, ключевым моментом является функция ipcheck, которая принимает 3 параметра ($mysql, $post_id, $upd), где $upd: параметр 0 - изменения в БД не вносятся, а только проверяется наличие IP в базе, и проверяется голосовал ли юзер за эту новость;
параметр 1 - всё так-же проверяется, но если юзер не голосовал - в БД вносится его IP и ID новости, и в следующий раз он проголосовать не сможет, даже с отключенными куками.
Функция возвращает 2 параметра:
fail - юзер уже голосовал за определенную новость
ok - юзер не голосовал за эту новость, хотя походу этот параметр я зря создал, используется только "fail" хз
Вот сам rating.php как он выглядит у меня:
<?php
// Protect against hack attempts
if (!defined('NGCMS')) die ('HAL');
function ruslang($votes) {
$votes2=substr($votes, strlen($votes)-1,1);
if($votes=="11" ||$votes=="12" ||$votes=="13" ||$votes=="14" || $votes2=="0" || $votes2=="5" ||
$votes2=="6" || $votes2=="7" || $votes2=="8" || $votes2=="9"){$golosov="голосов";}
elseif($votes2=="1"){$golosov="голос";}
elseif($votes2=="2" || $votes2=="3" || $votes2=="4"){$golosov="голоса";}
if($votes=="0"){$votes="Нет";}
return $votes." ".$golosov;
}
function ipcheck ($mysql, $post_id, $upd) {
/* Сначала ищем IP в базе, голосовал-ли юзер вообще */
if($mysql->record("select ip from ".prefix."_rating where ip = '".$_SERVER['REMOTE_ADDR']."'")) {
/* IP найден > Далее ищем ID новости в базе, по найденному IP */
if($mysql->record("select ip from ".prefix."_rating where ip = '".$_SERVER['REMOTE_ADDR']."' and (locate('|".str_replace("'", "", $post_id)."|', News_ID)>0)")) {
return "fail";
}
else { /* ID новости не найден, юзер с этим IP голосует за эту новость */
if($upd){$mysql->query("update ".prefix."_rating set News_ID=concat(News_ID, '".str_replace("'", "", str_replace("'", "", $post_id))."|') where IP = '".$_SERVER['REMOTE_ADDR']."'");}
return "ok";
}
}
else { /* Юзер еще ни разу не голосовал, его IP не найден. Создаем новую строку в таблице с его IP и новостью */
if($upd){$mysql->query("INSERT INTO ".prefix."_rating set ip='".$_SERVER['REMOTE_ADDR']."', News_ID = '|".str_replace("'", "", $post_id)."|'");}
return "ok";
}
}
$show=0; // забыл зачем это нужно xD
function plugin_rating_update(){ // начинается функция апдейта рейтинга
global $mysql, $tpl, $userROW;
LoadPluginLang('rating', 'site');
// Security protection - limit rating values between 1..100 :)
$rating = intval($_REQUEST['rating']);
$post_id = intval($_REQUEST['post_id']);
if (($rating <1)||($rating >100)) {
return 'incorrect rating';
}
// Check if referred news exists
if (!is_array($row = $mysql->record("select * from ".prefix."_news where id = ".db_squote($post_id)))) {
return 'Referred news not found';
}
// Check if we try to make a duplicated rate
if ($_COOKIE['rating'.$row['id']] || ipcheck($mysql, db_squote($post_id), 0)=="fail")
return 'Вы уже голосовали!';
// Check if we feet "register only" limitation
if (extra_get_param('rating','regonly') && !is_array($userROW)) {
return 'only registered users can rate news';
}
// Ok, everything is fine. Let's update rating.
@setcookie('rating'.$post_id, 'voted', (time() + 31526000), '/');
ipcheck ($mysql, db_squote($post_id), 1);
$data = $mysql->record("select rating, votes from ".prefix."_news where id = ".db_squote($post_id));
$mysql->query("update ".prefix."_news set rating=rating+".$rating.", votes=votes+1 where id = ".db_squote($post_id));
$localskin = extra_get_param('rating', 'localskin');
if (!$localskin) $localskin='basic';
$tpath = locatePluginTemplates(array('rating', ':rating.css'), 'rating', extra_get_param('rating', 'localsource'), $localskin);
register_stylesheet($tpath['url::rating.css'].'/rating.css');
$tvars['vars']['tpl_url'] = $tpath['url::rating.css'];
$tvars['vars']['home'] = home;
$tvars['vars']['rating'] = (($data['rating']+$rating) == 0) ? 0 : round((($data['rating']+$rating) / ($data['votes']+1)), 0);
$tvars['vars']['votes']=ruslang(($data['votes']+1)); // супер-функция
$tpl -> template('rating', $tpath['rating']);
$tpl -> vars('rating', $tvars);
return $tpl -> show('rating');
}
function rating_show($newsID, $rating, $votes, $show){
global $mysql, $tpl, $userROW;
LoadPluginLang('rating', 'site');
$localskin = extra_get_param('rating', 'localskin');
if (!$localskin) $localskin='basic';
$tpath = locatePluginTemplates(array('rating', 'rating.form', ':rating.css'), 'rating', extra_get_param('rating', 'localsource'), $localskin);
register_stylesheet($tpath['url::rating.css'].'/rating.css');
$tvars['vars']['tpl_url'] = $tpath['url::rating.css'];
$tvars['vars']['home'] = home;
$tvars['vars']['ajax_url'] = generateLink('core', 'plugin', array('plugin' => 'rating'), array());
$tvars['vars']['post_id'] = $newsID;
$tvars['vars']['rating'] = (!$rating || !$votes) ? 0 : round(($rating / $votes), 0);
$tvars['vars']['votes']=ruslang($votes); // супер-функция
if ((isset($_COOKIE['rating'.$newsID]) && $_COOKIE['rating'.$newsID]) || (extra_get_param('rating','regonly') && !is_array($userROW)) || $show==1 || ipcheck($mysql, $newsID, 0)=="fail") {
// Show
$tpl -> template('rating', $tpath['rating']);
$tpl -> vars('rating', $tvars);
return $tpl -> show('rating');
} else {
// Edit
$tpl -> template('rating.form', $tpath['rating.form']);
$tpl -> vars('rating.form', $tvars);
return $tpl -> show('rating.form');
}
return;
}
function plugin_rating_screen(){
global $SUPRESS_TEMPLATE_SHOW, $template;
@header('Content-type: text/html; charset="windows-1251"');
if ($_REQUEST['post_id']) {
$template['vars']['mainblock'] = plugin_rating_update();
$SUPRESS_TEMPLATE_SHOW = 1;
} else {
$template['vars']['mainblock'] = 'unsupported action';
}
}
//
// ”Ё«мва ®ў®б⥩ (¤«п Ї®Є § ३⨣ )
//
class RatingNewsFilter extends NewsFilter {
function showNews($newsID, $SQLnews, &$tvars, $mode = array()) {
global $tpl, $mysql, $userROW;
$tvars['vars']['plugin_rating'] = rating_show($SQLnews['id'],$SQLnews['rating'],$SQLnews['votes'],0);
$tvars['vars']['rating'] = rating_show($SQLnews['id'],$SQLnews['rating'],$SQLnews['votes'],1);
}
}
register_filter('news','raing', new RatingNewsFilter);
register_plugin_page('rating','','plugin_rating_screen',0);
код грязнейший, но работает, всю ночь гуглил и писал Вопросов еще много остается...
Еще несколько правок, и можно официально обновлять плагин, вся соль в ipcheck()!
Вот, как-то так, комментируем, обсуждаем... Очень волнует нагрузка на сервер при таких подходах, хз что будет
Точно... Это в версии 0.9.2. Я сейчас открыл файл rating.php в архиве 0.9.3 Release Candidate 0 [SVN834], там этой строчки уже нет, однако. Я так и понял что она была лишняя
Тогда я возьму обновленный плагин из дистрибутива 0.9.3 Release Candidate 0 [SVN834] и буду шаманить его, если найду какие-нибудь баги - сообщу
Но, мой вопрос пока остается открытым, кто что может подсказать по этому поводу - излагайте свои мысли , и сам я тоже попробую как-нибудь плагин поковырять, заодно может и PHP выучу и научусь с БД работать
И с ходу, еще вопрос. В rating.php есть участок кода:
// Ok, everything is fine. Let's update rating.
@setcookie('rating'.$post_id, 'voted', (time() + 31526000), '/');
is_array($row = $mysql->record("select * from ".prefix."_news where id = ".db_squote($post_id)));
у меня вопрос, зачем здесь нужна строка
is_array($row = $mysql->record("select * from ".prefix."_news where id = ".db_squote($post_id)));
какие функции она выполняет??
Просто у меня есть желание сделать сайт где рейтинг новостей будет практически главным смыслом сайта.
Экспериментально я правил некоторые файлы для достижения нужного результата, и всё вроде работает, что именно правил уже всё не вспомню, кучу файлов, изменения стилей, и т.д.
Вот, например в rating.form.tpl вот так я изменил принцип голосования:
<a href="#" title="Ужасно" class="b-none" onclick="rating('1', '{post_id}'); alert('Большое спасибо! Ваш голос принят!'); return false;"><img src="{home}/templates/default/img/z1.png" width="70px" height="70px" alt="Ужасно"/></a>
<a href="#" title="Плохо" class="b-none" onclick="rating('25', '{post_id}'); alert('Большое спасибо! Ваш голос принят!'); return false;"><img src="{home}/templates/default/img/z2.png" width="70px" height="70px" alt="Плохо"/></a>
<a href="#" title="Удовлетворительно" class="b-none" onclick="rating('50', '{post_id}'); alert('Большое спасибо! Ваш голос принят!'); return false;"><img src="{home}/templates/default/img/z3.png" width="70px" height="70px" alt="Удовлетворительно"/></a>
<a href="#" title="Хорошо" class="b-none" onclick="rating('75', '{post_id}'); alert('Большое спасибо! Ваш голос принят!'); return false;"><img src="{home}/templates/default/img/z4.png" width="70px" height="70px" alt="Хорошо"/></a>
<a href="#" title="Отлично" class="b-none" onclick="rating('100', '{post_id}'); alert('Большое спасибо! Ваш голос принят!'); return false;"><img src="{home}/templates/default/img/z5.png" width="70px" height="70px" alt="Отлично"/></a>
хотя алерт можно вынести в функцию, чтобы 5 раз не писать, и класс можно было упростить на .divclass a{стили} но это пока не критично, я думаю
и в rating.php я изменил значение максимальной оценки с 5 до 100:
// Security protection - limit rating values between 1..5
$rating = intval($_REQUEST['rating']);
$post_id = intval($_REQUEST['post_id']);
if (($rating <1)||($rating >100)) {
return 'incorrect rating';
}
В итоге, рейтинг новости в шаблоне теперь имеет числовой вывод оценки, оценка от 1 до 100 - вроде то что нужно
Но... что же это за рейтинг такой, проголосовал, очистил куки, опять проголосовал, надо подшаманить как-то...
Привет всем!
У меня такой вопрос - а как доработать плагин рейтинга новостей, чтобы он не только сохранял печеньку в браузере незарегистрированного юзера когда он проголосует за новость, но и сохранял в базе данных его IP, например там я хз.. - (127.0.0.1 | news id : 346 | news id : 347 | news id : 29) т.е если я правильно понимаю нужно создать таблицу в которой будут записываться IP голосовавших юзеров и ID всех новостей за которые они голосовали.
Или наоборот, в таблице с новостью создать ячейку, куда будут записываться IP голосовавших за нее, и потом читать оттуда. Как лучше - не знаю, как реализовать - тоже не знаю
В связи с этим возникают вопросы, на сколько при этом увеличится нагрузка на сервер и размер БД, если, например, посетителей больше 1k и большое количество новостей?
Просьба сильно не пинать, ну не учился я на программиста, есть только минимальнейшие теоретические знания PHP, смотрю я в код rating.php и непонятно что к чему..
А желание сделать есть, подскажите как чего, тем-более кто-то на форуме этот вопрос пытался поднять, но безрезультатно, или я плохо искал...
З.Ы. использую версию Next Generation CMS 0.9.2 Release [SVN646], если что