Своя тема для WordPress с нуля

Несмотря на то, что WordPress перегружен кучей ненужных функций и возможностей, мне он кажется хорошим временным решением до появления собственного движка. Почему движок? Все просто – на то есть три известные причины – собственный движок всегда легче и удобнее чужих, его легко модифицировать и использовать и создание движка один из самых лучших, полезных и интересных методов изучения веб-программирования и веб-дизайна. Все это конечно замечательно, но процесс создания такого чуда надо где-то документировать, да и за два дня такое не сделать, посему и был выбран WordPress – для описания всех своих действий.

Казалось бы все хорошо, но стандартная тема «Default» мало пригодна для использования, в принципе можно взять какую-нибудь готовую, но мы легких путей не ищем, да тем более создание собственной темы, пусть и для «чужой» CMS, очень полезное и увлекательное занятие, по крайней мере мне так кажется :)

Самое важное отличие этого поста от десятков подобных – та тема, которую мы будет создавать, сейчас висит на этом блоге. Не стоит пугаться ее простоты и невзрачности, Москва тоже не сразу сроилась, а посмотрите на нее сегодня :)

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

Своя тема для WordPress с нуля

Два основных файла, без которых ничего не получится – index.php и style.css – обязательно должны лежать в папке с новой темой. Помимо них будут и другие, но без них тема в принципе уже будет работать. Первый файл отвечает за отображение главной страницы, второй за оформление. Мне кажется, если вы беретесь за создание своей темы, такие знания должны присутствовать, но я постараюсь описать весь процесс наиболее простым языком, понятным даже новичкам.

Перед тем как приступить непосредственно к созданию шаблона, необходимо поставить правильный редактор, вроде PSPada, так, на всякий случай напоминаю, и необходимо запомнить пару адресов – http://validator.w3.org/ и http://jigsaw.w3.org/css-validator/ – это сервисы проверки правильности – валидности кода, соответственно для index.php и style.css. Когда-то давно я делал кривые шаблоны и верстал как будто у меня руки из жопы одного места. Но со временем начинаешь понимать прелесть валидного кода, в котором нет ни ошибок, ни предупреждений. Так что проверять свои творения стоит как можно чаще и главное с самого начала, чтобы потом проблем меньше было. Однако даже если код правильный, не всегда он правильно отображается, поэтому у меня всегда под рукой набор разных браузеров, чего и вам советую – Mozilla Firefox, Opera, Safari (или Chrome, кому чего по душе) и, конечно же, IE - большое УГ не очень хороший браузер. Он стоит у каждого веб-разработчика, которому не хочется терять практически половину посетителей, хотя в последние месяцы ситуация радует – больше людей начинают пользоваться альтернативными браузерами.

В WordPress, да и вообще в динамических сайтах принято делить основную страницу index.php на несколько частей, обычно это header.php, sidebar.php и footer.php. Эти части подгружаются в основную страницу темы, как бы складывая ее по кирпичикам, чтобы можно было внести какие-то изменения, например, добавить копирайт внизу страницы, просто отредактировав footer.php, не меняя ничего на сотне генерируемых страниц вашего сайта. Начнем с «шапки» сайта – header.php. Я использую язык разметки XHTML 1.0 Strict. И хотя уже сейчас доступна и даже частично распространена верстка на HTML5, который радует такими прелестями как короткий doctype, контейнеры header, content и многое другое, но это я рассмотрю в отдельных статьях. Вообщем код «шапки» – header.php будет выглядеть следующим образом:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" <?php language_attributes(); ?>>
<head>
<meta http-equiv="Content-Type" content="<?php bloginfo('html_type'); ?>; charset=<?php bloginfo('charset'); ?>" />
<title><?php wp_title('&laquo;', true, 'right'); ?> <?php bloginfo('name'); ?></title>
<link rel="stylesheet" href="<?php bloginfo('stylesheet_url'); ?>" type="text/css" media="screen" />
<link rel="stylesheet" href="<?php bloginfo('template_directory'); ?>/reset.css" type="text/css" media="screen" />
<link rel="pingback" href="<?php bloginfo('pingback_url'); ?>" />
<?php wp_head(); ?>
</head>
  <body>
    <div id="wrapper">
      <div id="header">
        <h1><a href="<?php echo get_option('home'); ?>/"><?php bloginfo('name'); ?></a> &bull; <?php bloginfo('description'); ?></h1>
      </div>

Далее, как и до конца поста, подробное описание всего этого добра по строкам.

  • Строки 1-2 отвечают за правильное определение типа документа, в данном случае это XHTML Strict
  • Строка 3 открывает контейнер head
  • Строка 4 передает информацию о содержании страниц и кодировке
  • Строка 5 создает заголовок страницы, который формируется соответствующей функцией
  • Строка 6 отвечает за загрузку файла с таблицами стилей и указывает принципы его использования
  • Строка 7 отвечает за загрузку дополнительного файла стилей reset.css, о котором я буду говорить дальше.
  • Строка 8 отвечает за пингбэки
  • Строка 9 содержит функцию wp_head(), которая с одной стороны вносит много информационного мусора при формировании страниц блога на сервере, но без нее невозможно удобное использование некоторых полезных плагинов, в том числе wp-syntax, с помощью которого я оформляю код на этом блоге. А от ненужных строчек, которые формирует эта функция можно легко избавиться, но об этом позднее.
  • Строки 10-13 закрывают/открывают разные контейнеры
  • Строка 14 отвечает за отображение заголовка и описания блога в шапке
  • Строка 15 закрывает контейнер header

Сайдбар – sidebar.php, боковая колонка – обычно тут размещают разного рода ссылки, рекламу, но у нас этот файл будет самым простым, все, что здесь будет это:

1
2
3
  <div id="sidebar">
    <?php wp_loginout(); ?>	
	</div>

Подвал – footer.php не сложнее сайдбара:

1
2
3
4
5
6
7
      <div id="footer">
          &copy; <?php echo date('Y'); ?> <?php bloginfo('name'); ?> 
      </div> 
    </div>
  <?php wp_footer(); ?>
  </body>
</html>

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

А теперь – самое важное – из только что сверстанных кусков соберем главный файл страницы – index.php. Он будет выглядеть как-то так:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php get_header(); ?>
 <?php get_sidebar(); ?>
	<div id="content">
 
	<?php if (have_posts()) : ?>
 
		<?php while (have_posts()) : the_post(); ?>
 
			<div class="post">
				<h2 class="post-title"><a href="<?php the_permalink() ?>" rel="bookmark" title="<?php the_title_attribute(); ?>. Не ожидали? А мы пишем и про такое :)"><?php the_title(); ?></a></h2>
				<p class="post-info"><?php the_author() ?> опубликовал запись <?php the_time('d F Y') ?> в рубрике <?php the_category(' и ') ?> </p>
 
				<div class="entry">
				<?php the_excerpt(); ?> 
        <a href="<?php the_permalink() ?>" rel="bookmark" title="Читать полностью - &laquo;<?php the_title_attribute(); ?>&raquo;" class="readmore">Читать полностью&nbsp;&raquo;</a> 
				</div>
 
				<p class="postmetadata"><?php the_tags('Тэги: ', ', ', '<br />'); ?> <?php comments_popup_link('Нет комментариев &#187;', '1 комментарий &#187;', 'Комментариев: % &#187;'); ?></p>
			</div>
 
		<?php endwhile; ?>
 
		<div class="navigation">
			<div class="alignleft"><?php next_posts_link('&laquo; Старые записи') ?></div>
			<div class="alignright"><?php previous_posts_link('Новые записи &raquo;') ?></div>
		</div>
 
	<?php else : ?>
 
		<h2 class="center">Не найдено</h2>
		<p class="center">Извините, но, похоже, Вы ищите то, чего здесь нет.</p>
		<?php get_search_form(); ?>
 
	<?php endif; ?>
 
	</div>
 
<?php get_footer(); ?>
  • Строки 1-2 отвечают за подключение файлов шапки и сайдбара, которые мы только что создали, и потом открывается контейнер content
  • Строки 5 и 7 содержат две функции, которые проверяют наличие записей на блоге и выводят их
  • Строка 9 открывает контейнер, котором выводятся записи
  • Строки 10-11 выводят ссылку-название записи, всплывающую подсказку, автора записи, рубрику и время публикации
  • Строка 13 открывает контейнер, который содержит сам текст записи
  • Строка 14 в этой версии темы выводит короткий анонс записи без форматирования, на данный момент это более приемлимое решение для этого блога.
  • Строки 15-19 отвечают за отображение ссылки «читать дальше», тэгов присвоенных записи и количества комментариев
  • Строка 21 содержит функцию, которая, когда заканчиваются все или допустимое количество записей, выводит следующие и предыдущие записи
  • Строки 30-32 выводят предложение поискать получше
  • Строка 38 подключает файл подвала.

Ура, основная часть шаблона сверстана, осталось оформить файл вывода статьи – single.php, который загружается при просмотре полного текста статей. Он, как и index.php, собирается из шапки, сайдбара и подвала, которые я уже описал.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<?php get_header(); ?>
 <?php get_sidebar(); ?>
	<div id="content">
 
	<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
 
		<div class="navigation">
			<div class="alignleft"><?php previous_post_link('&laquo; %link') ?></div>
			<div class="alignright"><?php next_post_link('%link &raquo;') ?></div>
		</div>
 
		<div class="post">
			<h2 class="post-title"><?php the_title(); ?></h2>
			<p class="post-info"><?php the_author() ?> опубликовал запись в рубрике <?php the_category(' и ') ?> <?php the_time('d F Y') ?></p>
 
			<div class="entry">
				<?php the_content(); ?>
			 <hr /> 
       <?php the_tags( '<p>Ахтунг! Всякое про ', ', ', ' детектед</p>'); ?>
 
				<p>
 
            <?php if ( comments_open() && pings_open() ) {  
							 ?>
 
						<?php } elseif ( !comments_open() && pings_open() ) {
							 ?>
							Комментирование запрещено, но вы можете оставить <a href="<?php trackback_url(); ?> " rel="trackback">трэкбек</a> с Вашего сайта.
 
						<?php } elseif ( comments_open() && !pings_open() ) {
							?>
							Пингбеки запрещены, только комментарии.
 
						<?php } elseif ( !comments_open() && !pings_open() ) {
							?>
							Комментирование запрещено.
 
						<?php } edit_post_link('Редактировать','','.'); ?>
 
      </p>
 
			</div>
			<hr />
		</div>
 
	<?php comments_template(); ?>
 
	<?php endwhile; else: ?>
 
		<p>Ничего не нашлось, попробуйте еще.</p>
 
<?php endif; ?>
 
	</div>
 
<?php get_footer(); ?>
  • Строки 1-16 по аналогии с index.php
  • Строка 17 выводит полное содержание записи
  • Строки 18-20 выводят горизонтальную линию и тэги, связанные с записью
  • Строки 21-40 выводят различные сообщения в зависимости от того закрыто комментирование или нет
  • Строка 46 подключает файл с шаблоном страницы комментариев, по умолчанию comments.php
  • Строки 48-50 выводят предложение поискать получше
  • Строка 56 подключает файл подвала

Теперь остался собственно файл comments.php.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
<?php
 
// Do not delete these lines
	if (!empty($_SERVER['SCRIPT_FILENAME']) && 'comments.php' == basename($_SERVER['SCRIPT_FILENAME']))
		die ('Please do not load this page directly. Thanks!');
 
	if ( post_password_required() ) { ?>
		<p class="nocomments">Запись защищена паролем. Пожалуйста, введите пароль для ее просмотра.</p>
	<?php
		return;
	}
?>
 
<?php if ( have_comments() ) : ?>
	<p><?php comments_number('Нет комментариев', 'Один комментарий', 'Комментариев: %' );?></p>
 
	<div class="navigation">
		<div class="alignleft"><?php previous_comments_link() ?></div>
		<div class="alignright"><?php next_comments_link() ?></div>
	</div>
 
	<ol class="commentlist">
	<?php wp_list_comments('avatar_size=80'); ?>
	</ol>
 
	<div class="navigation">
		<div class="alignleft"><?php previous_comments_link() ?></div>
		<div class="alignright"><?php next_comments_link() ?></div>
	</div>
 <?php else : ?>
 
	<?php if ( comments_open() ) : ?>
		<!-- If comments are open, but there are no comments. -->
 
	 <?php else : ?>
		<!-- If comments are closed. -->
		<p>Комментирование закрыто.</p>
 
	<?php endif; ?>
<?php endif; ?>
 
 
<?php if ( comments_open() ) : ?>
 
<div>
 
<p><?php comment_form_title( 'Оставить комментарий'); ?></p>
 
<?php if ( get_option('comment_registration') && !is_user_logged_in() ) : ?>
 
<?php else : ?>
 
<form action="<?php echo get_option('siteurl'); ?>/wp-comments-post.php" method="post" id="commentform">
 
<?php if ( is_user_logged_in() ) : ?>
 
 
 
<p>Вы в системе как <a href="<?php echo get_option('siteurl'); ?>/wp-admin/profile.php"><?php echo $user_identity; ?></a>. <a href="<?php echo wp_logout_url(get_permalink()); ?>" title="Выйти из системы">Выйти &raquo;</a></p>
 
<?php else : ?>
 
<p><input type="text" name="author" id="author" value="<?php echo esc_attr($comment_author); ?>" size="22" tabindex="1" <?php if ($req) echo "aria-required='true'"; ?> />
<label for="author"><small>Имя <?php if ($req) echo "(необходимо)"; ?></small></label></p>
 
<p><input type="text" name="email" id="email" value="<?php echo esc_attr($comment_author_email); ?>" size="22" tabindex="2" <?php if ($req) echo "aria-required='true'"; ?> />
<label for="email"><small>E-mail (не будет опубликован) <?php if ($req) echo "(необходим)"; ?></small></label></p>
 
<p><input type="text" name="url" id="url" value="<?php echo esc_attr($comment_author_url); ?>" size="22" tabindex="3" />
<label for="url"><small>Вебсайт</small></label></p>
 
<?php endif; ?>
 
<!-- <p>Используйте эти тэги, но в меру.<br /><code><?php echo allowed_tags(); ?></code></p>   -->
 
<p><textarea name="comment" id="comment" cols="58" rows="10" tabindex="4"></textarea></p>
 
<p><input name="submit" type="submit" id="submit" tabindex="5" value="Оставить комментарий" />
<?php comment_id_fields(); ?>
<?php do_action('comment_form', $post->ID); ?>
</p>
 
 
</form>
 
<?php endif; ?>
</div>
 
<?php endif; ?>
  • За что отвечают строки 3-5 я пока не разобрался, но пишут, что удалять их не стоит, так что мы и не будем
  • Строки 7-8 отвечают за вывод соответствующего сообщения если запись под паролем
  • Строки 14-20 отвечают за вывод информации о количестве комментариев и ссылок на предыдущие или следующие страницы с комментариями к записи
  • Строки 22-24 выводят комментарии и аватары размером 80*80 пикселей
  • Строки 26-29 выводят ссылки на предыдущие или следующие страницы с комментариями к записи, их можно убрать, так как они дублируют верхние
  • Строки 32-45 отвечают за вывод соответствующего сообщения, если комментирование закрыто или разрешают комментировать запись
  • Строки 47-61 выводят предложение оставить комментарий, показывают зарегистрированы/залогинены ли вы и отвечают за комментарии администратора блога
  • Строки 63-70 выводят форма для ввода сайта, почты и имени
  • Строка 74 выводит разрешенные к использованию тэги, у меня она закомментирована, кому надо раскомментирует
  • Строки 76-78 выводят поле для ввода комментария и кнопку отправки комментария

В начале этого поста я рассказывал о функции wp_head(), которая передает много лишней информации, вроде того, что ваш блог сгенерирован wordpress, если вас это не устраивает, создайте файл functions.php такого вида:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
remove_action( 'wp_head', 'feed_links_extra', 3 ); 
remove_action( 'wp_head', 'feed_links', 2 );
remove_action( 'wp_head', 'rsd_link' );
remove_action( 'wp_head', 'wlwmanifest_link' );
remove_action( 'wp_head', 'index_rel_link' );
remove_action( 'wp_head', 'parent_post_rel_link', 10, 0 ); 
remove_action( 'wp_head', 'start_post_rel_link', 10, 0 );
remove_action( 'wp_head', 'adjacent_posts_rel_link', 10, 0 );
remove_action( 'wp_head', 'wp_generator' );
remove_action( 'wp_head', 'canonical' );
?>

Основная работа проделана, теперь осталось поработать с таблицей стилей style.css, чтобы блог не выглядел как волшебное УГ. Особых изысков описывать я не буду – один из самых простых дизайнов. Зато со временем я буду добавлять различные элементы и модифицировать его, описывая это в статьях.

/*
Theme Name: DWY Public (DWYpub)
Theme URI: http://dowebyourself.com
Description: Simple wordpress theme for public use
Version: 0.15
Author: Dr. Kabatchok
Author URI: http://twitter.com/y0ung5eo
Tags: simple, free
 
	The CSS, XHTML and design is released under GPL:
	http://www.opensource.org/licenses/gpl-license.php
 
*/
 
/*Body etc.*/
 
body {
  font: 15px "Trebuchet MS", Arial, Helvetica, sans-serif;
  color: #555;
  }
 
a {
  text-decoration: underline;
  color: #555;
  }
 
a:hover {
  color: #ca83b8;
  border: none;
  }
 
#wrapper {
  width: 980px;
  margin: 0 auto;
  padding: 5px 5px;
  }
 
/*Header*/
 
#header {
  background-color: #e5e5e5;
  width: 960px;
  height: 100px;
  margin: 5px auto 0px;
  border: 1px solid #333;
  }
 
#header h1 {
  font-size: 24px;
  }
 
/*Content*/
 
#content {
  background-color: #fff;
  margin: 10px 0 10px 10px;
  width: 700px;
  padding: 0 20px 10px 20px;
  border: 1px solid #333;
  }
 
.post {
  text-align: justify;
}  
 
.post-title {
  font-size: 20px;
}
 
.post-info {
  font-size: 12px;
  margin: 0 0 5px 20px; 
}
 
.postmetadata {
  font-size: 12px; 
  text-align: center; 
  margin: 50px 0; 
  line-height: 1.3em; 
  background-color: #e5e5e5;
}
 
.readmore { 
  float: right; 
  line-height: 2; 
  font-size: 12px;
 
}
 
.navigation {
  display: block;
  text-align: center;
  margin-top: 10px;
  margin-bottom: 30px;
}
 
.alignright {
  float: right;
}
 
.alignleft {
  float: left;
}
 
 
/*Entry*/
 
 
.entry {
  margin: 10px 0;
  line-height: 1.25em;
  letter-spacing: 0.06em;
}
 
.entry p {
  margin: 10px 0;
}
 
.entry ol {
  margin-left: 0px;
  padding: 0 0 0 30px;
  list-style-type: decimal;
}
 
.entry ul {
  margin-left: 0px;
  padding: 0 0 0 30px;
  list-style-type: disc;
}	
 
.entry li {
  margin: 7px 0 8px 10px;
}
 
 
/*Siderbar*/
 
 
#sidebar {
  background-color: #eed2e7;
  float: right;
  width: 200px;
  margin: 10px 10px 0 0;
  padding: 5px 0 5px 5px;
  color: #555;
  border: 1px solid #333;
  }
 
#sidebar a {
  text-decoration: none;
  }
 
 
/*Footer*/
 
 
#footer {
  background-color: #e5e5e5;
  width: 960px;
  margin: 10px auto;
  padding: 10px 0;
  text-align: center;
  border: 1px solid #333;
  }
 
 
/*Comments*/
 
 
.commentlist li .avatar { 
  float: right;
  border: 1px solid #e5e5e5;
  padding: 2px;
  background: #fff;
  margin: 0 0 0px 5px;
}
 
.commentlist cite, .commentlist cite a {
  line-height: 1.5em;
  font-style: normal;
}
 
.commentlist p {
  font-weight: normal;
  line-height: 1.5em;
  text-transform: none;
}	
 
.comment {
  font-size: 11px;
}
 
.comment-body {
  font-size: 14px;
  margin: 25px 0;
  width: 700px;
  text-align: justify;
}
 
 
/*Commentform*/
 
 
#commentform {
  margin: 5px 10px 0 0;
  }
 
#commentform input {
  width: 170px;
  padding: 2px;
  margin: 5px 5px 1px 0;
  border: 2px solid #e5e5e5;
  }
 
#commentform textarea {
  width: 100%;
  padding: 2px;
  border: 2px solid #e5e5e5;
  }
 
#commentform #submit {
  margin: 5px 0 5px;
  }
  • Закомментированные строчки в начале файла содержат описание темы, которое будет отображаться в админке wordpress
  • Раздел Body etc. содержит правила оформления ссылок и темы в целом
  • Раздел Header содержит правила оформления заголовка блога
  • Раздел Content содержит правила оформления основного контента на страницах, ссылок на предыдущие/следующие записи и самих записей
  • Раздел Entry содержит правила оформления единичной записи
  • Раздел Siderbar содержит правила оформления сайдбара
  • Раздел Footer содержит правила оформления подвала
  • Раздел Comments содержит правила оформления страницы комментариев
  • Раздел Commentform содержит правила оформления форм для ввода личных данных

Не знаю почему, но мне не попадались примеры использования файла reset.css в темах WordPress – или я мало тем повидал или может, не принято так. Но, так или иначе, сброс стилей осуществлять очень даже желательно, если не обязательно, причем не через универсальный селектор, как обычно делают, а через отдельный файл reset.css. Конечно у каждого свое мнение на этот счет, а кто-то даже не догадывается, о чем я сейчас говорю. Гугл и яндекс вам в помощь :)

Содержание reset.css у меня такое:

body, html, div, blockquote, img, label, p, h1, h2, h3, h4, h5, h6, ul, ol, li, 
form, a, fieldset, input, table, td, tr  {     
    margin: 0; padding: 0; border: 0; outline: none;
}
 
body {
    line-height: 1;
} 
 
h1, h2, h3, h4, h5, h6 {  
    padding: .6em 0;
    margin: 0 15px; 
    font-size: 100%; 
} 
 
ul, ol {  
    list-style: none;  
}

Говоря кратко – этот файл сбрасывает стили браузеров и делает возможным одинаковое отображение содержимого страниц в IE, FF, Опере и других браузерах.

Вот вы и создали тему, практически точную копию которой вы можете лицезреть на блоге Do Web Yourself в данный момент! Как уже я упоминал выше, вы можете скачать эту тему, чтобы ставить над ней всяческие бесчеловечные эксперименты :)

P.S. Эту статью я писал практически месяц, отвлекаясь на другие дела и разбираясь с php функциями wordpress, надеюсь, кому-то пригодится мой труд, но самое главное в проделанной работе – полученные знания и опыт. Следующие несколько статей не будут такими большими как эта, в них я рассмотрю некоторые косметические исправления в дизайне и структуре блога.

Тэги: / /
  • http://codegust.com Noisy Wizard

    Труд определенно пригодиться, главное не забыть, что я хотел сделать тему для wp сам) Удачи

  • Доктор Кабачок

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

blog comments powered by Disqus