вторник, 13 декабря 2011 г.

Делаем стильное меню с картинками (SlideDownBoxMenu) на TypoScript


Есть такое замечательное и оригинальное двухуровневое меню под названием SlideDownBoxMenu. Данное меню хорошо тем, что каждому пункту меню 1-го уровня сопоставляется тематическая картинка, которая делает меню интуитивно-понятным. Ну, а про прикольную popup-анимацию я уже и не говорю.



Поглядеть и познакомиться со SlideDownBoxMenu поближе можно здесь или на одном из моих заброшенных (по вине очень занятого хозяина) сайтов.

Для того, чтобы такое меню заработало на сайте Typo3 даже написан специальный экст - t3s_slidedownboxmenu. Однако, я не сторонник применять эксты там, где можно обойтись TypoScript-ом. Тем более, что верстка самого меню не так уж и сложна, но и не тривиальна.

Сверстанное меню должно иметь следующий вид:


<ul id="sdt_menu" class="sdt_menu">
<li>
<a href="#">
<img src="images/1.jpg" alt=""/>
<span class="sdt_active"></span>
<span class="sdt_wrap">
<span class="sdt_link">Меню</span>
<span class="sdt_descr">Короткое описание</span>
</span>
</a>
</li>

<li>
<a href="#">
<img src="images/2.jpg" alt=""/>
<span class="sdt_active"></span>
<span class="sdt_wrap">
<span class="sdt_link">Меню2</span>
<span class="sdt_descr">Короткое описание2</span>
</span>
</a>
<div class="sdt_box">
<a href="#">Подменю1</a>
<a href="#">Подменю2</a>
<a href="#">Подменю3</a>
</div>
</li>

<li>...</li>
</ul>

Тут основная загвоздка в картинке. Но это все ерунда при наличии небольшого количества серого вещества! Привожу хорошо задокументированный рабочий вариант скрипта:


lib.slidemenu = COA
# Создаем контейнер для пунктов меню - UL
lib.slidemenu.wrap = <ul id="sdt_menu" class="sdt_menu"> | </ul>
lib.slidemenu.10 = HMENU
lib.slidemenu.10 {
  noBlur = 1
  # Берем для меню только корневые страницы сайта
  entryLevel = 0
  # Данные страницы из меню исключаем
  excludeUidList = 14,23,25
  1 = TMENU
  1 {
    noBlur = 1
    expAll=1

    NO {
  # Каждый пункт меню это  - LI
 wrapItemAndSub = <li> | </li>
  # Запрещаем Typo3 делать ссылку для данного пункта меню
  # (т.к. верстаем все сами)
  doNotLinkIt = 1
  # Ссылку делаем сами (тут только её начало)
 allWrap.stdWrap.dataWrap = <a href="{field:alias}.html"> |
  # Первой внутри ссылки будет картинка (откуда она берется описывается ниже)
 beforeImg.import = uploads/media/
  beforeImg.import.field = media
  beforeImg.import.listNum = 0
  # Перед именем страницы, согласно верстке, должна быть такая конструкция:
 before.stdWrap.dataWrap = <span class="sdt_active"></span><span class="sdt_wrap"><span class="sdt_link"> |
  # После имени страницы, согласно верстке, должна быть такая конструкция:
 after.stdWrap.dataWrap = | </span><span class="sdt_descr">{field:subtitle}</span></span></a>
    }
 
    # Для текущего пункта меню все делается аналогично. Только добавляется класс - current
    ACT = 1
    ACT {
      wrapItemAndSub = <li class="current active"> | </li>
 allWrap.stdWrap.dataWrap = <a href="{field:alias}.html"> |
 beforeImg.import = uploads/media/
  beforeImg.import.field = media
  beforeImg.import.listNum = 0
 before.stdWrap.dataWrap = <span class="sdt_active"></span><span class="sdt_wrap"><span class="sdt_link"> |
 after.stdWrap.dataWrap = | </span><span class="sdt_descr">{field:subtitle}</span></span></a>
 doNotLinkIt = 1
    }

    CUR = 1
    CUR {
      wrapItemAndSub = <li class="current active"> | </li>
 allWrap.stdWrap.dataWrap = <a href="{field:alias}.html"> |
 beforeImg.import = uploads/media/
  beforeImg.import.field = media
  beforeImg.import.listNum = 0
 before.stdWrap.dataWrap = <span class="sdt_active"></span><span class="sdt_wrap"><span class="sdt_link"> |
 after.stdWrap.dataWrap = | </span><span class="sdt_descr">{field:subtitle}</span></span></a>
 doNotLinkIt = 1
    }
 
  }
  # Верстка пунктов меню второго уровня. Тут все просто и без комментариев.
  2 = TMENU
  2 {
    noBlur = 1
    expAll=1
    wrap = <div class="sdt_box"> | </div>

    NO {
      linkWrap = |
    }

    CUR = 1
    CUR {
      linkWrap = |
 ATagParams = class="current"
    }

  }

}


Теперь о сопоставлении картинок и страниц.

Каждый начинающий тайпер наверняка видел, что страница сайта в админке имеет ряд свойств, которые разделены на закладки. Причем закладка Ресурсы/Resources часто вызывает недоумение - что это за файлы которые можно атачить к странице? Ответ: можно присоединять  любые файлы в том числе и картинки, для решения таких задач как эта (а можно, например, приатаченные картинки использовать и как фон для данной страницы).

Вот так это делается:


Все что мы здесь атачим Typo3 автоматически сохраняет в папке uploads/media/. Нумерация файлов в списке начинается с нуля. Имя поля которое содержит список имеет имя - media.
Думаю, что теперь ясны 3 магические строки скрипта:
beforeImg.import = uploads/media/
beforeImg.import.field = media
beforeImg.import.listNum = 0


Для лучшего понимания работы скрипта советую почитать мануал.

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

вторник, 6 декабря 2011 г.

Не работает register:newsCategoryUid


Нужно было мне получить название категории новостей по её id, а точнее по значению регистра - register:newsCategoryUid (для бредкрамба на странице единичной новости).

Делал примерно так:

20 = RECORDS
20 {
source = {register:newsCategoryUid}
source.insertData = 1
tables = tt_news_cat
conf.tt_news_cat = TEXT
conf.tt_news_cat.field = title
wrap = <li> | </li>
}

И не работает, хоть убей! После нескольких часов мук, решение все же было найдено.
В шаблон единичной новости добавил следующий HTML-код:
<div style="display:none;">###NEWS_CATEGORY###</div>

После этого, регистр сразу же начал заполняться значением, а мой запрос начал нормально отрабатывать.

Поясню, style="display:none;" написал лишь для того, чтобы на детальной странице новостей категория была скрыта, здесь она мне не нужна.

Возможно, для кого-то это и само-собой понятно, но лично я был уверен, что этот регистр tt_news заполняется всегда и при любых условиях.

четверг, 24 ноября 2011 г.

Меню из категорий новостей (tt_news) с картинками и на разных языках

Казалось бы, tt_news имеет специальный тип вывода категорий - CATMENU. Но то ли скудно у него обстоят дела с картинками, то ли я с чем-то не до конца разобрался... Короче, решил сделать вывод списка категорий с помощью TypoScript. И пока не жалею.

Внешне все выглядит так:


Теперь по функционалу:

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


2. Клик по картинке или названию категории выводит записи новостей, относящиеся только к данной категории.
3. Т.к. сайт многоязычный, то при смене языка меняются и названия категорий. Это был самый трудный момент реализации, т.к. локализация категорий в новостях выполнена довольно странно и нетрадиционно для Typo3.



Вот готовый typoscript для решения данной задачи:

lib.catmenu >
lib.catmenu = COA
lib.catmenu {
10 = CONTENT
10 {
wrap = |
table = tt_news_cat
select {
# CONCAT(title,"|",title_lang_ol) - сливает все языки в одну строку
# разделителем служит символ палка - "|"
selectFields = uid, image, CONCAT(title,"|",title_lang_ol) AS catname
pidInList = {$news_folder_id}
orderBy = uid
where = parent_category={$parent_category} AND deleted=0 AND hidden=0
}
renderObj = COA
renderObj {
wrap = <div class="category">|</div>
10 = COA
10 {
# задаем id страницы которая будет отображать новости
# данной категории
stdWrap.typolink.parameter.dataWrap = {$view_page_id}
# чтобы наша следующая страница со списком новостей знала
# id категории с которой нужно работать передаем ей
# в качестве параметра - tx_ttnews[cat]={field:uid}
stdWrap.typolink.additionalParams.dataWrap = &no_cache=1&tx_ttnews[cat]={field:uid}
stdWrap.typolink.wrap = |
stdWrap.typolink.ATagParams =
# вывод картинки
10 = IMAGE
10{
required=1
wrap = |
file.import=uploads/pics/
file.import.field=image
file.width=235c
file.height=235c
}
# вывод названия категории
20 = COA
20 {
wrap = <h3>|</h3>
10 = TEXT
10 {
field = catname
# разбиваем поле catname на элементы массива
# за счет разделителя - | (char code = 124)
# и берем нужный элемент по коду языка сайта - GP:L
split {
token.char = 124
returnKey.data = GP:L
}
}
}
}
}
}
}

P.S. Как это выглядит в живую можно посмотреть здесь, в видео ролике или на действующем сайте.

P.S. (2) Не забудьте настроить RealUrl (realurlconf.php) для красивого отображения категорий в адресной строке браузера. Вот необходимые для этого строки:
// news categories
'category_name' => array (
array(
'GETvar' => 'tx_ttnews[cat]',
'lookUpTable' => array(
'table' => 'tt_news_cat',
'id_field' => 'uid',
'alias_field' => 'title',
'addWhereClause' => ' AND NOT deleted',
'useUniqueCache' => 1,
'useUniqueCache_conf' => array(
'strtolower' => 1,
'spaceCharacter' => '-',
),

),

),

),
пятница, 18 ноября 2011 г.

Линк для разлогинивания пользователя (logout link)

Простейший TypoScript для генерации ссылки позволяющей сделать пользователю логаут. На самом деле, в инете такого добра полно, но надоело каждый раз куда-то лазить и подглядывать, а так будет всегда под рукой:
lib.logout = TEXT
lib.logout {
wrap = <div id="logout"> | </div>
value= Logout
lang.fr = Déconnexion
typolink.parameter.data = page:uid
typolink.additionalParams.cObject = COA
typolink.additionalParams.cObject {
10 = TEXT
10.value = &logintype=logout
20 = TEXT
20.data = GP : L
20.wrap = &L=|
20.required = 1
}
}
вторник, 8 ноября 2011 г.

Алиасы для файлов в новостях или этот сумасшедший TypoScript

Думаю все знают, что к каждой новости в Typo3 можно приатачить какие-либо файлы.
Иногда такие файлы нужно вывести в специальном боксе или "киоске". Но что делать если имена файлов очень длинные, без пробелов и в отведенную для них область просто не помещаются?



Выход один - необходимо каждому файлу сопоставить алиас. Как это сделать? Это можно реализовать с помощью чумачечего TypoScript, причем без установки дополнительных расширений.
Для начала нужно определиться с полем, которое будет содержать в себе алиасы файлов. Т.к. у меня картинок в новостях не было, то я решил заюзать стандартное и родное для tt_news поле - imagecaption.


Теперь немного "интуитивно-понятного" скрипта, который мы размещаем в нашем шаблоне:
plugin.tt_news {
newsFiles {
labelStdWrap.cObject = COA
labelStdWrap.cObject {
10 = TEXT
10 {
field = imagecaption
split.token.char = 10
split.returnKey.data = register : FILE_NUM
}

20 = LOAD_REGISTER
20 {
FILE_NUM.cObject = TEXT
FILE_NUM.cObject.data = register:FILE_NUM
FILE_NUM.cObject.wrap = | +1
FILE_NUM.prioriCalc = intval
}
}
}
}

... и ву-а-ля



P.S. Спасибо немецкому форуму, который так красиво решил эту нетривиальную задачу!
четверг, 22 сентября 2011 г.

Зaпоминаем состояние jcarousel с помощью якорей

Решил посвятить пост не TYPO3, а jQuery и довольно известной карусели - jcarousel.

На днях пришлось решать следующую задачку. Есть jcarousel и у нее порядка 10 страниц. С помощью стрелочек "влево" и "вправо" мы гуляем по ней то туда, то сюда. Зашли на страницу - 7. Потом вдруг переходим на другую страницу сайта, после чего опять возвращаемся к странице с каруселью. И что мы имеем в данной ситуации? А имеем:

Первый неприятный момент - карусель показывает нам свою 1-ю страницу, вместо той с которой мы до этого ушли (номер 7). Ну, и мы начинаем пролистывание нашей карусели сначала...

Второй неприятный момент заключается в том, что нельзя отослать кому-то ссылку показывающую 7-ю страницу карусели, т.к. все страницы имееют один и тот же url.

Чтобы избавиться от этих двух недостатков нам нужно как-то запоминать состояние карусели. Сделать это можно с помощью якорей или иначе - хеш-ссылок. Например, http://www.domain.com/page.html#watch1, http://www.domain.com/page.html#watch7 и т.д.

Первое, что нужно сделать, это менять строку адреса браузера при пролистывании карусели. На выручку приходит параметр jcarousel - itemVisibleInCallback (другие параметры мне не подошли, хотя напрашивались, например itemFirstInCallback), который определяет пользовательскую функцию, далее цитирую мануал: "вызываемую (по завершению анимации) когда элемент находится в зоне видимости поля карусели". В данную функцию передается 4 параметра: экземпляр карусели, ссылка на элемент LI этого объекта, индекс, указывающий позицию элемента в списке и состояние карусели (prev, next, init). Итак, готовый кусочек кода выглядит так:

// carouselStart - номер элемента карусели с которого начинается показ
var carouselStart = 3;
var watchPos = -1;
var anchor = ( jQuery.url.attr('anchor') == null ? '' : jQuery.url.attr('anchor') );
watchPos = anchor.indexOf('watch');
if ( watchPos >= 0 ) {
carouselStart = anchor.substring(watchPos+5);
}
if(jQuery.isFunction(jQuery.fn.jcarousel)) {
jQuery('#mycarousel').jcarousel({
easing: 'easeOutQuart',
animation: 900,
scroll: 3,
wrap: 'last',
start: (carouselStart - 2),
itemVisibleInCallback: function (carousel, liElement, itemIndex, action) {
window.location.hash = "watch"+itemIndex;
}
});
}

Поясню кое-что.
1. #mycarousel - id тега UL, который является контейнером элементов карусели - LI;
2. scroll: 3 - у меня на одной странице карусели отображается 3 её элемента;
3. start: (carouselStart - 2) - номер элемента карусели с которого начинается показ
4. itemVisibleInCallback - определяет функцию которая меняет адресную сроку браузера. Самый ценный параметр, который поступает на вход этой функции - itemIndex. Т.к. у меня на каждой странице карусели отображается по 3 её элемента, то при нажатии на кнопку Next itemIndex принимает следующие значения: 3, 6, 9... и т.д.
Так, мы запоминаем состояние карусели.

Для того, чтобы карусель начала показ с нужного места, используется переменная - carouselStart. Значение этой переменной берется из адресной строки браузера.

Ну вот, собственно и все. Надеюсь кому-то помог и сэкономил время.

P.S. Рабочий вариант карусели можно посмотреть здесь.
понедельник, 9 мая 2011 г.

Присваиваем ID и CLASS для тега BODY на странице TYPO3

Зачастую у верстальщика или разработчика сайта возникает необходимость присвоить тегу BODY какой-нибудь class и(или) id.
Чтобы сделать это в TYPO3, лично меня спасает следующий TypoScript код:

page.bodyTag >
page.bodyTagCObject = COA
page.bodyTagCObject {
5 = TEXT
5.value = <body
10 = TEXT
10.field = uid
10.noTrimWrap = | id="page_|" |
20 = TEXT
20.field = alias
20.noTrimWrap = | class="|"|
20.required = 1
25 = TEXT
25.value = >
}

Что мы получаем на выходе? А получаем два варианта тега body:
1. <body id="page_ID"> - когда странице не задан алиас
2. <body id="page_ID" class="page-alias"> - когда странице задан алиас

Как видно из скрипта, в качестве id страницы используется поле uid, а в качестве ее класса - поле alias. Имена полей всегда можно изменить для собственных нужд.

А иногда бывает необходимость одним страницам сайта иметь один фон, а остальным - другой фон. Как быть в таком случае? Можно немного доработать последний скрипт и воспользоваться свойством "Шаблон страницы/Frontend Layout", который будет служить переключателем фона.



page.bodyTag >
page.bodyTagCObject = COA
page.bodyTagCObject {
10 = TEXT
10.field = uid
10.noTrimWrap = | id="page_|" |
20 = TEXT
20.field = alias
#20.required = 1
wrap = <body |>
}
[globalVar=TSFE:page|layout=1]
page.bodyTagCObject.20.noTrimWrap = | class="gray-bg |"|
[else]
page.bodyTagCObject.20.noTrimWrap = | class="white-bg |"|
[global]

В результате, при выборе шаблона страницы - Макет 1, в теге body данной страницы мы получим класс - "gray-bg", в противном случае каждая страница сайта будет иметь класс - "white-bg".

Для того, чтобы в админке при смене шаблона страницы мы видели человеческие надписи, а не  магические - Стандартный, Макет1.... и т.д., можно в свойствах корневой страницы сайта добавить несколько строк typoscript кода:

TCEFORM.pages { 
layout.altLabels.1 = Gray background 
layout.altLabels.0 = White background 
}


В результате получим следующее:


Надеюсь, что материал окажется полезным для тайпо-голиков.

суббота, 29 января 2011 г.

Расширение NEWSLIST_EXT или делаем четные и нечетные (odd/even) строки в списке новостей

Ни как не могу понять почему такую простую вещь разработчики не могут включить в ядро экстеншена.
Народ начинает изголяться, причем каждый по-своему. Один пишет экст (newslist_ext, например, который с tt_news 3.0.1 не работает), другой добавляет стили с помощью javascript (в надежде, что js включен у всех), третий в CSS применяет псевдо-классы first-child и last-child (чтобы выделить хотя бы первый и последний элемент списка новостей).

Для себя данную проблему решил следующим образом.

Сначала я установил расширение newslist_ext и посмотрел, что оно делает, а точнее, что оно должно делать. А делает оно элементарную вещь - переопределяет функцию getListContent класса tx_ttnews, т.е. к родному коду функции добавлено еще несколько дополнительных строк. Понятное дело, что за таким экстом, который обновляется каждые пол-года, автору было не угнаться и его творение (newslist_ext) перестало функционировать с выходом tt_news v.3. Похоже, что функция getListContent претерпела значительные изменения.

Вот кусочек php-кода (и моих 5 копеек), который родил автор newslist_ext, для того, чтобы наша жизнь стала краше и мы могли выделять в списке новостей не только первую и последнюю позиции, но и четные и нечетные строки:

// add code : add marker
$counter = $cc+1;
$markerArray['###NEWS_LIST1###'] = '';
$markerArray['###NEWS_LIST2###'] = '';
$markerArray['###NEWS_LIST3###'] = '';

$markerArray['###NEWS_LIST1###'] = $counter;
if ($counter==1) $markerArray['###NEWS_LIST2###'] = 'first';
if ($counter == $resCount) $markerArray['###NEWS_LIST2###'] = 'last';

if (($counter % 2) != 1) {
$markerArray['###NEWS_LIST3###'] = 'even';
} else {
$markerArray['###NEWS_LIST3###'] = 'odd';
}
// add code : end

Теперь в файле typo3conf\ext\tt_news\pi1\class.tx_ttnews.php находим актуальную для нашей версии tt_news функцию getListContent. Выделяем ее и копируем в буфер обмена. Переходим к файлу class.ux_tx_ttnews.php в директории typo3conf\ext\newslist_ext. В этом файле определена одна единственная функция - getListContent, которую мы вытираем и заменяем содержимым нашего клипборда.

Осталось дело за малым - добавить дополнительные строки php-кода, которые вы видите выше. Вставить их нужно за следующей строкой:

$layoutNum = ($itempartsCount == 0 ? 0 : ($cc % $itempartsCount));

Сохраняем файл и получаем работоспособное расширение newslist_ext.

Не забывайте добавить маркеры ###NEWS_LIST3### в те места, где вы собираетесь увидеть дополнительные CSS-стили - odd и even

Скачать обновленный NEWSLIST_EXT можно тут T3X_newslist_ext-0_0_1-z-201101291026.t3x
среда, 26 января 2011 г.

TYPO3 хостинг. Обсудим провайдеров?

 

Предлагаю сообществу Typo3 обсудить, а возможно и составить белый и черный список хостинг-провайдеров. Ведь все мы рано или поздно задаем себе вопрос - где разместить свой сайт. Я последнее время смотрю только в сторону VDS-серверов. Тут ты и царь и бог! Но не всегда!

Взял я на свою беду VDS от одного известной российской компании - 1GB.RU, пакет назывался VZ-Профи. Этот пакет сулил мне и Linux x64 (AMD64), но почему-то с 32-битными приложениями, и виртуальной памяти - 2Гб, и дискового пространства 20 Гб, а максимальной мощности процессора аж - 2400 мГц (максимум). Короче, думал, что буду как сыр в масле. Да не тут-то было...

Решил свой новый сайт (http://www.gyrkalo.dp.ua/ пока в стадии разработки) реализовать на Typo3 v.4.4.6. Его производительность можете оценить сами, но до конца марта, пока этот сайт будет находится у 1gb.ru.

Все мои познания Linux привели к выводу, что причиной тормоза является дисковая подсистема. Все мои просьбы помочь разобраться с данной проблемой ни к чему не привели - одни отписки в стиле - вы сами контролируете VDS вот и ищите, что не так. В общем я принял твердое решение сменить хостинг-провайдера и найти нового. Думаю, что это будет какой-то Европейский хостинг, например в Германии. Результат своих поисков непременно опишу.

Если у кого есть, что сказать в защиту или против 1gb.ru, пишите. Но думаю, что они того не стоят, а лучше было бы услышать хорошие советы и положительные рекомендации о каких-либо компаниях.

Пишите свои истории. Это интересно!

 

понедельник, 24 января 2011 г.

Руссификация (локализация) BE TYPO3

 


Несмотря на то, что данная "проблема" - не нова, и поисковики выдают источники решающие данную проблему, я решил все-таки разместить несколько скриншотов, которые должны снимать с повестки дня новичков руссификацию админки (бэкенда) Typo3.

1. Здесь выбирается язык для локализации админки:


Как видим, по-умолчанию всего один язык - английский. Будем добавлять русский.

2. Добавляем русский язык к системе с помощью ExtManager (менеджера расширений):



после нажатия на кнопку Save selection появляются дополнительные кнопки:



Начинается процесс скачивания дополнительных файлов из репозитория (системного хранилища) Typo3:



При этом, как видим, обновляется не только сама система, но и многие расширения:



3. В списке доступных языков появилась новая строка, выбираем её и сохраняем параметры:



Теперь Админка будет иметь тот язык, который пользователю ближе по духу. Typo3 позволяет администратору сайта иметь одну локализацию, а рядовому пользователю (редактору) - другую.

Очень советую базу данных MySql создавать с опцией DEFAULT CHARACTER SET utf8, а в конфигурационном файле localconf.php устанавливать следующие параметры:

$TYPO3_CONF_VARS['BE']['forceCharset']='utf-8';
$TYPO3_CONF_VARS['SYS']['setDBinit'] = 'SET NAMES utf8'.chr(10).'SET SESSION character_set_server=utf8';

 

среда, 5 января 2011 г.

Странности при сохранении в t3editor

При визуальной настройке плагина новостей, на последней закладке - "Другие настройки", имеется область ввода - "Typoscript для этого элемента содержимого" внутри которого можно вписывать дополнительные настройки (см. картинку).



Так вот, в этом поле, после сохранения, стали пропадать символы: "<>&".

Такая же фигня обнаружилась и внутри t3editor в расширении - TypoScript code (id -typoscript_code).

В данной ситуации всё лечится всего одной строкой в localconf.php:
$TYPO3_CONF_VARS['BE']['flexformForceCDATA'] = '1';

Этот параметр можно установить и в Install Tools. Именно тут, после долгого изучения всех параметров TYPO3, я и нашел данное решение.

А чуть позже, тут, нашел и это:



Выходит, что это какие-то косяки с libxml (1.6.32 и выше) на сервере! Дай бог им всем здоровья ;), а у меня пропало пол-дня.

 

О себе

Моя фотография
Вадим Гиркало
Фрилансер, веб-разработчик сайтов на базе бесплатной, мощнейшей и очень гибкой CMS системы - TYPO3.
Просмотреть профиль

Позвонить мне в Skype

Архив блога

TOP - 3