Список пользователей

1
Админ
Постов: 288
2
VIP
Постов: 75
3
Элита
Постов: 62
4
Проверенные
Постов: 46
5
Проверенные
Постов: 45
6
Проверенные
Постов: 39
7
VIP
Постов: 36
8
VIP
Постов: 35

  • Страница 1 из 1
  • 1
Скрипт друзей с функцией подтверждения V3.0 для uСoz
Дата: Воскресенье, 21.06.2026, 20:39 | Сообщение # 1 | | Написал: Известный
Автор темы
Мурчанн не в сети
        Сообщений:288
         Регистрация:20.10.2016
За уважение окружающих За уважение окружающих За хорошее поведение За преданность форуму За любовь народа

«Продолжается развитие скрипта с добавлением новых возможностей. Реализована система двустороннего осмысленного подтверждения, включающая многоуровневые этапы верификации и контроля, что позволяет повысить надежность обработки данных и снизить вероятность ошибок.»

Модуль «Друзья» это современное решение для uCoz, превращающее стандартную систему добавления в друзья в полноценный социальный функционал. Вместо мгновенного добавления (как это было в старых версиях uCoz), скрипт реализует двухэтапную систему подтверждения через личные сообщения точно так же, как это работает в ВКонтакте, Facebook и других крупных социальных сетях.

Процесс добавления в друзья состоит из трёх логичных шагов:

Заявка. Пользователь нажимает кнопку «Добавить в друзья» на странице другого пользователя. Вместо мгновенного добавления система открывает красивое модальное окно, где можно выбрать группу друзей (Друг, Приятель, Знакомый, Семья, Коллега, Кумир) и при желании добавить личное сообщение. После нажатия «Отправить заявку» пользователю приходит личное сообщение со специальной ссылкой-приглашением.

Подтверждение

Получатель заявки переходит по ссылке и видит стильное окно подтверждения с аватаром отправителя. После нажатия «Подтвердить» он автоматически добавляет отправителя в свой список друзей и отправляет встречное сообщение со ссылкой на завершение.

Завершение. Отправитель первоначальной заявки получает ответное сообщение, переходит по ссылке и добавляет получателя к себе в друзья. Дружба становится взаимной.

Защита от дубликатов

Скрипт автоматически проверяет список друзей обеих сторон перед каждым действием. Если пользователь уже находится в списке, добавление блокируется с показом информативного уведомления. Более того, при попытке повторного добавления отображается точная дата, когда этот друг был добавлен ранее.

Двусторонняя синхронизация

Даже если один из пользователей удалил друга из своего списка, система корректно обработает повторную заявку не создаст дубликат, а отправит ссылку на завершение взаимного добавления.

Группы друзей

Шесть предустановленных категорий: Друг, Приятель, Знакомый, Семья, Коллега, Кумир. Выбор группы происходит в красивом выпадающем меню с синим градиентным фоном и белым текстом.

Личное сообщение к заявке

При отправке заявки можно прикрепить персональное сообщение , это делает процесс более живым и персонализированным, как в настоящих социальных сетях.

Красивые модальные окна

Все окна выполнены в современном стиле VK/Facebook:

Плавные анимации появления
Градиентные заголовки с эффектом мерцания
Пульсирующие иконки
Аватары с объёмными тенями
Стилизованные кнопки с hover-эффектами
Информационные блоки с иконками

Информативные уведомления

При попытке добавить уже существующего друга пользователь видит:

1. Аватар пользователя
2. Имя
3. Статус «Уже в вашем списке друзей»
4. Дату добавления (извлекается из шаблона)
5. Описание ситуации

Безопасность и надёжность

Токены подтверждения

Каждая заявка получает уникальный токен, который сохраняется в localStorage после обработки. Это защищает от:

1. Повторного подтверждения одной и той же заявки
2. Перехвата ссылок третьими лицами
3. Случайного двойного клика

Защита от несанкционированного добавления

Скрипт перехватывает все AJAX-запросы на добавление в блог и блокирует их, пока пользователь не пройдёт через официальное окно подтверждения. Это исключает возможность добавления «в обход» системы.

Капча для ЛС

Если на сайте включена защита от спама в личных сообщениях, скрипт автоматически покажет окно ввода капчи , пользователю не нужно ни о чём беспокоиться.

Производительность

Минимальная нагрузка на сервер

Все запросы выполняются только по действию пользователя , никаких фоновых опросов
За весь процесс дружбы происходит около 12 запросов (для сравнения: одна загрузка Facebook делает 50-100 запросов)
Большинство запросов лёгкие GET-запросы

Умное кэширование

1. Аватары кэшируются в памяти и localStorage
2. Список друзей проверяется через AJAX только при необходимости
3. Профили пользователей загружаются по требованию

Быстрый отклик

Проверка локального кэша (localStorage) происходит мгновенно, до обращения к серверу. Это даёт пользователю ощущение мгновенной реакции интерфейса.

Дизайн

Интерфейс модуля выполнен в единой стилистике современных социальных сетей:

Модалка заявки — с аватаром получателя, выбором группы и возможностью добавить сообщение

Модалка подтверждения — с градиентной шапкой, пульсирующей иконкой и крупной аватаркой отправителя

Модалка завершения — компактная, с чётким призывом к действию

Модалка успеха — с анимированной синей галочкой в стиле VK

Alert-уведомления — компактные, с аватаром пользователя и детальной информацией

Все элементы адаптивны и корректно отображаются на мобильных устройствах.

Технические особенности

Работает на jQuery (совместим с uCoz)

Использует стандартный модуль «Блог» как хранилище записей о друзьях

Поддерживает все стандартные переменные uCoz
Код
($TITLE$, $OTHER1$, $OTHER3$, $DATE$ и др.)


Не требует установки дополнительных модулей или плагинов

Полностью изолирован — не конфликтует с другими скриптами на сайте

Поддерживает HTTPS и корректно работает с Mixed Content

Всё, что нужно для запуска установить скрипт, настроить шаблоны и наслаждаться. 2



Вид материалов



Код
<div style="display:none;">
<span class="friend">
<span class="nick">$TITLE$</span>
<span class="ava">$OTHER1$</span>
<span class="gr">$OTHER2$</span>
<span class="del"><?substr($MODER_PANEL$,strrpos($MODER_PANEL$,'/index/92-3-'),strpos(substr($MODER_PANEL$,strrpos($MODER_PANEL$,'/index/92-3-')),"','"))?></span>
<span class="url">$OTHER3$</span>
<span class="date">$DATE$ $TIME$</span>
</span>
</div>
<span id="adordelfr$OTHER5$"></span>
<h3 class="entry-title"><a class="entry-title-link" href="$ENTRY_URL$">$TITLE$</a></h3>
<?if($MODER_PANEL$)?><div class="entry-moder-container">$MODER_PANEL$</div><?endif?>


Глобальный блок модуль

Код
<div id="vk_friends_block">
<h3>Братаны</h3>
<div id="vk_friends_list_custom"></div>

<div class="friends-count-box-custom">
$_BLOG_ENTRIES$
</div>
</div>

<link rel="stylesheet" href="https://bro.usite.pro/css/brother-friends.css?v=1">

<div id="friends-list" class="my-friends-list"></div>

<script type="text/javascript">
$(document).ready(function(){
var othid = '$_USER_ID$';
var storageKey = 'friends_' + othid;
var cacheTimeKey = storageKey + '_time';
var cacheTTL = 24 * 60 * 60 * 1000; // 24 часа

function renderFriends(friends){
var html = "";
var count = 0;
var seen = {};

// Синхронизация с friends.04.js
if (typeof window.friends !== 'undefined' && window.friends.markFriendAdded) {
friends.forEach(function(f) {
var idMatch = f.profile.match(/\/index\/8-(\d+)/);
if (idMatch) {
window.friends.markFriendAdded(othid, idMatch[1], f.nick);
}
});
}

friends.forEach(function(f){
if(count >= 4) return;
if(seen[f.profile]) return;
seen[f.profile] = true;

html += '<div class="my-friend-card">';
html += '<div class="friend-avatar-friends" style="background-image:url(\''+f.ava+'\');" data-profile="'+f.profile+'"></div>';
html += '<div class="friend-nick-friends"><a href="'+f.profile+'" target="_blank">'+f.nick+'</a></div>';
if(f.group) html += '<div class="friend-group-friends">'+f.group+'</div>';
html += '</div>';

count++;
});

$("#friends-list").html(html);
$("#friends-count-box").text(count);
}

function fetchFriends(updateCache = true, compareCache = false){
$.get('/blog/0-0-1-0-17-' + othid, function(data){
var $items = $(data).find('.friend');
if(!$items.length){
$("#friends-list").html("<p>У этого пользователя нет друзей</p>");
$("#friends-count-box").text(0);
return;
}

var friends = [];
$items.each(function(){
var $el = $(this);
friends.push({
nick: $el.find('.nick').text().trim() || "Без имени",
ava: $el.find('.ava').text().trim() || '/.s/src/profile/img/profile_photo_thumbnail.png',
group: $el.find('.gr').text().trim() || "",
profile: $el.find('.url').text().trim() || '#'
});
});

if(compareCache){
var cached = localStorage.getItem(storageKey);
if(cached){
var cachedFriends = JSON.parse(cached);
var cachedProfiles = cachedFriends.map(f => f.profile).sort().join(',');
var newProfiles = friends.map(f => f.profile).sort().join(',');
if(cachedProfiles === newProfiles){
// Ничего не изменилось
return;
}
}
}

renderFriends(friends);

if(updateCache){
localStorage.setItem(storageKey, JSON.stringify(friends));
localStorage.setItem(cacheTimeKey, Date.now());
}
});
}

// Проверяем кэш
var cached = localStorage.getItem(storageKey);
var cacheTime = localStorage.getItem(cacheTimeKey);

if(cached && cacheTime && (Date.now() - cacheTime < cacheTTL)){
// Используем кэш
renderFriends(JSON.parse(cached));
// Параллельно тихая проверка на новые друзья
fetchFriends(false, true);
} else {
// Кэш устарел или нет
fetchFriends(true, false);
}
});
</script>

<script type="text/javascript">
if (typeof friends === 'undefined') window.friends = {}; // не перезаписываем, если уже есть

// Заполняем данные пользователя
friends.ucodes = {
homepage: '$HOME_PAGE_LINK$',
userid: '$USER_ID$',
username: '$USERNAME$',
_userid: '$_USER_ID$',
_username: '$_USERNAME$',
own: '$_IS_OWN_PROFILE$',
avatar: '<?if($_AVATAR$)?><?substr(substr($_AVATAR$, strpos($_AVATAR$, "src=\"")+5),0,strpos(substr($_AVATAR$, strpos($_AVATAR$, "src=\"")+5), "\""))?><?else?>/.s/src/profile/img/profile_photo_thumbnail.png<?endif?>',
_profurl: '$_PROFILE_URL$',
isuser: '$USER_LOGGED_IN$',
copyright: 'ucodes.ru'
};
</script>

<script>
if (typeof friends === 'undefined') window.friends = {};
(function($, friends){

friends.nowpage = 1;
friends.maxPage = false;

friends.fix = function() {
if (window._uWnd && _uWnd.getbyname('fr')) {
_uWnd.close('fr');
friends.nowpage = 1;
friends.maxPage = false;
setTimeout(friends.loadFr, 500);
} else friends.loadFr();
};

friends.loadFr = function(upd){
$("body").css("cursor", "wait");
var _upd = !!upd;
var targetId = (friends.ucodes && (friends.ucodes._userid || friends.ucodes.userid)) || '';
var url = '/blog/0-0-' + friends.nowpage + '-0-17-' + targetId + '?' + Math.random();

$.get(url).done(function(data){
var $data = $('<div>').append($.parseHTML(data));
var $items = $data.find(".friend, .fr, .друг, .friend-item, .fr-item");
if (!$items.length) $items = $data.find(".entry, .entry-card, .user-row");

var all = [];

$items.each(function(){
var $it = $(this);

// Пытаемся корректно достать аватар (несколько вариантов)
var ava = '';
var $img = $it.find('img').first();
if ($img.length && ($img.attr('src') || '').match(/avatar/)) {
ava = $img.attr('src');
if (ava && ava.indexOf('http') !== 0) ava = location.origin + ava;
} else {
var text = $it.text();
var avaMatch = text.match(/(\/avatar\/\d{2}\/\d{2}\/\d+\.(?:png|jpg))/);
ava = avaMatch ? location.origin + avaMatch[0] : '';
}

// ← добавляем эту строку
var DEFAULT_AVA = "/.s/src/profile/img/profile_photo_thumbnail.png";
if (!ava) ava = DEFAULT_AVA;

// ссылка на профиль
var profileUrl = '#';
var hrefMatch = $it.html().match(/https?:\/\/[^\s"']+\/index\/8-(\d+)/);
if (hrefMatch) {
profileUrl = location.origin + '/index/8-' + parseInt(hrefMatch[1],10);
} else {
var $aProfile = $it.find('a[href*="/index/8-"]').first();
if ($aProfile.length) {
profileUrl = $aProfile.attr('href');
if (profileUrl && profileUrl.indexOf('http') !== 0) profileUrl = location.origin + profileUrl;
}
}

// Ник и группы
var textClean = $it.clone();
textClean.find('img, a, .ava, .del, .gr').remove();
var nick = (textClean.text() || '').trim().split('\n')[0] || 'Без имени';
nick = nick.replace(/\s{2,}/g,' ').trim();
var groupMatches = ($it.text().match(/(Кумир|Семья|Друг|Приятель|Коллега|Знакомый)/gi) || []);
var groups = groupMatches;

// MODER_PANEL — ссылка удаления (если есть)
var del = '';
var $delEl = $it.find('.del, .del-link').first();
if ($delEl.length) {
del = ($delEl.text() || $delEl.attr('href') || '').trim();
}
if (!del) {
var delMatch = $it.html().match(/\/index\/92-3-\d+/);
del = delMatch ? delMatch[0] : '';
}

// собираем
all.push([ava, nick, groups, profileUrl, /*legacy action html*/ '', del]);
});

friends.open(all, _upd);

}).fail(function(){ console.error("Ошибка загрузки друзей."); })
.always(function(){ $("body").css("cursor", "default"); });
};

friends.table = function(arr){
if(!arr.length) return '<div class="no-friends">Друзей нет</div>';
var html = '';
$.each(arr, function(i, item){
// item = [ava, nick, groups, profileUrl, actionHtml, del]
var avaUrl = item[0] || '';
var nick = escapeHtml(item[1] || 'Без имени');
var profile = item[3] || '#';
var groupsHtml = '';
if (item[2] && item[2].length) {
groupsHtml = item[2].map(function(cat){
return '<a class="friend-group-friends" href="'+ profile +'" target="_blank">'+ escapeHtml(cat.trim()) +'</a>';
}).join(' ');
}

// крестик — показываем только если есть ссылка на MODER_PANEL и юзер владелец
var delBtnHtml = '';
var hasDel = item[5] && item[5].length;
if (hasDel && friends.ucodes && friends.ucodes.own == 1) {
delBtnHtml = '<button class="friend-delete-btn-friends" data-del="'+ encodeURIComponent(item[5]) +'" title="Удалить"></button>';
}

html += `
<div class="friend-card-friends">
<div class="friend-avatar-friends" style="background-image:url('${avaUrl}'); cursor:pointer;" data-profile="${profile}"></div>
<div class="friend-info-friends">
<div class="friend-nick-friends"><a class="friend-profile-link" href="${profile}" target="_blank" rel="noopener">${nick}</a></div>
${groupsHtml}
</div>
${delBtnHtml}
</div>`;
});
return html;
};

friends.open = function(frnds, isUpdate){
var cards = friends.table(frnds);
var moreHtml = (friends.maxPage && friends.nowpage < friends.maxPage)
? '<div class="moreFr" style="text-align:right;"><a href="javascript:void(0)" onclick="friends.more();">Остальные друзья</a></div>' : '';

if(isUpdate){
if(!$('#frr').length) $('#fr').after('<div id="frr"></div>');
$('#frr').append(cards + moreHtml);
} else {
var contentHtml = '<div id="frr">' + cards + moreHtml + '</div>';
if(window._uWnd){
new _uWnd('fr', 'Друзья '+(friends.ucodes && friends.ucodes._username ? friends.ucodes._username : ''), 500, 800, {
minw:500, maxw:500, maxh:800,
onbeforeclose: function(){ friends.nowpage=1; friends.maxPage=false; }
}, contentHtml);
} else {
if(!$('#fr').length) $('body').append('<div id="fr" style="margin-top:10px;"></div>');
$('#fr').html(contentHtml);
}
}
};

friends.more = function(){
friends.nowpage++;
$(".moreFr").remove();
friends.loadFr(true);
};

// клик по аватару — открываем профиль
$(document).on('click', '.friend-avatar-friends', function(){
var p = $(this).data('profile') || '#';
if (p && p !== '#') window.open(p, '_blank');
});

// Удаление крестиком — аккуратно формируем полный URL и шлём GET
$(document).on('click', '.friend-delete-btn-friends', function(e){
e.preventDefault();
var $btn = $(this);
var delUrl = decodeURIComponent($btn.data('del') || '');

if (!delUrl) {
return alert('Ссылка удаления не найдена');
}
if (!confirm('Вы уверены, что хотите удалить?')) return;

// если относительный — добавляем origin
if (delUrl.indexOf('http') !== 0) {
if (delUrl.charAt(0) !== '/') delUrl = '/' + delUrl;
delUrl = location.origin + delUrl;
}

// Убираем карточку визуально
$btn.closest('.friend-card-friends').fadeOut(220, function(){ $(this).remove(); });

// Отправляем запрос
$.get(delUrl)
.done(function(){ console.log('Удаление отправлено: ' + delUrl); })
.fail(function(){ alert('Ошибка при удалении'); });
});

// Вспомогательная
function escapeHtml(str){
return String(str || '').replace(/[&<>"'`=\/]/g, function(s){ return ({'&':'&','<':'<','>':'>','"':'"',"'":''',"/":'/','`':'`','=':'='})[s]; });
}

$(document).on("click", ".openFriends", function(){ friends.fix(); });

})(jQuery, friends);
</script>

<div class="udtb">

<!-- Контейнер для обеих кнопок -->
<div class="buttons-row">
<!-- Левая кнопка -->
<div id="addFriendContainer"></div>

<!-- Правая кнопка -->
<a href="#" id="lookfr" class="btn-look" onclick="friends.fix();return false;">Посмотреть друзей</a>
</div>
</div>

<script type="text/javascript">
var othid = '$_USER_ID$'.trim();
var myid = '$USER_ID$'.trim();

$(document).ready(function(){

// Если мой ID = ID профиля, который смотрю → это я сам
if (myid == othid) {
$("#addFriendContainer").html(""); // делаем пустым, кнопка НЕ вставится
return; // выходим и больше ничего не делаем
}

// Иначе (это не я) → вставляем кнопку
$("#addFriendContainer").html(
'<a href="javascript:;" onclick="friends.sAdd(); return false;" class="btn-add">Добавить в друзья</a>'
);

});
</script>

<script type="text/javascript" src="https://bro.usite.pro/js/friends.04.js?v=2"></script>


Подробную информацию об установке скрипта, а также о необходимых настройках системы для его корректной работы, вы можете найти в отдельной теме. В ней подробно описаны все этапы установки, требования к системе и параметры, которые необходимо настроить для стабильной работы скрипта.

Скрипт друзей с функцией подтверждения V2.2 для uСoz

Конечно, с точки зрения функционала скрипт уже достаточно далеко продвинулся вперёд. В дальнейшем смогу дорабатать дизайн, добавлять новые возможности для модальных окон, делать их более информативными.

К этому вопросу, скорее всего, вернусь ещё не скоро. Сейчас важно двигаться в другом направлении, поскольку есть и другие модули, которые также требуют доработки и улучшения. 12

Скрипт

Видео

Мурчанн

Признаюсь, не знаю почему, но глядя на звезды мне всегда хочется мечтать.
  • Страница 1 из 1
  • 1
Поиск: