Commit 4000262c authored by Vadim's avatar Vadim

Version 2.0

Добавлен рейтинг по голосам ("звездочки")
parent cda075aa
/* Виджеты Популярные записи
и Сейчас на сайте*/
.widget-item .bg-az-top-posts {
margin: 5px;
padding: 0;
......@@ -37,3 +39,51 @@
i.fa-user-o:before {
margin-left: 1em;
}
/* Рейтинг */
.bg_counter_rating {
position: relative;
padding:0px;
margin:0px;
}
#bg_counter_rate_box {
box-sizing:border-box;
padding:0px;
margin:0px;
}
#bg_counter_rate_box * {
box-sizing:border-box;
padding:0px; margin:0px;
}
#bg_counter_rate_box li {
float:left;
width:20px;
height:20px;
margin-right:3px;
list-style:none;
cursor:defult;
background-image:url(stars.png);
background-size:20px auto;
background-position:0px 20px;
}
#bg_counter_rate_box li::before {
display: none;
}
#bg_counter_popup_help {
position: absolute;
padding: 2px 10px;
margin: 0px;
left: 0px;
bottom: 24px;
min-width: 115px;
z-index: 100;
font-size: 16px;
background-color: rgba(250, 248, 241, 0.9);
color: #8A5536;
display: none;
font-family: Arial;
text-align: center;
border: 1px solid #A6801E;
border-radius: 3px;
box-shadow: 0 0 5px rgba(0,0,0,0.5);
}
<?php
/*****************************************************************************************
GET /rating/<path>
Рейтинг. Возвращает непосредственных потомков, упорядоченных по убыванию
value.
Доступные параметры:
limit - ограничение на количество возвращаемых потомков. По умолчанию: 100
offset - сколько элементов в начале пропустить. По умолчанию: 0
limit/offset работают по такому же принципу, как и в SQL.
Пример запроса:
GET /rating/project/test/author/1?limit=5&offset=3
Пример ответа:
{
"success":true,
"data":[
{"id":"6","type":"book","value":11},
{"id":"7","type":"book","value":7},
{"id":"5","type":"book","value":1},
{"id":"4","type":"book","value":1},
{"id":"3","type":"book","value":1}
]
}
Если счётчик не существует, возвращает пустой массив потомков.
******************************************************************************************/
function getPopularPosts ($limit, $offset=0, $number=false) {
global $project;
$option = get_option('bg_counter_options');
$result = wp_remote_get (BG_COUNTER_STAT_RATING.$project."?limit=".$limit."&offset=".$offset."&type=post");
if( is_wp_error( $result ) ) {
error_log( PHP_EOL .date("Y-m-d H:i:s ", time())." RATING (top posts). Ошибка при получении данных с сервера: ".$result->get_error_message(), 3, BG_COUNTER_LOG ); // сообщение ошибки
error_log( " " .$result->get_error_code(), 3, BG_COUNTER_LOG ); // ключ ошибки
return false;
}
if (($code = wp_remote_retrieve_response_code( $result )) != 200) {
error_log( PHP_EOL .date("Y-m-d H:i:s ", time())." RATING (top posts). Сервер вернул код ошибки: ".$code, 3, BG_COUNTER_LOG ); // сообщение ошибки
error_log( " " .wp_remote_retrieve_response_message( $result ), 3, BG_COUNTER_LOG ); // ключ ошибки
return false;
}
$json = wp_remote_retrieve_body($result);
$response = json_decode($json, false);
if ($response->success == true){
$the_key='getPopularPosts_key';
if(false===($quote=get_transient($the_key))) {
if ($number) $quote = '<ol class="bg-az-top-posts">'. PHP_EOL;
else $quote = '<ul class="bg-az-top-posts">'. PHP_EOL;
foreach ($response->data as $p) {
if ($p->type!='post') continue;
$id = intval($p->id);
if (!$id) continue;
$post = get_post($id);
if (!$post) continue;
$title = $post->post_title;
$link = '<a href="'. get_permalink($post).'" title="'.$title.'" data-ID="'.$p->id.'" data-type="'.$p->type.'" data-value="'.$p->value.'" data-status="'.$post->post_status.'">'.$title.'</a>';
$quote .= '<li>'.$link.' - <span class="bg-az-count">'.bg_counter_number_format($p->value).'</span></li>'. PHP_EOL;
}
if ($number) $quote .= '</ol>'. PHP_EOL;
else $quote .= '</ul>'. PHP_EOL;
set_transient( $the_key, $quote, $option['period'] );
}
return $quote;
} else {
error_log( PHP_EOL .date("Y-m-d H:i:s ", time())." RATING (top posts). Сервер вернул ответ неудачи:\n".$json, 3, BG_COUNTER_LOG );
return false;
}
}
/*****************************************************************************************
POST /set-counter/<path>
Задаёт значение счётчика и создаёт его, если счётчик не существовал.
Предназначено для импорта данных и должно быть закрыто от внешних клиентов.
Пример запроса:
POST /set-counter/project/test/author/1/book/3
Тело: {"counter": 3}
Пример ответа:
{"success": true}
******************************************************************************************/
// Установить 1 счетчик
function setCount ($path, $counter) {
global $project;
$result = wp_remote_post (BG_COUNTER_STAT_SET.$project.$path, array('body' => '{"counter": '.$counter.'}'));
if( is_wp_error( $result ) ) {
echo "<br>".$result->get_error_message(); // сообщение ошибки
echo "<br>".$result->get_error_code(); // ключ ошибки
error_log( PHP_EOL .date("Y-m-d H:i:s ", time())." SET-COUNTER. Ошибка при получении данных с сервера: ".$result->get_error_message(), 3, BG_COUNTER_LOG ); // сообщение ошибки
error_log( " " .$result->get_error_code(), 3, BG_COUNTER_LOG ); // ключ ошибки
return false;
}
$json = wp_remote_retrieve_body($result);
$response = json_decode($json, false);
if ($response->success) return true;
else {
echo $json;
error_log( PHP_EOL .date("Y-m-d H:i:s ", time())." SET-COUNTER. Сервер вернул ответ неудачи:\n".$json, 3, BG_COUNTER_LOG );
return false;
}
}
// Установить ВСЕ счетчики проекта
function setAllCounts ($request) {
global $project;
$result = wp_remote_post (BG_COUNTER_STAT_SET, array('body' => $request));
if( is_wp_error( $result ) ) {
echo "<br>".$result->get_error_message(); // сообщение ошибки
echo "<br>".$result->get_error_code(); // ключ ошибки
error_log( PHP_EOL .date("Y-m-d H:i:s ", time())." SET-COUNTER (all). Ошибка при получении данных с сервера: ".$result->get_error_message(), 3, BG_COUNTER_LOG ); // сообщение ошибки
error_log( " " .$result->get_error_code(), 3, BG_COUNTER_LOG ); // ключ ошибки
return false;
}
$json = wp_remote_retrieve_body($result);
$response = json_decode($json, false);
if ($response->success) return true;
else {
echo $json;
error_log( PHP_EOL .date("Y-m-d H:i:s ", time())." SET-COUNTER (all). Сервер вернул ответ неудачи:\n".$json, 3, BG_COUNTER_LOG );
return false;
}
}
/*****************************************************************************************
Отображает разметку текста для отображения кол-ва просмотров
и кол-ва читающих пост пользователей
******************************************************************************************/
function bg_az_counter_views ($type=null, $id=null, $now=null) {
if (is_single() || is_page()) {
if (is_null($id)) {
if (!isset ($post)) $post = get_post();
$id = $post->ID;
}
if (is_null($type)) {
$type='post';
}
}
if (is_null($now)) {
$option = get_option('bg_counter_options');
$now = $option['now'];
}
if (!is_null($type) && !is_null($id)) {
$views = '<i title="Просмотры страницы" class="fa fa-eye"></i> <span class="bg-az-counter-views"></span>';
$users = ' <i title="Сейчас читают страницу" class="fa fa-user-o"></i> <span class="bg-az-counter-now"></span>';
$quote = '<span class="bg-az-counter" data-type="'.$type.'" data-ID="'.$id.'">'.$views.($now?$users:'').'</span>';
return $quote;
} else return "";
}
/*****************************************************************************************
Шорт-коды
Функции обработки шорт-кодов
******************************************************************************************/
// Регистрируем шорт-код bg_counter
add_shortcode( 'bg_counter', 'bg_counter_shortcode' );
// Регистрируем шорт-код bg_counter_top_posts
add_shortcode( 'bg_counter_top_posts', 'bg_counter_top_posts_shortcode' );
// [bg_counter type='post' id='1234' now='true']
// Выводит на экран разметку счетчика
function bg_counter_shortcode( $atts ) {
global $post;
extract( shortcode_atts( array(
'type' => null,
'id' => null,
'now' => null
), $atts ) );
$quote = bg_az_counter_views ($type, $id, $now);
return "{$quote}";
}
// [bg_counter_top_posts limit='10']
// Выводит на экран список популярных постов
function bg_counter_top_posts_shortcode( $atts ) {
global $post;
extract( shortcode_atts( array(
'limit' => '10',
'number' => false
), $atts ) );
$quote = getPopularPosts ($limit, 0, (bool)$number);
return "{$quote}";
}
function bg_counter_number_format ($num) {
$num = floatval ($num);
if ($num > 1000000000.0) {
$num = round($num/1000000000.0, 1)." млрд.";
} elseif ($num > 1000000.0) {
$num = round($num/1000000.0, 1)." млн.";
} elseif ($num > 10000.0) {
$num = round($num/1000.0, 1)." тыс.";
} else {
$num = number_format($num, 0, ',', '&nbsp;');
}
return $num;
}
This diff is collapsed. Click to expand it.
......@@ -141,6 +141,74 @@ class bg_counter_OnlineNowWidget extends WP_Widget {
return $instance;
}
}
/*****************************************************************************************
Виджет отображает в сайдбаре
Список популярных постов по результатам голосования
******************************************************************************************/
class bg_counter_PostRatingWidget extends WP_Widget {
// создание виджета
function __construct() {
parent::__construct(
'bg_counter_post_rating_widget',
'Популярные записи', // заголовок виджета
array( 'description' => 'Позволяет вывести записи, отсортированные по количеству голосов.' ) // описание
);
}
// фронтэнд виджета
public function widget( $args, $instance ) {
$title = apply_filters( 'widget_title', $instance['title'] ); // к заголовку применяем фильтр (необязательно)
$posts_per_page = $instance['posts_per_page'];
echo $args['before_widget'];
if ( ! empty( $title ) )
echo $args['before_title'] . $title . $args['after_title'];
$list = getPostRating ($posts_per_page);
if ($list) {
?>
<div class="widget-item">
<div class="widget-inner">
<?php echo $list; ?>
</div>
</div>
<?php
}
echo $args['after_widget'];
}
// бэкэнд виджета
public function form( $instance ) {
if ( isset( $instance[ 'title' ] ) ) {
$title = $instance[ 'title' ];
}
if ( isset( $instance[ 'posts_per_page' ] ) ) {
$posts_per_page = $instance[ 'posts_per_page' ];
}
?>
<p>
<label for="<?php echo $this->get_field_id( 'title' ); ?>">Заголовок</label>
<input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" />
</p>
<p>
<label for="<?php echo $this->get_field_id( 'posts_per_page' ); ?>">Количество записей:</label>
<input id="<?php echo $this->get_field_id( 'posts_per_page' ); ?>" name="<?php echo $this->get_field_name( 'posts_per_page' ); ?>" type="text" value="<?php echo ($posts_per_page) ? esc_attr( $posts_per_page ) : '5'; ?>" size="3" />
</p>
<?php
}
// сохранение настроек виджета
public function update( $new_instance, $old_instance ) {
$instance = array();
$instance['title'] = ( ! empty( $new_instance['title'] ) ) ? strip_tags( $new_instance['title'] ) : '';
$instance['posts_per_page'] = ( is_numeric( $new_instance['posts_per_page'] ) ) ? $new_instance['posts_per_page'] : '5'; // по умолчанию выводятся 5 постов
return $instance;
}
}
/*****************************************************************************************
Регистрация виджетов
......@@ -149,5 +217,6 @@ class bg_counter_OnlineNowWidget extends WP_Widget {
function bg_counter_widgets_load() {
register_widget( 'bg_counter_TopPostsWidget' );
register_widget( 'bg_counter_OnlineNowWidget' );
register_widget( 'bg_counter_PostRatingWidget' );
}
add_action( 'widgets_init', 'bg_counter_widgets_load' );
\ No newline at end of file
......@@ -12,13 +12,13 @@ jQuery( document ).ready(function() {
if (bg_counter.debug) console.log(" Соединение установлено. "+request);
GetAllCounters();
};
}
GetAllCounters();
} else GetAllCounters();
});
/*********************************************************************************
POST /counters/<path>
Увеличивает счётчик на единицу (и создаёт его, если он не существует).
Тело запроса пустое.
......@@ -32,11 +32,11 @@ jQuery( document ).ready(function() {
"success":true,
"data":{
"created": false,
"now": 3,
"value": 35
}
}
В ответе параметр created говорит, существовал ли счётчик до этого.
**********************************************************************************/
function SendOnce(type, id) {
......@@ -51,7 +51,7 @@ function SendOnce(type, id) {
if (response.success) {
// Здесь надо будет добавить вывод данных на экран
if (bg_counter.debug) console.log(JSON.stringify(response.data));
setViewCount (id, addDelimiter(response.data.value));
setViewCount (type, id, bg_counter_number_format(response.data.value), (response.data.now+1));
} else {
if (bg_counter.debug) console.log('POST REQUEST: '+request+' ERROR: '+response.error);
}
......@@ -100,8 +100,8 @@ function GetAllCounters() {
if (response.success) {
if (bg_counter.debug) console.log('GET REQUEST: '+request);
if (bg_counter.debug) console.log(JSON.stringify(response.data));
el.find('span.bg-az-counter-views').text(addDelimiter(response.data.total));
el.find('span.bg-az-counter-now').text(addDelimiter(response.data.now));
el.find('span.bg-az-counter-views').text(bg_counter_number_format(response.data.total));
el.find('span.bg-az-counter-now').text(bg_counter_number_format(response.data.now));
} else {
if (bg_counter.debug) console.log('GET REQUEST: '+request+' ERROR '+xhr.status+': '+response.error);
el.find('span.bg-az-counter-views').text('0');
......@@ -148,4 +148,20 @@ function addDelimiter(nStr, dlm='\xa0') {
x1 = x1.replace(rgx, '$1' + dlm + '$2');
}
return x1 + x2;
}
\ No newline at end of file
}
function bg_counter_number_format (num) {
num = parseFloat (num);
if (num > 1000000000.0) {
num = num/1000000000;
num = num.toFixed(1)+" млрд.";
} else if (num > 1000000.0) {
num = num/1000000;
num = num.toFixed(1)+" млн.";
} else if (num > 10000.0) {
num = num/1000;
num = num.toFixed(1)+" тыс.";
} else {
num = addDelimiter(num);
}
return num;
}
var start_rate_index, rating_voted;
var options={};
options.expires=60*60*24; // Кука на сутки
options.path=window.location.pathname; // Текущий путь
jQuery( document ).ready(function() {
if (!bg_counter.ID) return;
// start_rate_index = Math.round(parseFloat(jQuery( "#bg_counter_score" ).html()));
// var price = ["очень плохо", "плохо", "удовлетворительно", "хорошо", "отлично"];
start_rate_index = parseFloat(jQuery( "#bg_counter_score" ).html());
rating_voted = (jQuery( "#bg_counter_score" ).attr("data-voted")=='true')?true:false;
iniRatingState(start_rate_index, rating_voted);
getRate(bg_counter.type, bg_counter.ID);
jQuery( "#bg_counter_rate_box li" ).mouseover(function() {
if(!rating_voted){
var index = jQuery( this ).index();
iniRatingState(index+1, rating_voted);
jQuery('#bg_counter_popup_help').text(bg_counter.price[index]);
// jQuery('#bg_counter_popup_help').css('width', '110px');
} else {
jQuery('#bg_counter_popup_help').text(bg_counter.voted);
// jQuery('#bg_counter_popup_help').css('width', '260px');
}
jQuery('#bg_counter_popup_help').show();
});
jQuery( "#bg_counter_rate_box" ).mouseout(function() {
if(!rating_voted){
iniRatingState(start_rate_index, rating_voted);
}
jQuery('#bg_counter_popup_help').hide();
});
jQuery( "#bg_counter_rate_box li" ).click(function() {
if(!rating_voted){
rating_voted = true;
jQuery( "#bg_counter_rate_box li" ).css('cursor', 'default');
var sindex = jQuery( this ).index()+1;
sendRate(bg_counter.type, bg_counter.ID, sindex);
}
});
});
function iniRatingState(sindex, voted){
if(!voted) jQuery( "#bg_counter_rate_box li" ).css('cursor', 'pointer');
else jQuery( "#bg_counter_rate_box li" ).css('cursor', 'default');
star = parseInt(jQuery( "#bg_counter_rate_box li" ).css('height')); /* высота звездочки */
jQuery( "#bg_counter_rate_box li" ).css('background-position', '0px '+star+'px');
jQuery( "#bg_counter_rate_box li" ).each(function( index ) {
n=sindex-sindex%1;
if(index < n){
jQuery(this).css('background-position', '0px '+5*star+'px');
}
else if (sindex-index > 0) {
p=star*(Math.round(4*(sindex-index))+1);
jQuery(this).css('background-position', '0px '+p+'px');
}
});
}
/*********************************************************************************
GET /item-score/<path>
Возвращает рейтинг и количество голосов отдельно взятого объекта -
score. Также возвращается флаг, голосовал ли уже данный пользователь.
Пример запроса:
GET /item-score/project/test/author/1
Пример ответа:
{
"success": true,
"data": {
"alreadyVoted":true,
"score": 3.7142857142857144,
"votes": 7
}
}
Если объект не существует, возвращает success: false и data: null.
Важно: если alreadyVoted равно true, то повторная попытка голосования
провалится.
**********************************************************************************/
function getRate(type, id) {
var request = bg_counter.scoreurl+bg_counter.project+"/"+type+"/"+id;
var xhr = new XMLHttpRequest();
xhr.open("GET", request, true);
if (bg_counter.debug) console.log('GET REQUEST: '+request);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.responseText) {
var response = JSON.parse(xhr.responseText);
if (response.success) {
// Вывод данных на экран
if (bg_counter.debug) console.log(JSON.stringify(response.data));
m = response.data.votes % 10;
j = response.data.votes % 100;
if(m==0 || m>=5 || (j>=10 && j<=20)) txt_votes = bg_counter.votes5;
else if(m>=2 && m<=4) txt_votes = bg_counter.votes2;
else txt_votes = bg_counter.vote1;
start_rate_index = parseFloat(response.data.score).toFixed(1);
jQuery('#bg_counter_votes').html(response.data.votes);
jQuery('#bg_counter_votes_txt').html(txt_votes);
jQuery('#bg_counter_score').html(start_rate_index);
iniRatingState(start_rate_index, response.data.alreadyVoted);
} else {
if (bg_counter.debug) console.log('GET REQUEST: '+request+' ERROR: '+response.error);
jQuery('#bg_counter_votes').html('0');
jQuery('#bg_counter_votes_txt').html(bg_counter.votes5);
jQuery('#bg_counter_score').html('0');
iniRatingState(0, false);
}
}
}
}
xhr.send();
}
/*********************************************************************************
POST /rate/<path>
Увеличивает сумму оценок объекта на указанную величину (от 1 до 5) и количество
голосов на 1.
Возвращает новый рейтинг и количество голосов. Рейтинг рассчитывается по формуле:
новый рейтинг = сумма оценок / количество голосов;
Пример запроса:
POST /rate/project/test/author/1/book/3
Тело: {"rating": 4}
Пример ответа:
{
"success": true,
"data": {"score": 4.0625, "votes": 16}
}
**********************************************************************************/
function sendRate(type, id, number) {
var request = bg_counter.rateurl+bg_counter.project+"/"+type+"/"+id;
var xhr = new XMLHttpRequest();
xhr.open("POST", request, true);
if (bg_counter.debug) console.log('POST REQUEST: '+request);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.responseText) {
var response = JSON.parse(xhr.responseText);
if (response.success) {
// Вывод данных на экран
if (bg_counter.debug) console.log(JSON.stringify(response.data));
m = response.data.votes % 10;
j = response.data.votes % 100;
if(m==0 || m>=5 || (j>=10 && j<=20)) txt_votes = bg_counter.votes5;
else if(m>=2 && m<=4) txt_votes = bg_counter.votes2;
else txt_votes = bg_counter.vote1;
start_rate_index = parseFloat(response.data.score).toFixed(1);
jQuery('#bg_counter_votes').html(response.data.votes);
jQuery('#bg_counter_votes_txt').html(txt_votes);
jQuery('#bg_counter_score').html(start_rate_index);
iniRatingState(start_rate_index, true);
} else {
if (bg_counter.debug) console.log('POST REQUEST: '+request+' ERROR: '+response.error);
}
}
}
}
xhr.send('{"rating": '+number+'}');
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment