Реализуем редактор постов через админ панель

 

Здравствуй дорогой читатель! Приветствую на продолжение цикла статей посвященного созданию CMS блога! Сегодня мы будем писать редактор для нашей CMS. В прошлой статье мы писали форму добавления постов в базу данных, сейчас же нам предстоит организовать точно такую же форму, но цель ее будет вытащить из базы данных пост, и сохранить изменения которые мы с Вами внесем. Как и добавление постов, редактор будет выглядеть в виде простой формы. В скором будущем мы начнем писать bb коды для этой формы, но а сейчас она будет уметь лишь добавлять простой текст, и не более того...

 

 

Предлагаю начать с того, по каким ссылкам будут находится наши странички, которые мы сейчас создадим, и сколько страничек будет вообще! Отвечу с начало на вопрос: "Сколько страничек будет?" Их будет две, на первой мы будем видеть полный список наших постов, на второй же будет находится форма редактирования. Для того что бы писать ссылки, в будущем нашем коде, сразу объявим, что список постов будет находится по адресу index.php?page=all_content, а форма - index.php?page=edd_content&id=ID_поста. Узнавать номер поста будем через глобальную переменную GET['id']. Чует мое сердце, что эта глобальная переменная будет отвечать не только за ID поста, поэтому стоит определить ее в главном нашем файле index.php, но об этом мы поговорим ниже

 

Первая страница - Лист с постами

 

Для реализации этой страницы нам нужно создать шаблон. Я набросал html код, вот он:

<table width="600px" cellpadding="0" cellspacing="0" border="0" align="center">
<tr>
<td width="250px" height="30px" valign="top" style="font-weight:bold;">Название поста</td>
<td valign="top" style="font-weight:bold;">Автор поста</td>
</tr>

[_while]
<tr>
<td width="250px" valign="top"><a style="font-weight:100;" href="/index.php?page=edd_content&id=[_id]">[_title]</a></td>
<td valign="top">[_author]</td>
</tr>
[_while]
</table>

Разберем наши код-слова:

  • [_id] - ID нашего поста в базе данных
  • [_title] - Заголовок поста
  • [_author] - Автор поста
  • [_while]...[_while] - Все что между этими код-словами будет дублироваться в нашем модуле, ведь согласиться, у нас же будет не один пост

Я не зря начал статью с ссылок на страничку, ведь после того как мы определили какие страницы у нас будут, мы без труда можем, наш заголовок, переделать в ссылку которая будет вести на форму поста [_id]

Назвал я этот шаблон allcontent.html и поместил в папку с шаблонами templates

 

Вторая страница - Форма

 

Пришло время создать шаблон формы. Она уж очень схожа с шаблоном который мы делали в прошлой статье. Но разница все же есть, во-первых необходимо добавить одно скрытое поле, которое будет передавать, в обработчик, ID нашего поста, так же нужно исправить название кнопки, добавить код-слова и переделать name у инпутов. Вот html код шаблона:

<table width="600px" cellpadding="0" cellspacing="0" border="0" align="center">
<tr>
<td width="100px" height="100px" valign="top" align="center">
<form action="?page=edd_content" method="post" name="form">
<input name="edd_id_post" type="hidden" value="[_id]">
<br>
<input style="width:600px;" name="edd_name_post" type="text" value="[_title]">
<br>
<textarea style="width:600px;height:600px;" name="edd_txt_post" rows="10">[_text]</textarea>
<input style="width:600px;" name="edd_author_post" type="text" value="[_author]">
<br>
<br><br><input type="submit" value="Редактировать пост">
</form>
</td>
</tr>
</table>

В основном код-слова схожи с предыдущем шаблоном, но в этом появилась новое [_text] - не трудно догадаться, что оно будет отвечать за текст самого поста.

Так же обратите внимание, что обработчик нашей формы будет лежать на странице index.php?page=edd_content

Назвал я этот шаблон eddcontent.html и сохранил в папке с шаблонами templates

 

Написание самого модуля

 

Давайте подумаем, что он из себя будет представлять? Я думаю стоит написать две функции, одна будет отвечать за вывод списка, вторая же будет отвечать за саму форму. Подключать этот модуль будем в случае если наша переменная $page равна "all_content" или "edd_content".

 

  • Написание модуля - функция вывода списка

 

  1. Функция будет подключать наш шаблон allcontent.html
  2. вырезать из этого шаблона все, что стоит между [_while]
  3. после этот вырезанный кусок будем нашпиговывать информацией из базы данных
  4. и в конце выведем результат функции на экран. 

Вот код с комментариями:

function allcontent()//Функция вывода списка постов
{
$sm_read = file("templates/allcontent.html");//...подключаем шаблон
$sm_read = implode("",$sm_read);//функция file() возвращаем массив, поэтому склеиваем его

preg_match("/\[_while\](.*?)\[_while\]/s",$sm_read,$tamp_while);//Находим в шаблоне тут часть, которую будет ду вайлить

$result_index = mysql_query("SELECT id,author,title FROM blog ORDER BY id DESC");//Выводим из базы данных посты
$myrow_index = mysql_fetch_array($result_index);

do
{
$copy_tamp = $tamp_while[1];//Сохраняем ту часть которая будет повторяться в отдельную переменную

//Делаем замены код-слов
$copy_tamp = str_replace("[_title]",$myrow_index[title],$copy_tamp);//Название поста
$copy_tamp = str_replace("[_author]",$myrow_index[author],$copy_tamp);//Автор
$copy_tamp = str_replace("[_id]",$myrow_index[id],$copy_tamp);//ID постов

$list .= $copy_tamp;//Объединяем результат в одну переменную
}
while($myrow_index = mysql_fetch_array($result_index));

$sm_read = preg_replace("/\[_while\].*?\[_while\]/s",$list,$sm_read);//Вставляем в шаблон список постов

return $sm_read;//Выводим с генерированный html код
}

Функцию сохранил как allcontent.php в папке moduls

 

  • Написание модуля - функция вывода формы

 

Эта функция будет по id вытаскивать из базы данных всю информацию о посте, и сохранять ее форме, в общем то ничего сложно Вот код с комментариями:

function eddcontent($id)//Функция вывода выбранного поста
{
$result_index = mysql_query("SELECT * FROM blog WHERE id='$id'");//Выводим из базы данных пост
$myrow_index = mysql_fetch_array($result_index);

$sm_read = file("templates/eddcontent.html");//...подключаем шаблон
$sm_read = implode("",$sm_read);//функция file() возвращаем массив, поэтому склеиваем его

$text_post = str_replace("<BR>","",$myrow_index[text]);//заменяем br в тексте на пустоту

//Делаем замены код-слов
$sm_read = str_replace("[_title]",$myrow_index[title],$sm_read);//Название поста
$sm_read = str_replace("[_text]",$text_post,$sm_read);//Текст поста
$sm_read = str_replace("[_author]",$myrow_index[author],$sm_read);//Автор
$sm_read = str_replace("[_id]",$myrow_index[id],$sm_read);//ID постов

return $sm_read;//Выводим с генерированный html код
}

Добавил эту функцию в уже существующий модуль allcontent.php

 

  • Обработчик

 

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

//ОБРАБОТЧИК
//Объявляем переменные, если форма была отправлена
if($_POST['edd_name_post'])$edd_name_post = $_POST['edd_name_post'];
if($_POST['edd_txt_post'])$edd_txt_post = $_POST['edd_txt_post'];
if($_POST['edd_author_post'])$edd_author_post = $_POST['edd_author_post'];
if($_POST['edd_id_post'])$edd_id_post = $_POST['edd_id_post'];
//Объявляем переменные, если форма была отправлена

if($edd_name_post & $edd_txt_post & $edd_author_post)
{
//Избавляемся от html кода
$edd_name_post = htmlspecialchars($edd_name_post);
$edd_txt_post = htmlspecialchars($edd_txt_post);
$edd_author_post = htmlspecialchars($edd_author_post);
//Избавляемся от html кода

//Избавляемся от кавычки
$edd_name_post = str_replace("'","&#039",$edd_name_post);
$edd_txt_post = str_replace("'","&#039",$edd_txt_post);
$edd_author_post = str_replace("'","&#039",$edd_author_post);
//Избавляемся от кавычки

$edd_txt_post = str_replace("\n","<BR>",$edd_txt_post);//Заменяем переносы строки на тег <BR>

//ОБНОВЛЯЕМ ПОСТ В БАЗЕ ДАННЫХ
$edd_blog = mysql_query ("UPDATE blog SET text='$edd_txt_post',title='$edd_name_post',author='$edd_author_post' WHERE id='$edd_id_post'");
//ОБНОВЛЯЕМ ПОСТ В БАЗЕ ДАННЫХ

header("location: index.php?page=all_content");//Перенаправление
exit;//на страницу списка постов
}
//ОБРАБОТЧИК

Добавил я этот кусок кода в файл allcontent.php перед нашими функциями allcontent() и eddcontent($id)

 

Подключаем наш модуль

 

Осталось дело за малым, подключить наш модуль к админке. Но прежде чем это сделать, хотел бы вернуться к разговору о глобальной переменной GET['id']. Откройте наш файл index.php и добавьте этот кусочек кода:

if($_GET['id'])$id = $_GET['id'];

перед:

if($_GET['page'])$page = $_GET['page']; else $page = "index";


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

Вернемся к подключению нашего модуля. У нас есть две функции. Функция allcontent() будет подключаться в случае если переменная $page равна "all_content", а функция eddcontent($id) - в случае если $page равна "edd_content" Вот код с комментариями:

//РЕДАКТОР КОНТЕНТА
if($page == "all_content" || $page == "edd_content")
{
include("moduls/allcontent.php");//Подключаем наш модуль
if($page == "all_content")$txt = allcontent();//Если пост еще НЕ выбран
if($page == "edd_content")$txt = eddcontent($id);//Если пост выбран
}
//РЕДАКТОР КОНТЕНТА

Вставляется этот кусок кода перед подключением нашего главного шаблона:

include("templates/index.html");

 

Редактируем шаблон "меню админ панели"

 

Осталось разместить ссылку, на список постов, на главной странице админ панели. Для этого открываем шаблон menu.html который мы создавали в прошлой статье, и добавляем ссылку:

<table width="600px" cellpadding="0" cellspacing="0" border="0" align="center">
<tr>
<td width="100px" height="100px" valign="top" align="center"><a href="?page=add_content"><img src="img/add_content.jpg" border="0px"></a></td>
<td width="100px" height="100px" valign="top" align="center"><a href="?page=all_content"><img src="img/all_content.jpg" border="0px"></a></td>
<td width="100px" height="100px" valign="top" align="center">&nbsp;</td>
<td width="100px" height="100px" valign="top" align="center">&nbsp;</td>
<td width="100px" height="100px" valign="top" align="center">&nbsp;</td>
<td width="100px" height="100px" valign="top" align="center">&nbsp;</td>
</tr>
</table>

Вот изображение кнопки: Кнопка

 

Заключение

 

Ну что ж, сегодня мы реализовали простейший редактор для нашей админ панели, в следующей статье мы скорее всего будем создавать bb коды для наших форм, так что не "переключайтесь", спасибо, что прочли статью, удачи Вам, у меня все!

Исходник
 

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

Ваше имя *
Сайт
Ваш E-mail *
Ваше сообщение *
 
Alex, 13 Марта 2012 г. 03:38 пишет:
Гость
Ещё раз простите чайника, но не могу разобраться в действии линка "Читать дальше" под статьями на главной странице. При нажатии на эту ссылку статьи просто исчезают и остаётся меню (уже выравненное по левому краю) и заголовок сайта.
Rio-Shaman, 13 Марта 2012 г. 04:37 пишет:
Автор
У Вас вопрос именно по этому посту, или проблема в другой части кода? Спрашиваю по причине того, что я не припоминаю в админ панели кнопку "Читать дальше".

Если проблема в пользовательской части блога, то мой первый вопрос: прошли ли вы все этапы создания пользовательской части? Я про пункт "Часть III - Разработка пользовательской части блога" вот в этом посте:
http://rio-shaman.ru/index.php?blog=36
Ответ для пользователя: Alex
Alex, 13 Марта 2012 г. 05:48 пишет:
Гость
Я насчёт пользовательской части. Простите за оффтоп - написал куда пришлось.
Часть III делал не всё, некоторые пункты пропустил... видимо не стоило.
Ольга, 27 Марта 2012 г. 15:30 пишет:
Читатель
и здесь что-то у меня не идет, хотя в базу пишет с исправлениями
Warning: Cannot modify header information - headers already sent by (output started at Z:\home\localhost\www\00blog\admin\index.php:1) in Z:\home\localhost\www\00blog\admin\moduls\allcontent.php on line 31

31 строка
header("location: index.php?page=all_content");//Перенаправление
Rio-Shaman, 27 Марта 2012 г. 16:05 пишет:
Автор
Ошибка спровоцирована тем что у Вас где то в коде до строчки

header("location: index.php?page=all_content");//Перенаправление

Выводится какой то текст, оператором echo или каким либо другим способом. Либо ищите где вписывали объявление, либо скидывайте архивом Вашу сборку, я посмотрю.
Ответ для пользователя: Ольга
Ольга, 27 Марта 2012 г. 16:17 пишет:
Читатель
если это не очень нахально, то http://rghost.ru/37252392
Rio-Shaman, 27 Марта 2012 г. 16:36 пишет:
Автор
У Вас при открытие админ панели в самом вверху присутствуют вот эти символы?
п»їп»ї
Ответ для пользователя: Ольга
Ольга, 27 Марта 2012 г. 16:55 пишет:
Читатель
да
Rio-Shaman, 27 Марта 2012 г. 17:05 пишет:
Автор
Дело в том что у Вас у большинства файлов кодировка UTF. Все заработает если всем файлам поменять кодировку на windows-1251.

Для этого нужно открыть файлы блокнотом:
-Выбрать "Сохранить как..."
-Выбрать кодировку ANSI
-Пересохранить с заменой

Кодировка неверная, отсюда каракозябры появляются, которые мешают работать скрипту
Ответ для пользователя: Ольга
Ольга, 27 Марта 2012 г. 17:06 пишет:
Читатель
сейчас попробую поправить, спасибо большое
Ольга, 27 Марта 2012 г. 17:19 пишет:
Читатель
действительно получилось, все файлы рнр были с кодировкой UTF. СПАСИБО.

А можно спросить вне топа? titl в закладках иногда с каракозябрами, читала в поисковиках о возможных причинах, но простого решения так и не нашла. Пишут про charset=utf-8, что возвращает все-равно windows-1251 и.т.п. Может как-то можно это поправить ?
Rio-Shaman, 27 Марта 2012 г. 17:31 пишет:
Автор
Исправьте совершенно все файлы ( .php .html и .css ) в кодировку windows-1251 ( пересохранить через блокнот ). Проблема должна испариться =)
Ответ для пользователя: Ольга
Ольга, 27 Марта 2012 г. 17:53 пишет:
Читатель
все файлы, кроме рнр, у меня в ansi. А после Ваших рекомендаций и рнр исправила, или я что-то не поняла?
Rio-Shaman, 27 Марта 2012 г. 18:02 пишет:
Автор
Не все. Пару файлов из тех, что Вы мне прислали в кодировке UTF. Поменяйте кодировку совершенно на всех файла, не только те которые в папке admin но и остальные. После этого караказябр быть не должно.
Ответ для пользователя: Ольга
Ольга, 27 Марта 2012 г. 18:05 пишет:
Читатель
исправила на других своих страничках рнр тоже все стало нормально.
Алесей, 23 Апреля 2012 г. 13:58 пишет:
Читатель
Слух, походу я что то не то сделал, так ак у меня не выводит форму редактирования) При нажатии на пост просто обновляется страничка и все)
Rio-Shaman, 23 Апреля 2012 г. 16:28 пишет:
Автор
Смотрите внимательней порядок написание кода, тут все рабочее, я копипастом проверял...
Ответ для пользователя: Алесей
Алексей, 23 Апреля 2012 г. 18:37 пишет:
Читатель
Если не сложно, то можете выложить ваш index.php админки и allcontent.php? Пока продолжу)
Rio-Shaman, 23 Апреля 2012 г. 19:18 пишет:
Автор
Вот папка admin на стадии этого поста, у меня все работает, только что по материалу на блоге создал
http://rio-shaman.ru/download/download_1916593448.rar
Ответ для пользователя: Алексей
Алексей, 23 Апреля 2012 г. 19:54 пишет:
Читатель
Странно, но сравнивая ваш код и мой отличия были в одной строчке в index.php Сейчас все ок. Спасибо за отзывчивость)
Ответ для пользователя: Rio-Shaman
Наталья, 16 Ноября 2012 г. 13:24 пишет:
Гость
Здравствуйте! Не могу вывести список allcontent :( Генерировалась ошибка: Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in /home/a1235396/public_html/admin/moduls/allcontent.php on line 65 там у меня: while($myrow_index = mysql_fetch_array($result_index)); написала перед mysql_fetch_array @, ошибка редактировалась, но список так и не выводится... где искать ошибку?.........
Наталья, 16 Ноября 2012 г. 13:31 пишет:
Гость
Еврика! перенаписала заявку и все вышло. Видимо, что-то было написано на кирилице :) теперь все Ок
Сережка, 09 Марта 2013 г. 18:29 пишет:
Читатель
Уважаемый администратор, у меня та же проблема, кодировка вроде везде одна и та же, но проблема не уходит, если я куда нибудь залью архив, посмотрите?!
Богдан, 22 Июля 2013 г. 22:57 пишет:
Гость
не знаю к кому обратится - написал код который должен выводить содержимое файла в форму, после чего я ее редактирую и сохраняю, да вот проблема - выводить выводит, а сохранять не в какую( может посмотришь своим професиональным взглядом, увидешь что я не так сделал, ато я в отчаянии
<?php

// Добавлять в отчет все PHP ошибки
error_reporting(-1);

// То же, что и error_reporting(E_ALL);
ini_set('error_reporting', E_ALL);

$varName = $_POST['txt'];
$var = "file.txt";
$handle = fopen($var, 'r');
$contents = fread($handle, filesize($var));
fclose($handle);
?>
Богдан, 22 Июля 2013 г. 22:58 пишет:
Гость
а вот и форма
<form name=forma method=post action=text.php>
 <textarea name=txt rows=10 cols=60><?php @include($var);?></textarea>
 <br/>
 <input type=submit name=otp value=Сохранить>
 </form>
Алексей, 22 Июля 2013 г. 23:18 пишет:
Автор
Если честно Ваш код до странности не понятен. Вы считываете текст с файла, а после инклюдите его. Да и самого алгоритма обработчика в Вашем коде не видать. Вот код позволяющий редактировать файл:
<?php
$file_dir = 'file.txt';// путь к файлу

// если прислали новое содержимое файла с формы
if (isset($_POST['txt']))
{
    $var = fopen($file_dir, "w+");//открываем файл
    fwrite($var, $_POST['txt']);//записываем в него присланный текст
    fclose($var);//закрываем
}

$contents = implode("", file($file_dir));//читаем файл
?>

<form name="forma" method="post" action="">
 <textarea name="txt" rows="10" cols="60"><?php echo $contents;?></textarea>
 <br/>
 <input type="submit" name="otp" value="Сохранить">
</form>
Посмотрите значение используемых функций на http://php.su/
Ответ для пользователя: Богдан
Богдан, 25 Июля 2013 г. 15:55 пишет:
Гость
у меня опять проблема, что-то я в последнее время хандрю(

вообщем написал код позволяющий выводить список файлов на экран с возможностью удаления конкретного файла. С выводом проблем нет, а вот удалять - не в какую, пишет нет прав, это при том что у меня денвер. Поможете? Вот код:
<?php
 // Добавлять в отчет все PHP ошибки
error_reporting(-1);

// То же, что и error_reporting(E_ALL);
ini_set('error_reporting', E_ALL);


$dir = opendir("../images/small/");// открываем дерикторию
while (false !== ($file = readdir($dir))): //получаем эдемент каталога по его дескриптору
        if ($file != "." AND $file != "..")
		{ 
			echo "<INPUT TYPE=CHECKBOX NAME='img' VALUE='$file'>".$file."<br>\n";//выводим на экран форму и имя файла
        }

endwhile;
closedir($dir); //освобождаем дескриптор каталога

if(isset($_POST['delete'])){
	$name_file = $_POST['img']; //объявляю переменную $name_file
	if (unlink("../images/small/$name_file")) //удаляю выбраный файл
		{
		echo "Файл $name_file удален";
		}
			else
			{
			echo "Файл $name_file не может быть удален"; 
			}
				}
				else
				{
				echo "Кнопка удалить не была нажата"; 
				}
?>
 <form method="post" > 
 <input type="submit" name="delete" value="Удалить"> 
 </form>
да, и ещо на ету строку ругается:
	$name_file = $_POST['img']; //объявляю переменную $name_file
Алексей, 25 Июля 2013 г. 17:04 пишет:
Автор
У Вас input не в пределах формы..
При таком коде форма выглядит вот так:
 <INPUT TYPE=CHECKBOX NAME='img' VALUE='1.jpg'>1.jpg<br>  

 <form method="post" > 
 <input type="submit" name="delete" value="Удалить"> 
 </form>
А должна выглядеть вот так:
 <form method="post" > 
   <INPUT TYPE=CHECKBOX NAME='img' VALUE='1.jpg'>2.jpg<br>  

   <input type="submit" name="delete" value="Удалить"> 
 </form>
Да кстати Ваш код не позволяет удалять несколько файлов одновременно, так как чекбоксы имеют одно и тоже имя
Ответ для пользователя: Богдан
Богдан, 25 Июля 2013 г. 17:19 пишет:
Гость
Тобишь мне нужно только перенести input и все? с кодом все в порядке?
за второе замечание я знаю, ещо не успел доделать, зациклился на этой ошибке, спасибо за помощ)
Богдан, 25 Июля 2013 г. 18:44 пишет:
Гость
да, и последний вопрос - чтобы удалять сразу несколько файлов нужно в качестве имени инпута указать name="img[]" , а что нужно сделать с POST?
Богдан, 25 Июля 2013 г. 20:05 пишет:
Гость
а все, разобрался)
Дмитрий, 19 Февраля 2014 г. 11:24 пишет:
Читатель
Алексей, благодарю за положительную реакцию. У меня не редактируются посты, точнее после редактирования после клика "Редактировать пост" ничего не происходит и появляется пустая форма (и путь командной строки http://site.ru/admin/index.php?page=edd_content)
Дмитрий, 23 Февраля 2014 г. 01:32 пишет:
Читатель
Алексей! Приветствую Вас. Начал заново (в чистую) переделывать админ панель и всё получилось, изначально делал всё в кодировке utf8, и Dreamweaver при перекодировке постоянно подкидывал проблем! (пришлось аж раза по 3 перепроверять правильность кода (скрипта) после сохранения и самой кодировки)
Андрей, 26 Октября 2015 г. 00:28 пишет:
Гость
Помогите пожалуйста разобраться в чем причина. На странице редактирования поста (для примера) admin/index.php?page=edd_content&id=1 Все поля пустые, хотя в базе есть и на сайте все выводится.. Где может быть ошибка?
Алексей, 26 Октября 2015 г. 10:46 пишет:
Автор
Посмотрите что у Вас в переменной которая содержит выборку из БД (в статье это переменная $myrow_index функции eddcontent)
Например во так:
?><xmp><?
var_dump($myrow_index);
?></xmp><?
die();
Ответ для пользователя: Андрей
Андрей, 26 Октября 2015 г. 13:05 пишет:
Гость
Спасибо Алексей разобрался, проблемы нет, все выводится из БД.
Ответ для пользователя: Алексей
Виталий, 30 Октября 2015 г. 18:44 пишет:
Гость
Подскажите,пожалуйста, когда выводится список всех постов, то при нажатии на ссылку названия поста происходит переход на http://localhost/denwer/. В чем может быть причина?
Алексей, 30 Октября 2015 г. 23:10 пишет:
Автор
Попробуйте в шаблоне, вместо
/index.php?page=edd_content&id=[_id]
написать
?page=edd_content&id=[_id]
Честно говоря я теряюсь в догадках...
Ответ для пользователя: Виталий
Виталий, 31 Октября 2015 г. 15:00 пишет:
Гость
Заработало, Спасибо!)
Ответ для пользователя: Алексей
Alex, 09 Февраля 2016 г. 00:25 пишет:
Гость
Ничего не понятно, то edd menu, то add menu. Можно было готовый пример выложить, чтобы понять где что не верно сделал и структуру файлов посмотреть.
Алексей, 10 Февраля 2016 г. 10:19 пишет:
Автор

В конце статьи есть исходник

Ответ для пользователя: Alex