<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Программирование &#8902; Clip-Clap</title>
	<atom:link href="https://clip-clap.ru/category/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/feed/" rel="self" type="application/rss+xml" />
	<link>https://clip-clap.ru/category/it/программирование/</link>
	<description></description>
	<lastBuildDate>Mon, 23 Nov 2020 15:17:08 +0000</lastBuildDate>
	<language>ru-RU</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.5.8</generator>

<image>
	<url>https://clip-clap.ru/wp-content/uploads/2020/07/cropped-favicon-32x32.png</url>
	<title>Программирование &#8902; Clip-Clap</title>
	<link>https://clip-clap.ru/category/it/программирование/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Что такое: Open Source (Открытый исходный код)</title>
		<link>https://clip-clap.ru/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d1%87%d1%82%d0%be-%d1%82%d0%b0%d0%ba%d0%be%d0%b5-open-source-%d0%be%d1%82%d0%ba%d1%80%d1%8b%d1%82%d1%8b%d0%b9-%d0%b8%d1%81%d1%85%d0%be%d0%b4%d0%bd%d1%8b%d0%b9-%d0%ba%d0%be%d0%b4/</link>
					<comments>https://clip-clap.ru/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d1%87%d1%82%d0%be-%d1%82%d0%b0%d0%ba%d0%be%d0%b5-open-source-%d0%be%d1%82%d0%ba%d1%80%d1%8b%d1%82%d1%8b%d0%b9-%d0%b8%d1%81%d1%85%d0%be%d0%b4%d0%bd%d1%8b%d0%b9-%d0%ba%d0%be%d0%b4/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Fri, 06 Nov 2020 18:23:14 +0000</pubDate>
				<category><![CDATA[Администрирование]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[Open Source]]></category>
		<guid isPermaLink="false">https://clip-clap.ru/?p=2509</guid>

					<description><![CDATA[<p>Открытый исходный код – это термин, используемый для описания компьютерных программ с их исходным кодом доступных для всех, чтобы учиться.</p>
<p>Сообщение <a href="https://clip-clap.ru/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d1%87%d1%82%d0%be-%d1%82%d0%b0%d0%ba%d0%be%d0%b5-open-source-%d0%be%d1%82%d0%ba%d1%80%d1%8b%d1%82%d1%8b%d0%b9-%d0%b8%d1%81%d1%85%d0%be%d0%b4%d0%bd%d1%8b%d0%b9-%d0%ba%d0%be%d0%b4/">Что такое: Open Source (Открытый исходный код)</a> появились сначала на <a href="https://clip-clap.ru">Clip-Clap</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Открытый исходный код – это термин, используемый для описания компьютерных программ с их исходным кодом доступных для всех, чтобы учиться. WordPress является программным&nbsp;обеспечением с открытым исходным кодом, и любой может использовать, изучать, изменять и распространять исходный код.</p>



<p>Модель разработки программного обеспечения с открытым исходным кодом позволяет другим не только изучить код, но и улучшить его с помощью тестирования, сообщений об ошибках, и патчей. Сторонники этой модели развития утверждают, что открытая среда разработки позволяет разработчикам ПО быстро находить и устранять лазейки безопасности и ошибки в программном обеспечении.</p>


</br>



<p>Противники модели разработки с открытым исходным кодом утверждают, что программное обеспечение с открытым исходным кодом на самом деле более уязвимы, потому что источник легко доступен для любого изучения и взлома.Они также утверждают, что программное обеспечение с открытым исходным кодом поставляется абсолютно без каких либо&nbsp;гарантий, и если что-то плохое случится, никто не несет ответственность за причиненный ущерб.</p>



<p>Эти недостатки, как правило, компенсируется, однако, расширяемостью и настраиваемостью программного обеспечения с открытым исходным кодом. Это легко увидеть в таких проектах, как WordPress. Благодаря&nbsp;его лицензий с открытым исходным кодом, WordPress стала платформой для динамической веб-публикации, чем и&nbsp;является она сегодня.</p>
<p>Сообщение <a href="https://clip-clap.ru/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d1%87%d1%82%d0%be-%d1%82%d0%b0%d0%ba%d0%be%d0%b5-open-source-%d0%be%d1%82%d0%ba%d1%80%d1%8b%d1%82%d1%8b%d0%b9-%d0%b8%d1%81%d1%85%d0%be%d0%b4%d0%bd%d1%8b%d0%b9-%d0%ba%d0%be%d0%b4/">Что такое: Open Source (Открытый исходный код)</a> появились сначала на <a href="https://clip-clap.ru">Clip-Clap</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://clip-clap.ru/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d1%87%d1%82%d0%be-%d1%82%d0%b0%d0%ba%d0%be%d0%b5-open-source-%d0%be%d1%82%d0%ba%d1%80%d1%8b%d1%82%d1%8b%d0%b9-%d0%b8%d1%81%d1%85%d0%be%d0%b4%d0%bd%d1%8b%d0%b9-%d0%ba%d0%be%d0%b4/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Создание бота Вк на языке PHP &#8212; уроки API Вк</title>
		<link>https://clip-clap.ru/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d1%81%d0%be%d0%b7%d0%b4%d0%b0%d0%bd%d0%b8%d0%b5-%d0%b1%d0%be%d1%82%d0%b0-%d0%b2%d0%ba-%d0%bd%d0%b0-%d1%8f%d0%b7%d1%8b%d0%ba%d0%b5-php-%d1%83%d1%80%d0%be%d0%ba%d0%b8-api-%d0%b2%d0%ba/</link>
					<comments>https://clip-clap.ru/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d1%81%d0%be%d0%b7%d0%b4%d0%b0%d0%bd%d0%b8%d0%b5-%d0%b1%d0%be%d1%82%d0%b0-%d0%b2%d0%ba-%d0%bd%d0%b0-%d1%8f%d0%b7%d1%8b%d0%ba%d0%b5-php-%d1%83%d1%80%d0%be%d0%ba%d0%b8-api-%d0%b2%d0%ba/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Sun, 18 Oct 2020 10:48:17 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[Бот]]></category>
		<category><![CDATA[вк]]></category>
		<category><![CDATA[ВКонтакте]]></category>
		<guid isPermaLink="false">https://clip-clap.ru/?p=2458</guid>

					<description><![CDATA[<p>Боты стали новой эпохой в соц сетях и мессенджерах. Мы расскажем как создать настоящего бота на PHP, используя API социальной</p>
<p>Сообщение <a href="https://clip-clap.ru/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d1%81%d0%be%d0%b7%d0%b4%d0%b0%d0%bd%d0%b8%d0%b5-%d0%b1%d0%be%d1%82%d0%b0-%d0%b2%d0%ba-%d0%bd%d0%b0-%d1%8f%d0%b7%d1%8b%d0%ba%d0%b5-php-%d1%83%d1%80%d0%be%d0%ba%d0%b8-api-%d0%b2%d0%ba/">Создание бота Вк на языке PHP &#8212; уроки API Вк</a> появились сначала на <a href="https://clip-clap.ru">Clip-Clap</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Боты стали новой эпохой в соц сетях и мессенджерах. Мы расскажем как создать настоящего бота на PHP, используя API социальной сети Вконтакте.</p>



<p>Для создание ботов в социальной сети Вконтакте нужно несколько вещей:</p>



<ol><li>Специальный&nbsp;<code>standalone token</code>;</li><li>Токен вашей группы;</li><li>Сам скрипт с ботом;</li><li>Группа с подключенным сервером и&nbsp;<strong>API</strong>.</li></ol>


</br>



<h2 class="wp-block-heading">Получение токена</h2>



<p>Первым делом необходимо получить&nbsp;<code>standalone token</code>&nbsp;вместе с токеном вашей группы. Для этого переходим в раздел&nbsp;<a rel="noreferrer noopener nofollow" target="_blank" href="https://vk.com/dev">для разработчиков</a>&nbsp;и в &#171;<a rel="noreferrer noopener nofollow" target="_blank" href="https://vk.com/apps?act=manage">Мои приложения</a>&#171;. Здесь создаем новое приложение. Название приложения можете выбрать любым, главное чтобы оно было&nbsp;Standalone-приложение.</p>



<figure class="wp-block-image size-large is-resized"><img fetchpriority="high" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/10/15358142571-1024x411.png" alt="" class="wp-image-2459" width="1147" height="461" srcset="https://clip-clap.ru/wp-content/uploads/2020/10/15358142571-1024x411.png 1024w, https://clip-clap.ru/wp-content/uploads/2020/10/15358142571-300x120.png 300w, https://clip-clap.ru/wp-content/uploads/2020/10/15358142571-768x308.png 768w, https://clip-clap.ru/wp-content/uploads/2020/10/15358142571.png 1454w" sizes="(max-width: 1147px) 100vw, 1147px" /></figure>



<p>Включите&nbsp;<strong>Open API</strong>&nbsp;после создания приложения. В него пропишите сайт, с которым будет связано ваше приложение, а также базовый домен. </p>



<figure class="wp-block-image size-large is-resized"><img decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/10/1535814407.png" alt="" class="wp-image-2460" width="1140" height="401" srcset="https://clip-clap.ru/wp-content/uploads/2020/10/1535814407.png 876w, https://clip-clap.ru/wp-content/uploads/2020/10/1535814407-300x105.png 300w, https://clip-clap.ru/wp-content/uploads/2020/10/1535814407-768x270.png 768w" sizes="(max-width: 1140px) 100vw, 1140px" /></figure>



<p>После этого необходимо перейти по&nbsp;<a rel="noreferrer noopener" target="_blank" href="https://oauth.vk.com/authorize?client_id=6678654&amp;display=page&amp;redirect_uri=https://oauth.vk.com/blank.html&amp;scope=friends,photos,audio,video,status,messages,wall,groups,offline,stats,email&amp;response_type=token&amp;v=5.63">этой </a><a href="https://oauth.vk.com/authorize?client_id=6678654&amp;display=page&amp;redirect_uri=https://oauth.vk.com/blank.html&amp;scope=friends,photos,audio,video,status,messages,wall,groups,offline,stats,email&amp;response_type=token&amp;v=5.63" target="_blank" rel="noreferrer noopener nofollow">с</a><a rel="noreferrer noopener" target="_blank" href="https://oauth.vk.com/authorize?client_id=6678654&amp;display=page&amp;redirect_uri=https://oauth.vk.com/blank.html&amp;scope=friends,photos,audio,video,status,messages,wall,groups,offline,stats,email&amp;response_type=token&amp;v=5.63">сылке</a>. Также вы её видите ниже:</p>



<pre class="wp-block-code"><code>https:&#47;&#47;oauth.vk.com/authorize?client_id=6678654&amp;display=page&amp;redirect_uri=https://oauth.vk.com/blank.html&amp;scope=friends,photos,audio,video,status,messages,wall,groups,offline,stats,email&amp;response_type=token&amp;v=5.63</code></pre>


</br>



<p>Единственное, вам необходимо изменить&nbsp;<code>client_id</code>&nbsp;и установить чтобы оно было равным ID вашего приложения.</p>



<p>Далее вы нажимаете на кнопку&nbsp;Разрешить&nbsp;и после этого получаете токен, который будет записан в ссылке. Копируете токен от знака равенства (<code>=</code>) и до амперсанда (<code>&amp;</code>).</p>



<p>После этого создайте новую группу или воспользуйтесь вашей существующей группой. Зайдите в настройке, после чего в&nbsp;Ключи доступа&nbsp;и нажмите&nbsp;<strong>Создать ключ</strong>. Установите все права для ключа и после этого вы сможете получить токен вашей группы, который вам пригодиться для создания бота.</p>



<h2 class="wp-block-heading">Создание самого бота</h2>



<p>Для создания бота нам понадобиться три файла:</p>



<ul><li><code>config</code>&nbsp;&#8212; файл служащий исключительно для нас. В нем мы запишем различные настройки и данные, которые сможем в дальнейшем использовать в других файлах;</li><li><code>photos</code>&nbsp;&#8212; файл для обработки альбома. В этом файле мы будем обрабатывать альбом, получать из него все изображение и записывать их в новый&nbsp;<strong>txt</strong>&nbsp;файл в специальном формате, который в дальнейшем будем передавать в качестве прикрепленного изображения к сообщению;</li><li><code>bot</code>&nbsp;&#8212; основной файл с ботом. Здесь мы прописываем обработку данных от пользователя, а также передачу сообщений от бота к пользователю Вконтакте.</li></ul>



<p>Ниже представлены эти три файла. Они полностью готовы и с рабочим кодом. Детальное описание всего что в них происходит вы можете получить из видео.</p>



<p>Скрипт config:</p>



<pre class="wp-block-code"><code>$album = 'https://vk.com/album-113958919_252151167';
$res = parse_url($album);
$path = substr($res&#091;'path'], 6);
$arr = explode('_', $path);
$owner_id = $arr&#091;0];
$album_id = $arr&#091;1];

$standalone = "ваш standalone токен приложения";
$group_token = 'ваш токен группы';
$conf = &#091;
	'standalone' => $standalone,
	'group_token' => $group_token,
	'contorm_token' => 'eae5d4a2',
	'mess' => 'Фото в студио',
	'not_command' => 'Ничего не понял!',
	'owner_id' => $owner_id,
	'album_id' => $album_id,
	'group_id' => '170785666',
	'apiurl' => 'https://api.vk.com/method/',
	'path' => substr($_SERVER&#091;'PHP_SELF'], 0, -2),
	'photos' => 'photos.txt',
	'temp_link' => 'temp_album.txt',
	'random_id' => mt_rand(0000000000, 999999999999),
	'v' => '5.50'
];</code></pre>


</br>



<p>Скрипт photos:</p>



<pre class="wp-block-code"><code>unlink($conf&#091;"photos"]);
$query = file_get_contents($conf&#091;'apiurl'].'photos.get?owner_id='.$conf&#091;'owner_id'].'&amp;album_id='.$conf&#091;'album_id'].'&amp;v='.$conf&#091;'v'].'&amp;access_token='.$conf&#091;'standalone']);
$res = json_decode($query, true);

foreach($res as $v) {
	foreach($v&#091;'items'] as $q) {
		$result = 'photo'.$q&#091;'owner_id'].'_'.$q&#091;'id'];
		file_put_contents($conf&#091;'photos'], $result."\n", FILE_APPEND | LOCK_EX);
	}
}</code></pre>



<p>Скрипт bot:</p>



<pre class="wp-block-code"><code>require "config.php";
$data = json_decode(file_get_contents('php://input'));
$u_id = $data->object->user_id;
$mess = $data->object->body;

$user_info = json_decode(file_get_contents($conf&#091;'apiurl'].'users.get?user_id='.$u_id.'&amp;v='.$conf&#091;'v'].'&amp;access_token='.$conf&#091;'standalone']));
$user_name = $user_info->response&#091;0]->first_name;

$temp_link = file($conf&#091;'temp_link']);
if($temp_link&#091;0] != $album) {
	file_put_contents($conf&#091;'temp_link'], $album);
	require "photos.php";
	return true;
}

switch($data->type) {
	case 'confirmation':
		echo $conf&#091;'contorm_token'];
		break;
	case "message_new":
		if($mess == $conf&#091;'mess']) {
			$file = file_get_contents($conf&#091;'photos']);
			$photos_all = explode("\n", $file);
			
			$myCurl = curl_init();
			curl_setopt_array($myCurl, array(
				CURLOPT_URL => $conf&#091;'apiurl'].'messages.send?user_id='.$u_id.'&amp;group_id='.$conf&#091;'group_id'].'&amp;attachment='.$photos_all&#091;mt_rand(0, count($photos_all) - 1)].'&amp;message='.urlencode('Держи свое фото').'&amp;v='.$conf&#091;'v'].'&amp;access_token='.$conf&#091;'standalone'],
				CURLOPT_RETURNTRANSFER => true,
				CURLOPT_POST => true,
				CURLOPT_POSTFIELDS => http_build_query(array())
			));
			$response = curl_exec($myCurl);
			curl_close($myCurl);
		} else {
			$myCurl = curl_init();
			curl_setopt_array($myCurl, array(
				CURLOPT_URL => $conf&#091;'apiurl'].'messages.send?user_id='.$u_id.'&amp;group_id='.$conf&#091;'group_id'].'&amp;message='.urlencode($conf&#091;'not_command']).'&amp;v='.$conf&#091;'v'].'&amp;access_token='.$conf&#091;'standalone'],
				CURLOPT_RETURNTRANSFER => true,
				CURLOPT_POST => true,
				CURLOPT_POSTFIELDS => http_build_query(array())
			));
			$response = curl_exec($myCurl);
			curl_close($myCurl);
		}
		echo 'ok';
		break;
}</code></pre>



<p>Детальное описание создания бота на языке&nbsp;<a href="https://clip-clap.ru/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d0%b8%d0%b7%d1%83%d1%87%d0%b5%d0%bd%d0%b8%d0%b5-php-%d0%be%d1%81%d0%bd%d0%be%d0%b2%d1%8b-php/" target="_blank" rel="noreferrer noopener">PHP</a>&nbsp;под систему Вконтакте:</p>



<figure class="wp-block-embed-youtube wp-block-embed is-type-video is-provider-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe title="Создание бота Вк на языке PHP за 40 минут / Уроки API Вконтакте" width="800" height="450" src="https://www.youtube.com/embed/LhoJmonbUis?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div></figure>


</br>



<p>Последнее что необходимо будет сделать, так это прописать URL адрес скрипта&nbsp;<strong>bot.php</strong>&nbsp;в группе, в настройках API:</p>



<figure class="wp-block-image size-large is-resized"><img loading="lazy" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/10/15358158501-1024x384.png" alt="" class="wp-image-2461" width="1141" height="428" srcset="https://clip-clap.ru/wp-content/uploads/2020/10/15358158501-1024x384.png 1024w, https://clip-clap.ru/wp-content/uploads/2020/10/15358158501-300x113.png 300w, https://clip-clap.ru/wp-content/uploads/2020/10/15358158501-768x288.png 768w, https://clip-clap.ru/wp-content/uploads/2020/10/15358158501.png 1104w" sizes="(max-width: 1141px) 100vw, 1141px" /></figure>



<p>После этого можете написать сообщение в группу и бот будет вам отвечать. Напишите &#171;Фото в студио&#187; и бот пришлет вам случайную фотографию из альбома.</p>


</br>
<p>Сообщение <a href="https://clip-clap.ru/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d1%81%d0%be%d0%b7%d0%b4%d0%b0%d0%bd%d0%b8%d0%b5-%d0%b1%d0%be%d1%82%d0%b0-%d0%b2%d0%ba-%d0%bd%d0%b0-%d1%8f%d0%b7%d1%8b%d0%ba%d0%b5-php-%d1%83%d1%80%d0%be%d0%ba%d0%b8-api-%d0%b2%d0%ba/">Создание бота Вк на языке PHP &#8212; уроки API Вк</a> появились сначала на <a href="https://clip-clap.ru">Clip-Clap</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://clip-clap.ru/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d1%81%d0%be%d0%b7%d0%b4%d0%b0%d0%bd%d0%b8%d0%b5-%d0%b1%d0%be%d1%82%d0%b0-%d0%b2%d0%ba-%d0%bd%d0%b0-%d1%8f%d0%b7%d1%8b%d0%ba%d0%b5-php-%d1%83%d1%80%d0%be%d0%ba%d0%b8-api-%d0%b2%d0%ba/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Изучение PHP &#124; Основы PHP</title>
		<link>https://clip-clap.ru/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d0%b8%d0%b7%d1%83%d1%87%d0%b5%d0%bd%d0%b8%d0%b5-php-%d0%be%d1%81%d0%bd%d0%be%d0%b2%d1%8b-php/</link>
					<comments>https://clip-clap.ru/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d0%b8%d0%b7%d1%83%d1%87%d0%b5%d0%bd%d0%b8%d0%b5-php-%d0%be%d1%81%d0%bd%d0%be%d0%b2%d1%8b-php/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Sun, 18 Oct 2020 10:46:30 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[урок]]></category>
		<guid isPermaLink="false">https://clip-clap.ru/?p=2463</guid>

					<description><![CDATA[<p>PHP – это один из самых распространённых языков сценариев с широкой сферой применения и opensource-кодом. Если упростить определение, это язык</p>
<p>Сообщение <a href="https://clip-clap.ru/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d0%b8%d0%b7%d1%83%d1%87%d0%b5%d0%bd%d0%b8%d0%b5-php-%d0%be%d1%81%d0%bd%d0%be%d0%b2%d1%8b-php/">Изучение PHP | Основы PHP</a> появились сначала на <a href="https://clip-clap.ru">Clip-Clap</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>PHP – это один из самых распространённых языков сценариев с широкой сферой применения и opensource-кодом. Если упростить определение, это язык программирования, использующийся для создания веб-приложений (сценариев) и исполняющийся на стороне сервера.</p>



<h3 class="wp-block-heading">Видеоурок</h3>



<figure class="wp-block-embed-youtube wp-block-embed is-type-video is-provider-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="Изучение PHP для начинающих | Урок #1 - Основы PHP" width="800" height="450" src="https://www.youtube.com/embed/jqF6LYh6y2U?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div></figure>



<p>Вначале пути по освоению PHP нужно познакомиться с теорией и определениями. Первоочередной вопрос, что собой представляет PHP?</p>


</br>



<h3 class="wp-block-heading">Базовое представление о PHP</h3>



<p><strong>PHP</strong> – это аббревиатура от «Hypertext Preprocessor». Корни синтаксиса уходят глубоко в C, Java и Perl. Язык довольно легко даётся в изучение. Главным его преимуществом является быстрый и простой доступ к генерированию веб-страниц. Ещё одно важное достоинство PHP в сравнении с Perl или C – это блоки PHP-кода, которые можно внедрять прямо в документ с HTML.</p>



<p>Отличительная черта языка заключается в том, что он обрабатывается на стороне сервера. При помощи нехитрых действий можно создать такой код, что автоматически создаёт страницу, ничем не отличающуюся от чистого HTML-кода. Пользователь не сможет догадаться что перед ним обычный HTML или результат работы PHP.</p>



<p>На основе PHP реально создавать высокопроизводительные и нагруженные веб-приложения в сжатые сроки. Готовые продукты несложно редактировать, улучшать и поддерживать.</p>



<figure class="wp-block-image size-large is-resized"><img loading="lazy" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/10/15406287441-1024x671.jpg" alt="" class="wp-image-2465" width="1142" height="749" srcset="https://clip-clap.ru/wp-content/uploads/2020/10/15406287441-1024x671.jpg 1024w, https://clip-clap.ru/wp-content/uploads/2020/10/15406287441-300x197.jpg 300w, https://clip-clap.ru/wp-content/uploads/2020/10/15406287441-768x503.jpg 768w, https://clip-clap.ru/wp-content/uploads/2020/10/15406287441.jpg 1500w" sizes="(max-width: 1142px) 100vw, 1142px" /></figure>


</br>



<p>PHP один из тех языков, с которых можно начинать путь программиста. Несмотря на лёгкость освоения, язык очень функционален, он готов выполнять даже сложные задачи. Даже если вы не знаете ничего о PHP и программировании в целом, особого труда с освоением возникнуть не должно. Нет сомнений, что уже спустя пару часов изучения PHP вы научитесь создавать первые скрипты.</p>



<p>PHP относится к тем языкам, что регулярно улучшаются и развиваются. Он точно будет занимать лидирующие места в сфере веба ещё достаточно долго. Ближайшие 10-15 лет программисты на PHP будут востребованы.</p>



<h3 class="wp-block-heading">Что значит «серверный язык»?</h3>



<p>Язык PHP ценят за мощность и гибкость. Он выступает связующим звеном между клиентом и базой данных, а также выполняет базовую обработку всех входных параметров от пользователя. Всё это стало возможным благодаря работе скриптов на стороне сервера.</p>



<p>Практическая сторона вопроса:</p>



<ul><li>Удобно создает страницы или их фрагменты, которые одинаково отображаются у каждого пользователя;</li><li>PHP все равно какая производительность компьютера у пользователя или какая операционная система стоит. Вся обработка данных происходит на сервере;</li><li>Для получения новых данных на странице обязательно потребуется её обновление (если не прибегать к JS). До обновления страницы стандартными средствами PHP нельзя что-либо менять на ней. Иначе говоря, язык не интерактивный. С помощью Ajax, уже реально обновлять данные веб-страницы без её перезагрузки.</li></ul>



<h3 class="wp-block-heading">Советы начинающим программистам</h3>



<p>Как вы уже поняли, PHP применяется для разработки сайтов. В создании веба участвуют и другие языки, ключевые из них HTML и CSS. Исключительно с помощью PHP не создать сайт. HTML – это структура страницы, а CSS – её стили (цвета, расположение, эффекты и т. п.).</p>



<figure class="wp-block-image size-large is-resized"><img loading="lazy" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/10/15406283071.jpg" alt="" class="wp-image-2466" width="1145" height="401" srcset="https://clip-clap.ru/wp-content/uploads/2020/10/15406283071.jpg 770w, https://clip-clap.ru/wp-content/uploads/2020/10/15406283071-300x105.jpg 300w, https://clip-clap.ru/wp-content/uploads/2020/10/15406283071-768x269.jpg 768w" sizes="(max-width: 1145px) 100vw, 1145px" /></figure>


</br>



<p>Важной частью работы с PHP является взаимодействие с базой данных (БД). В ней хранятся базовые данные о пользователе, статьи, заметки и различные другие данные. Добавление, изменение и извлечение данных с БД – это те задачи, с которыми постоянно сталкивается PHP-программист. Именно поэтому должное внимание стоит уделить изучению языка запросов SQL.</p>



<p>У нас на сайте уже есть курсы по изучению JavaScript, HTML и CSS, которые помогут преодолеть пропасть между начинающим и профессиональным программистом.</p>



<h3 class="wp-block-heading">Итог</h3>



<p>Сегодня PHP сохраняет актуальность, при чём такая тенденция будет сохранятся и далее. Хоть и существуют достойные альтернативы языку, но согласно<a rel="noreferrer noopener" target="_blank" href="https://w3techs.com/technologies/details/pl-php/all/all">&nbsp;статистике w3techs</a>,&nbsp;<code>83%</code>&nbsp;сайтов применяют именно PHP в качестве основного серверного языка. Это значит, что разработчику на PHP всегда найдётся работа с конкурентной оплатой.</p>



<h3 class="wp-block-heading">Дополнительные курсы</h3>



<p>На нашем сайте есть дополнительные курсы по изучению PHP. Все курсы вы можете найти по <a href="https://itproger.com/tag/php">этой ссылке</a>.</p>


</br>
<p>Сообщение <a href="https://clip-clap.ru/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d0%b8%d0%b7%d1%83%d1%87%d0%b5%d0%bd%d0%b8%d0%b5-php-%d0%be%d1%81%d0%bd%d0%be%d0%b2%d1%8b-php/">Изучение PHP | Основы PHP</a> появились сначала на <a href="https://clip-clap.ru">Clip-Clap</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://clip-clap.ru/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d0%b8%d0%b7%d1%83%d1%87%d0%b5%d0%bd%d0%b8%d0%b5-php-%d0%be%d1%81%d0%bd%d0%be%d0%b2%d1%8b-php/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ЧЕМ ОТКРЫВАЮТСЯ ФАЙЛЫ РАСШИРЕНИЯ .ASPX</title>
		<link>https://clip-clap.ru/it/%d1%81%d0%be%d1%84%d1%82-%d0%b8-%d0%be%d1%81/%d1%87%d0%b5%d0%bc-%d0%be%d1%82%d0%ba%d1%80%d1%8b%d0%b2%d0%b0%d1%8e%d1%82%d1%81%d1%8f-%d1%84%d0%b0%d0%b9%d0%bb%d1%8b-%d1%80%d0%b0%d1%81%d1%88%d0%b8%d1%80%d0%b5%d0%bd%d0%b8%d1%8f-aspx/</link>
					<comments>https://clip-clap.ru/it/%d1%81%d0%be%d1%84%d1%82-%d0%b8-%d0%be%d1%81/%d1%87%d0%b5%d0%bc-%d0%be%d1%82%d0%ba%d1%80%d1%8b%d0%b2%d0%b0%d1%8e%d1%82%d1%81%d1%8f-%d1%84%d0%b0%d0%b9%d0%bb%d1%8b-%d1%80%d0%b0%d1%81%d1%88%d0%b8%d1%80%d0%b5%d0%bd%d0%b8%d1%8f-aspx/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Mon, 12 Oct 2020 16:56:50 +0000</pubDate>
				<category><![CDATA[Программирование]]></category>
		<category><![CDATA[Софт и ОС]]></category>
		<category><![CDATA[.ASPX]]></category>
		<guid isPermaLink="false">https://clip-clap.ru/?p=2354</guid>

					<description><![CDATA[<p>Файл с расширением ASPX – это расширенный файл страницы Active Server, разработанный для платформы Microsoft ASP.NET. Файлы ASPX генерируются веб-сервером</p>
<p>Сообщение <a href="https://clip-clap.ru/it/%d1%81%d0%be%d1%84%d1%82-%d0%b8-%d0%be%d1%81/%d1%87%d0%b5%d0%bc-%d0%be%d1%82%d0%ba%d1%80%d1%8b%d0%b2%d0%b0%d1%8e%d1%82%d1%81%d1%8f-%d1%84%d0%b0%d0%b9%d0%bb%d1%8b-%d1%80%d0%b0%d1%81%d1%88%d0%b8%d1%80%d0%b5%d0%bd%d0%b8%d1%8f-aspx/">ЧЕМ ОТКРЫВАЮТСЯ ФАЙЛЫ РАСШИРЕНИЯ .ASPX</a> появились сначала на <a href="https://clip-clap.ru">Clip-Clap</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Файл с расширением ASPX – это расширенный файл страницы Active Server, разработанный для платформы Microsoft ASP.NET. Файлы ASPX генерируются веб-сервером и содержат сценарии и исходные коды, которые помогают сообщать браузеру, как следует открывать и отображать веб-страницу. Чем открыть aspx-файл? Есть несколько проверенных способов.</p>



<figure class="wp-block-image size-large is-resized"><img loading="lazy" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/10/kak-chem-otkryt-fajl-s-rasshireniem-aspx1.jpg" alt="" class="wp-image-2356" width="1136" height="659" srcset="https://clip-clap.ru/wp-content/uploads/2020/10/kak-chem-otkryt-fajl-s-rasshireniem-aspx1.jpg 1000w, https://clip-clap.ru/wp-content/uploads/2020/10/kak-chem-otkryt-fajl-s-rasshireniem-aspx1-300x174.jpg 300w, https://clip-clap.ru/wp-content/uploads/2020/10/kak-chem-otkryt-fajl-s-rasshireniem-aspx1-768x445.jpg 768w" sizes="(max-width: 1136px) 100vw, 1136px" /></figure>


</br>



<h2 class="wp-block-heading">Что обозначает расширение aspx</h2>



<p>Чаще всего вы увидите расширение .ASPX в URL-адресе или, когда ваш веб-браузер случайно отправит вам ASPX, а не тот, который, как вы считали, вы загружаете. Если вы загрузили ASPX и ожидали, что он будет содержать информацию (например, документ или другие сохраненные данные), скорее всего, что-то не так с веб-сайтом, и вместо того, чтобы генерировать полезную информацию, он предоставил этот документ с сервера. В этом случае первый метод открытия состоит в том, чтобы просто переименовать ASPX-файл в такой формат, какой вам нужен. Например, если вы качали версию счета в формате PDF из своего личного кабинета на сайте банка в интернете, но вместо этого получили ASPX, просто переименуйте его в bill.pdf, а затем откройте. Если вам нужно изображения, попробуйте переименовать ASPX в image.jpg. Аналогично и для других форматов.ВАЖНО. Чтобы переименовать файл, ваш компьютер должен быть настроен для отображения расширения. Для этого откройте диалоговое окно «Выполнить» (в меню «Пуск» или сочетанием клавиш Win+R) и введите «control folders». В меню «Вид» найдите «Скрывать расширения для зарегистрированных типов файлов» – снимите флажок и примените изменения. </p>



<p>Проблема здесь в том, что иногда сервер (на веб-сайте, из которого вы получаете ASPX-файл) неправильно описывает сгенерированный файл (PDF, изображение, музыкальный файл и т. д.) и представляет его для загрузки. Вышеописанный метод помогает в большинстве ситуаций. Вы не всегда можете изменить расширение на какое-либо другое и ожидать, что в новом формате он откроется правильно. Особенно часто это бывает с файлом PDF и расширением ASPX, поэтому здесь такой способ будет неэффективен. Иногда причиной этой проблемы является связанный с браузером подключаемый модуль, поэтому может помочь загрузка страницы, которая генерирует ASPX, в другом браузере, отличном от того, что вы используете сейчас. Например, если вы используете Internet Explorer, попробуйте перейти на Chrome или Firefox.</p>



<figure class="wp-block-image size-large is-resized"><img loading="lazy" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/10/fajly-s-rasshireniem-aspx1.jpg" alt="" class="wp-image-2357" width="1137" height="758" srcset="https://clip-clap.ru/wp-content/uploads/2020/10/fajly-s-rasshireniem-aspx1.jpg 768w, https://clip-clap.ru/wp-content/uploads/2020/10/fajly-s-rasshireniem-aspx1-300x200.jpg 300w" sizes="(max-width: 1137px) 100vw, 1137px" /></figure>


</br>



<h2 class="wp-block-heading">Открываем формат</h2>



<p>URL-адрес с ASPX в конце означает, что веб-страница выполняется в рамках ASP.NET. Чтобы открыть этот тип документа, вам не нужно ничего делать, потому что браузер делает это за вас, будь то Chrome, Firefox, Internet Explorer и т. д. Фактический код в ASPX обрабатывается веб-сервером и может быть закодирован в любой программе, которая кодируется в ASP.NET. Как открыть файл aspx? Microsoft Visual Studio – это одна из бесплатных программ, которую вы можете использовать для открытия и редактирования ASPX. Еще одним инструментом, хотя и не бесплатным, является популярный Adobe Dreamweaver. Иногда содержимое ASPX можно редактировать простым текстовым редактором блокнот.</p>



<h3 class="wp-block-heading">Microsoft visual studio</h3>



<p>Visual Studio 2017 – это версия среды программирования Microsoft Visual Studio Professional (IDE). Программа адресована студентам, разработчикам программного обеспечения в духе открытого исходного кода. Visual Studio используется для создания настольных приложений c универсальным пользовательским интерфейсом, веб-приложений с использованием ASP.NET и Silverlight и облаком. Он также позволяет создавать программы для платформ Windows Phone, Android и iOS. Код может быть написан на одном из нескольких языков, включая: C #, Visual Basic, C ++, F # и JavaScript. Многие считают, что программа является средой программирования самого высокого уровня.</p>



<figure class="wp-block-image size-large is-resized"><img loading="lazy" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/10/Microsoft-visual-studio1.jpg" alt="" class="wp-image-2358" width="1135" height="676" srcset="https://clip-clap.ru/wp-content/uploads/2020/10/Microsoft-visual-studio1.jpg 1000w, https://clip-clap.ru/wp-content/uploads/2020/10/Microsoft-visual-studio1-300x179.jpg 300w, https://clip-clap.ru/wp-content/uploads/2020/10/Microsoft-visual-studio1-768x457.jpg 768w" sizes="(max-width: 1135px) 100vw, 1135px" /></figure>



<p>Что касается профессионального издания, то программа предполагает параллельное использование несколькими программистами при работе над одним проектом. Файл aspx открывается простым перетаскиванием по методу drag and drop или через системное диалоговое меню Файл – Открыть. После этого нужно найти папку, где содержится ваш файл с расширением aspx (обычно это папка загрузок вашего браузера).</p>


</br>



<h3 class="wp-block-heading">Adobe Dreamweaver</h3>



<p>Adobe Dreamweaver – это новая версия отличного инструмента для профессионалов. Программа используется для графического веб-дизайна. Приложение поддерживает JavaScript и динамический HTML (DHTML). Она также позволяет вам позиционировать элементы, применять перетаскивание при проектировании фреймов и таблиц. Dreamweaver отлично интегрируется с другими программами из семейства CS3. Адаптация ссылок, настройка документов DHTML для более старых версий браузеров и т. д. Чтобы открыть неизвестный файл aspx, используйте вышеописанный метод – с помощью функции drag and drop или через системный диалог через дерево папок.</p>



<figure class="wp-block-image size-large is-resized"><img loading="lazy" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/10/Adobe-Dreamweaver-aspx1.jpg" alt="" class="wp-image-2359" width="1141" height="761" srcset="https://clip-clap.ru/wp-content/uploads/2020/10/Adobe-Dreamweaver-aspx1.jpg 1000w, https://clip-clap.ru/wp-content/uploads/2020/10/Adobe-Dreamweaver-aspx1-300x200.jpg 300w, https://clip-clap.ru/wp-content/uploads/2020/10/Adobe-Dreamweaver-aspx1-768x512.jpg 768w" sizes="(max-width: 1141px) 100vw, 1141px" /></figure>


</br>



<h3 class="wp-block-heading">Microsoft Expression Web</h3>



<p>Expression Web — это программа, предназначенная для редактирования и верстки сайтов. Он является частью Expression Studio, содержит все компоненты, необходимые для производства высококачественных страниц. По определению приложение является конкурентом для Adobe Dreamwaver. Чтобы открыть файл .aspx, используйте меню File-Open? Или нажмите на символ открывающейся папки на верхней панели окна программы.</p>



<figure class="wp-block-image size-large is-resized"><img loading="lazy" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/10/Microsoft-Expression-Web1.jpg" alt="" class="wp-image-2360" width="1141" height="713" srcset="https://clip-clap.ru/wp-content/uploads/2020/10/Microsoft-Expression-Web1.jpg 1000w, https://clip-clap.ru/wp-content/uploads/2020/10/Microsoft-Expression-Web1-300x188.jpg 300w, https://clip-clap.ru/wp-content/uploads/2020/10/Microsoft-Expression-Web1-768x480.jpg 768w" sizes="(max-width: 1141px) 100vw, 1141px" /></figure>


</br>



<h3 class="wp-block-heading">Internet explorer</h3>



<p>Стандартный браузер Windows – Internet Explorer (в версии Windows 10 добавлен Microsoft Edge как браузер по умолчанию), позволит вам открыть aspx-файл без особого труда. Просто перетащите файл из папки, где он хранится, в окно браузера или используйте функцию Файл-Открыть.</p>



<figure class="wp-block-embed-youtube wp-block-embed is-type-video is-provider-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="Microsoft Edge: обзор нового браузера - Keddr.com" width="800" height="450" src="https://www.youtube.com/embed/dvcbZTCjJI0?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div></figure>


</br>



<h3 class="wp-block-heading">Блокнот</h3>



<p>Встроенный в Windows блокнот позволит вам просмотреть код файла aspx и при необходимости его исправить. Для открытия файла перетащите его левой кнопкой мыши в открытое окно редактора или воспользуйтесь меню Файл-Открыть, и найдите файл в проводнике. Файлы ASPX имеют определенное назначение. В отличие от изображений, таких как PNG, JPG, GIF и т. д., где преобразование сохраняет совместимость с большинством графических редакторов и софта для их просмотра, файлы ASPX перестанут делать то, для чего они предназначены, если вы конвертируете их в другие форматы.</p>



<p>Например, преобразование ASPX в HTML, безусловно, сделает результат HTML похожим на веб-страницу ASPX. Однако, поскольку элементы файлов ASPX обрабатываются на сервере, вы не сможете правильно их использовать, если в них должны отображаться HTML, PDF, JPG или любой другой файл, который сохраняется на компьютер. Visual Studio, например, может сохранять открытые ASPX-файлы в виде HTM, HTML, ASP, WSF, VBS, MASTER, ASMX, MSGX, SVC, SRF, JS и других форматах.</p>


</br>
<p>Сообщение <a href="https://clip-clap.ru/it/%d1%81%d0%be%d1%84%d1%82-%d0%b8-%d0%be%d1%81/%d1%87%d0%b5%d0%bc-%d0%be%d1%82%d0%ba%d1%80%d1%8b%d0%b2%d0%b0%d1%8e%d1%82%d1%81%d1%8f-%d1%84%d0%b0%d0%b9%d0%bb%d1%8b-%d1%80%d0%b0%d1%81%d1%88%d0%b8%d1%80%d0%b5%d0%bd%d0%b8%d1%8f-aspx/">ЧЕМ ОТКРЫВАЮТСЯ ФАЙЛЫ РАСШИРЕНИЯ .ASPX</a> появились сначала на <a href="https://clip-clap.ru">Clip-Clap</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://clip-clap.ru/it/%d1%81%d0%be%d1%84%d1%82-%d0%b8-%d0%be%d1%81/%d1%87%d0%b5%d0%bc-%d0%be%d1%82%d0%ba%d1%80%d1%8b%d0%b2%d0%b0%d1%8e%d1%82%d1%81%d1%8f-%d1%84%d0%b0%d0%b9%d0%bb%d1%8b-%d1%80%d0%b0%d1%81%d1%88%d0%b8%d1%80%d0%b5%d0%bd%d0%b8%d1%8f-aspx/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Настраиваем Twitter Bootstrap с SASS, используя bower и grunt</title>
		<link>https://clip-clap.ru/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%b0%d0%b8%d0%b2%d0%b0%d0%b5%d0%bc-twitter-bootstrap-%d1%81-sass-%d0%b8%d1%81%d0%bf%d0%be%d0%bb%d1%8c%d0%b7%d1%83%d1%8f-bower-%d0%b8-grunt/</link>
					<comments>https://clip-clap.ru/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%b0%d0%b8%d0%b2%d0%b0%d0%b5%d0%bc-twitter-bootstrap-%d1%81-sass-%d0%b8%d1%81%d0%bf%d0%be%d0%bb%d1%8c%d0%b7%d1%83%d1%8f-bower-%d0%b8-grunt/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Sun, 04 Oct 2020 22:09:26 +0000</pubDate>
				<category><![CDATA[Программирование]]></category>
		<category><![CDATA[Bootstrap]]></category>
		<category><![CDATA[bower]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[grunt]]></category>
		<category><![CDATA[SASS]]></category>
		<guid isPermaLink="false">https://clip-clap.ru/?p=2307</guid>

					<description><![CDATA[<p>Допустим при создании нового проекта мне понадобится: Twitter Bootstrap с SASS Компиляция SASS в CSS (для этого будем использовать Grunt)</p>
<p>Сообщение <a href="https://clip-clap.ru/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%b0%d0%b8%d0%b2%d0%b0%d0%b5%d0%bc-twitter-bootstrap-%d1%81-sass-%d0%b8%d1%81%d0%bf%d0%be%d0%bb%d1%8c%d0%b7%d1%83%d1%8f-bower-%d0%b8-grunt/">Настраиваем Twitter Bootstrap с SASS, используя bower и grunt</a> появились сначала на <a href="https://clip-clap.ru">Clip-Clap</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Допустим при создании нового проекта мне понадобится:</p>



<ul><li>Twitter Bootstrap с SASS</li><li>Компиляция SASS в CSS (для этого будем использовать Grunt)</li></ul>



<p>CSS-файл будет скомпилирован на основе исходников .scss при помощи Grunt.</p>



<p>Этот пост предполагает, что вы имеете некоторое представление о Bower, Grunt и Twitter Bootstrap.</p>


</br>



<h2 class="wp-block-heading" id="glava0">Устанавливаем Twitter Bootstrap с SASS, используя Bower</h2>



<p>Установим <a href="https://github.com/twbs/bootstrap-sass" target="_blank" rel="noreferrer noopener nofollow">Twitter Bootstrap с SASS</a> , при этом также загрузится jQuery:</p>



<p><code>bower install bootstrap-sass-official</code></p>



<p>пс: статья о Bower: <a href="http://nano.sapegin.ru/all/bower" target="_blank" rel="noreferrer noopener nofollow">зачем фронтенду нужен менеджер пакетов</a></p>



<p>Чтобы управлять нашими зависимостями нам потребуется&nbsp;<strong>bower.json</strong>. bower.json &#8212; в этом файле хранится список всех зависимостей проекта и другие метаданные. Чтобы создать данный файл нужно выполнить следующую команду:</p>



<p><code>bower init</code></p>



<p>Вам будет задан ряд вопросов, на которые вы можете нажимать enter и ответьте Yes на currently installed components as dependencies and add commonly ignored files to ignore list .</p>



<pre class="wp-block-code"><code>  "dependencies": {
    "bootstrap-sass-official": "~3.3.4"
  }</code></pre>



<h2 class="wp-block-heading" id="glava1">Устанавливаем Grunt</h2>



<p>см. статью <a href="https://clip-clap.ru/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d0%b2%d0%b2%d0%b5%d0%b4%d0%b5%d0%bd%d0%b8%d0%b5-%d0%b2-grunt-%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%be%d0%b9%d0%ba%d0%b0-%d0%bf%d1%80%d0%be%d0%b5%d0%ba%d1%82%d0%b0-%d0%b8-%d0%b7%d0%b0%d0%bf%d1%83%d1%81/" target="_blank" rel="noreferrer noopener">Введение в Grunt, настройка проекта и запуск задач</a></p>



<h2 class="wp-block-heading" id="glava2">Создание папки с нашими «активами» (assets)</h2>



<p>Давайте создадим папку assets и расположим в ней подпапки для CSS и SASS.</p>



<p>Идем дальше, создайте файл style.scss в папке sass и style.css в папке css.</p>



<p>В папке с нашим style.scss импортируем папку Twitter bootstrap:</p>



<pre class="wp-block-code"><code>@import 'app/bower_components/bootstrap-sass-official/assets/stylesheets/bootstrap';</code></pre>



<p>p.s. Обратите внимание как работает @import в sass: <a href="https://sass-scss.ru/documentation/pravila_i_direktivi/direktiva_import.html" target="_blank" rel="noreferrer noopener nofollow">Импортирование, оператор @import в SASS</a></p>



<p>Далее нам потребуется создать Gruntfile, посредством которого мы сможет следить (watch) и компилировать наш scss файл.</p>


</br>



<h2 class="wp-block-heading" id="glava3">Создание Gruntfile</h2>



<p>см. статью <a href="https://clip-clap.ru/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d0%b2%d0%b2%d0%b5%d0%b4%d0%b5%d0%bd%d0%b8%d0%b5-%d0%b2-grunt-%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%be%d0%b9%d0%ba%d0%b0-%d0%bf%d1%80%d0%be%d0%b5%d0%ba%d1%82%d0%b0-%d0%b8-%d0%b7%d0%b0%d0%bf%d1%83%d1%81/" target="_blank" rel="noreferrer noopener">Введение в Grunt, настройка проекта и запуск задач</a></p>



<p>Для того чтобы следить и компилировать наши Sass-файлы нам потребуется два плагина:</p>



<ul><li>grunt-contrib-sass</li><li>grunt-contrib-watch</li></ul>



<p>Вернитесь в терминал и введите следующий текст в папке проекта:</p>



<p><code>npm install grunt-contrib-sass --save-dev<br>npm install grunt-contrib-watch --save-dev</code></p>



<p>Плагины будут установлены в папке node_modules.</p>



<p>Давайте загрузим из в наш Gruntfile, подключив их после закрывающей скобки&nbsp;<code>grunt.initConfig</code>:</p>



<pre class="wp-block-code"><code>grunt.loadNpmTasks('grunt-contrib-sass');
grunt.loadNpmTasks('grunt-contrib-watch');</code></pre>



<h2 class="wp-block-heading" id="glava4">Настройка плагинов SASS и Watch в нашем Gruntfile</h2>



<p>В следующем шаге мы настроим SASS для нашего проекта.</p>



<p>Внутри grunt.initConfig включите следующий код.</p>



<pre class="wp-block-code"><code>sass: {
    dev: {
        options: {
            style: 'expanded',
            compass: false
        },
        files: {
            './assets/css/style.css':'./assets/sass/style.scss'
        }
    }
}</code></pre>



<p>Включите следующий код, который позволит Grunt наблюдать за нашими файлами и автоматически обновлять их при запущенном Grunt:</p>



<pre class="wp-block-code"><code>watch: {
    options: { livereload: true, },
    sass: {
        files: &#091;'./assets/sass/{,*/}*.scss',
                './bower_components/bootstrap-sass-official/assets/stylesheets/bootstrap/*.scss'],
        tasks: &#091;'sass:dev'],
    },
}
</code></pre>



<p>Наконец, давайте автоматизируем наши задачи. Таким образом мы консолидируем все задачи и запустим их, прописав в терминале grunt.</p>



<p>Для этого нам потребуется следующая строка кода:</p>



<h4 class="wp-block-heading">jQuery or Javascript</h4>



<pre class="wp-block-code"><code>grunt.registerTask("default", &#091;'watch']);</code></pre>


</br>



<h2 class="wp-block-heading" id="glava5">Результирующий Gruntfile</h2>



<p>Вот так, приблизительно, выглядит Gruntfile:</p>



<pre class="wp-block-code"><code>module.exports = function(grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        sass: {
            dev: {
                options: {
                    style: 'expanded',
                    compass: false
                },
                files: {
                    './assets/css/style.css':'./assets/sass/style.scss'
                }
            }
        },
        watch: {
            options: { livereload: true, },
            sass: {
                files: &#091;'./assets/sass/{,*/}*.scss',
                        './bower_components/bootstrap-sass-official/assets/stylesheets/bootstrap/*.scss'],
                tasks: &#091;'sass:dev'],
                options: {
                    spawn: false,
                }
            },
            html: {
                files: &#091;'./index.html']
            }
        }
    });
    grunt.loadNpmTasks('grunt-contrib-sass');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.registerTask("default", &#091;'sass','watch']);
};</code></pre>
<p>Сообщение <a href="https://clip-clap.ru/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%b0%d0%b8%d0%b2%d0%b0%d0%b5%d0%bc-twitter-bootstrap-%d1%81-sass-%d0%b8%d1%81%d0%bf%d0%be%d0%bb%d1%8c%d0%b7%d1%83%d1%8f-bower-%d0%b8-grunt/">Настраиваем Twitter Bootstrap с SASS, используя bower и grunt</a> появились сначала на <a href="https://clip-clap.ru">Clip-Clap</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://clip-clap.ru/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%b0%d0%b8%d0%b2%d0%b0%d0%b5%d0%bc-twitter-bootstrap-%d1%81-sass-%d0%b8%d1%81%d0%bf%d0%be%d0%bb%d1%8c%d0%b7%d1%83%d1%8f-bower-%d0%b8-grunt/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Введение в Grunt, настройка проекта и запуск задач</title>
		<link>https://clip-clap.ru/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d0%b2%d0%b2%d0%b5%d0%b4%d0%b5%d0%bd%d0%b8%d0%b5-%d0%b2-grunt-%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%be%d0%b9%d0%ba%d0%b0-%d0%bf%d1%80%d0%be%d0%b5%d0%ba%d1%82%d0%b0-%d0%b8-%d0%b7%d0%b0%d0%bf%d1%83%d1%81/</link>
					<comments>https://clip-clap.ru/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d0%b2%d0%b2%d0%b5%d0%b4%d0%b5%d0%bd%d0%b8%d0%b5-%d0%b2-grunt-%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%be%d0%b9%d0%ba%d0%b0-%d0%bf%d1%80%d0%be%d0%b5%d0%ba%d1%82%d0%b0-%d0%b8-%d0%b7%d0%b0%d0%bf%d1%83%d1%81/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Sun, 04 Oct 2020 21:37:14 +0000</pubDate>
				<category><![CDATA[Программирование]]></category>
		<guid isPermaLink="false">https://clip-clap.ru/?p=2308</guid>

					<description><![CDATA[<p>GRUNT &#8212; это система автоматизации (например, минимизация и конкатенация файлов, проверка ошибок, удаление логов, отслеживание изменений проекта, компиляция SASS, LESS</p>
<p>Сообщение <a href="https://clip-clap.ru/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d0%b2%d0%b2%d0%b5%d0%b4%d0%b5%d0%bd%d0%b8%d0%b5-%d0%b2-grunt-%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%be%d0%b9%d0%ba%d0%b0-%d0%bf%d1%80%d0%be%d0%b5%d0%ba%d1%82%d0%b0-%d0%b8-%d0%b7%d0%b0%d0%bf%d1%83%d1%81/">Введение в Grunt, настройка проекта и запуск задач</a> появились сначала на <a href="https://clip-clap.ru">Clip-Clap</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>GRUNT &#8212; это система автоматизации (например, минимизация и конкатенация файлов, проверка ошибок, удаление логов, отслеживание изменений проекта, компиляция SASS, LESS и т.д.).</p>



<p>Официальная страница <a href="http://gruntjs.com/" target="_blank" rel="noreferrer noopener nofollow">gruntjs.com</a></p>



<h2 class="wp-block-heading" id="glava0">Зачем использовать Grunt?</h2>



<p>Экосистема Grunt огромна и растет с каждым днем. Сотня плагинов на выбор; вы можете использовать Grunt, чтобы автоматизировать практически все что угодно и с минимальными усилиями. При желании Вы можете написать и опубликовать свой плагин для Grunt. <a href="http://gruntjs.com/getting-started" target="_blank" rel="noreferrer noopener nofollow">Быстрый старт</a> (http://gruntjs.com/getting-started).</p>


</br>



<h2 class="wp-block-heading" id="glava1">Зачем использовать task runner (таск-менеджер)?</h2>



<p>Одно слово – автоматизация. Ваша работа уменьшится при выполнении таких задач как минификация, компиляция, тестирование и т.д. Вам будет легче делать свою работу. После того как вы настроите его через <a href="http://gruntjs.com/sample-gruntfile" target="_blank" rel="noreferrer noopener nofollow">Gruntfile</a> (http://gruntjs.com/sample-gruntfile) все вышеописанные задачи будут сделаны без каких-либо усилий с вашей стороны.</p>



<h2 class="wp-block-heading" id="glava2">Доступные плагины GRUNT</h2>



<p>Доступные плагины GRUNT, на которые стоит обратить внимание:</p>



<ul><li>Плагин grunt-contrib-jshint(<a href="https://github.com/gruntjs/grunt-contrib-jshint" target="_blank" rel="noreferrer noopener nofollow">jshint</a>) &#8212; проверяет js-код.</li><li>Плагин grunt-contrib-concat(<a href="https://github.com/gruntjs/grunt-contrib-concat" target="_blank" rel="noreferrer noopener nofollow">concat</a>) &#8212; это плагин, который соединяет файлы одного типа (css,js) в один.</li><li>Плагин grunt-contrib-uglify (<a href="https://github.com/gruntjs/grunt-contrib-uglify" target="_blank" rel="noreferrer noopener nofollow">uglify</a>) &#8212; минификатор, используется для минимизации js-файлов.</li><li>Плагин grunt-contrib-cssmin (<a href="https://github.com/gruntjs/grunt-contrib-cssmin" target="_blank" rel="noreferrer noopener nofollow">cssmin</a>) &#8212; минификатор CSS.</li><li>Плагин grunt-contrib-sass (<a href="https://github.com/gruntjs/grunt-contrib-sass" target="_blank" rel="noreferrer noopener nofollow">sass</a>) &#8212; преобразуем SASS в CSS.</li><li>Плагин grunt-contrib-watch (<a href="https://github.com/gruntjs/grunt-contrib-watch" target="_blank" rel="noreferrer noopener nofollow">watch</a>) &#8212; отслеживание изменений в файлах.</li><li>Плагин grunt-contrib-imagemin (<a href="https://github.com/gruntjs/grunt-contrib-imagemin" target="_blank" rel="noreferrer noopener nofollow">imagemin</a>) &#8212; оптимизация изображений.</li><li>Плагин grunt-autoprefixer (<a href="https://github.com/nDmitry/grunt-autoprefixer" target="_blank" rel="noreferrer noopener nofollow">autoprefixer</a>) &#8212; парсит CSS и добавляет вендорные префиксы, используя данные с Can I Use.</li><li><a href="https://github.com/blai/grunt-express" target="_blank" rel="noreferrer noopener nofollow">grunt-expres</a>s используем для Livereload</li></ul>



<p>Grunt и его плагины устанавливаются и управляются посредством npm (менеджера пакетов Node.js). Для справки (освежить в памяти): Gruntfile пример &#8212; <a href="http://gruntjs.com/sample-gruntfile" target="_blank" rel="noreferrer noopener nofollow">sample-gruntfile</a>, настраиваем задачи &#8212; <a href="http://gruntjs.com/configuring-tasks" target="_blank" rel="noreferrer noopener nofollow">configuring-tasks</a>.</p>



<p>Устанавливаем grunt:<br><code>npm install -g grunt-cli</code></p>



<p>Это позволит находить команду grunt по системному пути и запускать из любого каталога.</p>



<h2 class="wp-block-heading" id="glava3">Работаем с существующим проектом</h2>



<ul><li>1. Перейдите в корневой каталог проекта</li><li>2. Установите зависимости проекта с помощью&nbsp;<code>npm install</code></li><li>3. Запустить Grunt с&nbsp;<code>grunt</code></li></ul>



<p>Использование, опции, возможные задачи для Grunt можно узнать через команду<br><code>grunt –help</code>.</p>


</br>



<h2 class="wp-block-heading" id="glava4">Подготовка проекта</h2>



<p><strong>package.json</strong>: этот файл используется npm, чтобы сохранить метаданные проекта при публикации проекта как npm модуля.&nbsp;<code>devDependencies</code>&nbsp;(зависимости) перечисляются также в этом файле.</p>



<p><strong>Gruntfile</strong>: Этот файл называется Gruntfile.js и используется, чтобы настроить или определить задачи и загрузить Grunt плагины. Когда в документации упоминается Gruntfile, речь идет о Gruntfile.js или Gruntfile.coffee.</p>



<h3 class="wp-block-heading">package.json</h3>



<p>Создать package.json можно:</p>



<ul><li>1. Командой <code>npm init</code></li><li>2. Начните с примером ниже и расширьте его, следуя <a href="https://docs.npmjs.com/files/package.json" target="_blank" rel="noreferrer noopener nofollow">спецификации</a></li></ul>



<pre class="wp-block-code"><code>{
  "name": "my-project-name",
  "version": "0.1.0",
  "devDependencies": {
    "grunt": "~0.4.5",
    "grunt-contrib-jshint": "~0.10.0",
    "grunt-contrib-nodeunit": "~0.4.1",
    "grunt-contrib-uglify": "~0.5.0"
  }
}</code></pre>



<p>В файле&nbsp;<strong>package.json</strong>&nbsp;имеется свойство&nbsp;<code>scripts</code>, в котором можно определить скрипты. Эти скрипты помогут автоматизировать некоторые процессы. Названием свойства в объекте&nbsp;<code>scripts</code>&nbsp;будет название скрипта, в качестве значение укажем команду, которую мы вводили в терминале. В нашем случае транспилируем код помощи babel:&nbsp;<code>"babel es2015.js -o es5.js"</code>. Далее в терминале достаточно будет ввести команду:<br><code>npm run build</code><br>для выполнение команды&nbsp;<code>babel es2015.js -o es5.js</code>.</p>



<pre class="wp-block-code"><code>"scripts": {
    "build": "babel es2015.js -o es5.js"
}
</code></pre>


</br>



<h3 class="wp-block-heading">Установка Grunt и gruntplugins (плагинов/зависимостей)</h3>



<p>Самый простой способ добавить Grunt и gruntplugins к существующему package.json:<br><code>npm install &lt;module&gt; --save-dev</code>.</p>



<p>Например, это позволит установить последнюю версию Grunt в ваш проект с добавлением его в devDependencies:<br><code>npm install grunt --save-dev</code></p>



<p>Устанавливаем модуль JSHint:<br><code>npm install grunt-contrib-jshint --save-dev</code></p>



<p>Установить все зависимости, уже перечисленные в файле package.json, можно командой<br><code>npm install</code>.</p>



<h3 class="wp-block-heading">Gruntfile</h3>



<p>Gruntfile состоит из следующих частей:</p>



<ul><li>Функция-обертка</li><li>Проект и настройка задач</li><li>Загрузка плагинов и задач</li><li>Пользовательские задачи</li></ul>



<h4 class="wp-block-heading">Пример Gruntfile</h4>



<pre class="wp-block-code"><code>module.exports = function(grunt) {

  // Настройки проекта
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    uglify: {
      options: {
        banner: '/*! &lt;%= pkg.name %> &lt;%= grunt.template.today("yyyy-mm-dd") %> */\n'
      },
      build: {
        src: 'src/&lt;%= pkg.name %>.js',
        dest: 'build/&lt;%= pkg.name %>.min.js'
      }
    }
  });

  // Загрузка плагина, который обеспечивает задачу "uglify"
  grunt.loadNpmTasks('grunt-contrib-uglify');

  // Задача(и) по умолчанию
  grunt.registerTask('default', &#091;'uglify']);

};</code></pre>


</br>



<h4 class="wp-block-heading">Функция-обертка</h4>



<p>Каждый Gruntfile использует этот базовый формат; весь ваш код должен быть определен внутри этой функции:</p>



<pre class="wp-block-code"><code>module.exports = function(grunt) {
  // Do grunt-related things in here
};</code></pre>



<h4 class="wp-block-heading">Проект и настройка задачи</h4>



<p>Большинство Grunt задач полагаются на данные, которые передаются как объект методу <code>grunt.initConfig</code> (<a href="http://gruntjs.com/api/grunt#grunt.initconfig" target="_blank" rel="noreferrer noopener nofollow">grunt.initconfig</a>). Пример настройки задачи <code>uglify</code>, настройки этой задачи задаются в одноименном свойстве.</p>



<pre class="wp-block-code"><code>// Настройки проекта
grunt.initConfig({
  pkg: grunt.file.readJSON('package.json'),
  uglify: {
    options: {
      banner: '/*! &lt;%= pkg.name %> &lt;%= grunt.template.today("yyyy-mm-dd") %> */\n'
    },
    build: {
      src: 'src/&lt;%= pkg.name %>.js',
      dest: 'build/&lt;%= pkg.name %>.min.js'
    }
  }
});</code></pre>



<p>Обратите внимание на использование данных из JSON-файла.</p>



<h4 class="wp-block-heading">Загрузка плагинов и задач</h4>



<p>Большинство задач (минификация, компиляция, тестирование, анализ кода) доступны как плагины grunt. После того как плагин указан в package.json как зависимость и установлен через&nbsp;<code>npm install</code>, его нужно подключить внутри вашего Gruntfile простой командой:</p>



<pre class="wp-block-code"><code>// Загрузка плагина, который обеспечивает задачу "uglify"
grunt.loadNpmTasks('grunt-contrib-uglify');</code></pre>



<h4 class="wp-block-heading">Пользовательские задачи</h4>



<p>Вы можете настроить Grunt, чтобы запускать одну или несколько задач по умолчанию, определив задачу&nbsp;<code>default</code>. В следующем примере команда&nbsp;<code>grunt</code>&nbsp;запустит задачу&nbsp;<code>uglify</code>&nbsp;(что также равнозначно командам&nbsp;<code>grunt uglify</code>&nbsp;и&nbsp;<code>grunt default</code>). Любое количество задач можно указать в массиве:</p>



<pre class="wp-block-code"><code>// // Задача(и) по умолчанию
grunt.registerTask('default', &#091;'uglify']);</code></pre>



<p>Если вашему проекту требуются задачи не предусмотренные плагином Grunt, вы можете определить свою задачу прямо внутри Gruntfile.</p>


</br>
<p>Сообщение <a href="https://clip-clap.ru/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d0%b2%d0%b2%d0%b5%d0%b4%d0%b5%d0%bd%d0%b8%d0%b5-%d0%b2-grunt-%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%be%d0%b9%d0%ba%d0%b0-%d0%bf%d1%80%d0%be%d0%b5%d0%ba%d1%82%d0%b0-%d0%b8-%d0%b7%d0%b0%d0%bf%d1%83%d1%81/">Введение в Grunt, настройка проекта и запуск задач</a> появились сначала на <a href="https://clip-clap.ru">Clip-Clap</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://clip-clap.ru/it/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d0%b2%d0%b2%d0%b5%d0%b4%d0%b5%d0%bd%d0%b8%d0%b5-%d0%b2-grunt-%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%be%d0%b9%d0%ba%d0%b0-%d0%bf%d1%80%d0%be%d0%b5%d0%ba%d1%82%d0%b0-%d0%b8-%d0%b7%d0%b0%d0%bf%d1%83%d1%81/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Создание отзывчивых писем без медиа-запросов</title>
		<link>https://clip-clap.ru/it/html/%d1%81%d0%be%d0%b7%d0%b4%d0%b0%d0%bd%d0%b8%d0%b5-%d0%be%d1%82%d0%b7%d1%8b%d0%b2%d1%87%d0%b8%d0%b2%d1%8b%d1%85-%d0%bf%d0%b8%d1%81%d0%b5%d0%bc-%d0%b1%d0%b5%d0%b7-%d0%bc%d0%b5%d0%b4%d0%b8%d0%b0-%d0%b7/</link>
					<comments>https://clip-clap.ru/it/html/%d1%81%d0%be%d0%b7%d0%b4%d0%b0%d0%bd%d0%b8%d0%b5-%d0%be%d1%82%d0%b7%d1%8b%d0%b2%d1%87%d0%b8%d0%b2%d1%8b%d1%85-%d0%bf%d0%b8%d1%81%d0%b5%d0%bc-%d0%b1%d0%b5%d0%b7-%d0%bc%d0%b5%d0%b4%d0%b8%d0%b0-%d0%b7/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Sun, 04 Oct 2020 21:16:01 +0000</pubDate>
				<category><![CDATA[HTML]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[скрипт]]></category>
		<guid isPermaLink="false">https://clip-clap.ru/?p=2290</guid>

					<description><![CDATA[<p>Существует достаточно много проблем, когда кодируешь html для писем. Едва ли справедливо, что нам нужно ориентироваться и на новые почтовые</p>
<p>Сообщение <a href="https://clip-clap.ru/it/html/%d1%81%d0%be%d0%b7%d0%b4%d0%b0%d0%bd%d0%b8%d0%b5-%d0%be%d1%82%d0%b7%d1%8b%d0%b2%d1%87%d0%b8%d0%b2%d1%8b%d1%85-%d0%bf%d0%b8%d1%81%d0%b5%d0%bc-%d0%b1%d0%b5%d0%b7-%d0%bc%d0%b5%d0%b4%d0%b8%d0%b0-%d0%b7/">Создание отзывчивых писем без медиа-запросов</a> появились сначала на <a href="https://clip-clap.ru">Clip-Clap</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Существует достаточно много проблем, когда кодируешь html для писем. Едва ли справедливо, что нам нужно ориентироваться и на новые почтовые клиенты и размеры устройств. Поддержка CSS и медиа-запросов может варьироваться от приложения к приложению, поэтому невозможно избежать страха всякий раз, когда вы слышите, что есть новое и захватывающее почтовое приложение, которое вы должны проверить.</p>



<p><strong>Примечание переводчика</strong>: Представленный ниже материал содержит значительное количество технических терминов, при переводе которых могут возникнуть неточности. Если вы заметили опечатку, ошибку или неточность перевода — напишите нам, и мы оперативно всё исправим.</p>



<div class="wp-block-image"><figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="600" height="839" src="https://clip-clap.ru/wp-content/uploads/2020/10/example600px1.jpg" alt="" class="wp-image-2291" srcset="https://clip-clap.ru/wp-content/uploads/2020/10/example600px1.jpg 600w, https://clip-clap.ru/wp-content/uploads/2020/10/example600px1-215x300.jpg 215w" sizes="(max-width: 600px) 100vw, 600px" /></figure></div>


</br>



<p>Но что, если бы вы могли построить шаблон, который был бы отзывчивым, даже в средах с бедной поддержкой современного CSS? Что если каждый раз, когда вы слышите о новом email приложении, которое все используют, вместо вздрагивания от страха вы могли бы чувствовать себя в безопасности, зная, что ваши письма выглядят достойно?</p>



<p>Метод, который я поддерживаю ниже, пригодится в качестве хорошего опыта для email-клиентов, которые не поддерживают медиа-запросы вообще.</p>



<p>Его называют резиновый гибридный (fluid-hybrid) метод, иногда — губчатый (spongy) метод почтовой разработки. Резиновая часть ссылается на то, что мы используем проценты. Гибридная часть от того, что мы также используем max-width, чтобы ограничить некоторые из наших элементов на больших экранах.</p>



<h2 class="wp-block-heading" id="glava0">Шесть основных проблем, которые мы должны решить</h2>



<h3 class="wp-block-heading">1. Приложение gmail для android и IOS &#8212; боль</h3>



<p>Это более популярное, чем дефолтное mail-приложение на Android, но Gmail не поддерживает медиа-запросы, на которые мы традиционно полагаемся, чтобы изменить размер и переформатировать наши письма для небольших экранов. Это статья покажет вам, как сделать письма отзывчивыми, которые отзывчивы даже в Gmail приложении.</p>



<h3 class="wp-block-heading">2. Новые почтовые приложения выпускаются постоянно</h3>



<p>Трудно проследить за всеми новыми приложениями, которые продолжают появляться. Некоторые из них действительно заботятся о email-рендеринге и имеют прекрасную поддержку CSS и медиа-запросов, но другие лишь сфокусированы на почтовых (email) рабочих процессах и не поддерживают медиа-запросы вообще. Эта статья покажет вам как сделать email, который всегда отзывчив, не обращая внимания на уровень CSS поддержки.</p>



<h3 class="wp-block-heading">3. Количество устройств с разным размером экрана практически безгранично</h3>



<p>Мы имеем не только большие десктопы и маленькие смартфоны, есть также большие смартфоны и небольшие ноутбуки. Просто потому что кто-то имеет доступ к Gmail через свой ноутбук совсем не означает, что его экран достаточно большой, чтобы просматривать письма шириной&nbsp;<code>700px</code>, и люди использующие iphone6+ могут справиться с двухколончатым макетом, но вместо этого обычно вырисовывается макет с одной колонкой. Эта статья покажет вам, как сделать макет, который будет подстраиваться под доступное пространство, даже в webmail.</p>



<h3 class="wp-block-heading">4. Создание отзывчивого письма посредством td на мобильных устройствах не работает повсеместно</h3>



<p>Некоторые почтовые клиенты (на IOS и даже некоторые нативные mail приложения на относительно старых версиях Android) не правильно группируют/складывают одну под другую (в одну колонку, см. ссылку) ячейки в одной и той же строке; будут сгруппированы (stack, <a href="https://www.emailonacid.com/blog/article/email-development/how_android_is_strangling_responsive_design/" target="_blank" rel="noreferrer noopener nofollow">https://www.emailonacid.com/blog/article/email-development/how_android_is_strangling_responsive_design/</a>) только две отдельные таблицы. В этой статье используется совершенно другой метод, который полностью поддерживается всеми приложениями и устройствами. Обычно решением это проблемы является использование таблиц с атрибутами <code>align=‛right‛</code> или <code>align=‛left‛</code>, но это наводит нас на еще одну ловушку.</p>



<h3 class="wp-block-heading">5. Когда используется метод выравнивания (<code>align</code>) для отзывчивой разработки, ваши таблицы группируются с выравниванием по левой или правой стороне в мобильных приложениях, которые не поддерживают медиа-запросы.</h3>



<p>В этой статье используется другой подход, который предусматривает, что ваши колонки выравниваются по центру на мобильных устройствах и даже в Gmail приложениях. (Вы также можете легко выравнивать их по левой или правой стороне в зависимости от того, что вы предпочитаете).</p>



<div class="wp-block-image"><figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="586" height="388" src="https://clip-clap.ru/wp-content/uploads/2020/10/0011.jpg" alt="" class="wp-image-2292" srcset="https://clip-clap.ru/wp-content/uploads/2020/10/0011.jpg 586w, https://clip-clap.ru/wp-content/uploads/2020/10/0011-300x199.jpg 300w" sizes="(max-width: 586px) 100vw, 586px" /></figure></div>



<p>Примечания: таблицы выравненные влево или вправо остаются на тех же позициях в мобильных устройствах , которые не поддерживают медиа-запросы.</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/10/0021.svg" alt="" class="wp-image-2293" width="1122" height="749"/></figure></div>



<p>Эта статья покажет вам, как иметь стек колонок выравненный по центру, даже в приложениях, которые не поддерживают медиа-запросы.</p>



<h3 class="wp-block-heading">6. Когда вы используете выравнивание таблицы для отзывчивой разработки, вы теряете способность вертикально выравнивать контент в соседних колонках. (как связано?)</h3>


</br>



<p>В этой статье будет также показано, как выровнять по вертикали две колонки в одной и той же строке по верху или по середине, как если бы они были в ячейках таблицы с атрибутом valign в одной и той же строке.</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/10/0031.jpg" alt="" class="wp-image-2294" width="1133" height="1092" srcset="https://clip-clap.ru/wp-content/uploads/2020/10/0031.jpg 590w, https://clip-clap.ru/wp-content/uploads/2020/10/0031-300x289.jpg 300w" sizes="(max-width: 1133px) 100vw, 1133px" /></figure></div>



<p>В этой статье используется &#171;резиновый гибридный&#187; метод, который позволяет вам выравнить ваши колонки по верху, середине или низу</p>



<h2 class="wp-block-heading" id="glava1">1. Давайте приступим</h2>



<p>Откройте пустой файл и сохраните его как index.html, затем скопируйте туда следующий код:</p>



<h4 class="wp-block-heading">HTML</h4>



<pre class="wp-block-code"><code>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
&lt;html xmlns="http://www.w3.org/1999/xhtml">
&lt;head>
&lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    &lt;!--&#091;if !mso]>&lt;!-->
        &lt;meta http-equiv="X-UA-Compatible" content="IE=edge" />
    &lt;!--&lt;!&#091;endif]-->
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0">
    &lt;title>&lt;/title>
    &lt;link rel="stylesheet" type="text/css" href="styles.css" />
    &lt;!--&#091;if (gte mso 9)|(IE)]>
    &lt;style type="text/css">
        table {border-collapse: collapse;}
    &lt;/style>
    &lt;!&#091;endif]-->
&lt;/head>
&lt;body>
    &lt;center class="wrapper">
        &lt;div class="webkit">
            &#091;content goes here]
        &lt;/div>
    &lt;/center>
&lt;/body></code></pre>



<p>Давайте быстро пробежимся через все элементы в приведенном выше коде:</p>



<p><code>!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"</code><br>этот DOCTYPE на мой взгляд лучший &#8212; с ним меньше всего проблем.</p>



<p><code>&lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8" /&gt;</code><br>Дает нам поддержку для всех Unicode символов в нашем документе.</p>



<p><code>&lt;meta http-equiv="X-UA-Compatible" content="IE=edge" /&gt;</code><br>Используется затем, чтобы телефоны windows отображали нашу мобильную версию корректно. Код спрятан внутри условного комментария, который скрывает его от каких-либо других ‘mso’ (Microsoft Outlook) продуктов, чтобы предотвратить проблемы с изображениями: Windows Live Mail не будет отображать изображения при использовании этого тега.</p>



<p>Мы включили элемент&nbsp;<code>title</code>, хотя лучше оставить его пустым. Тег&nbsp;<code>title</code>&nbsp;требуется для валидации XHTML, но некоторые нативные почтовые клиенты Android будут отображать этот заголовок перед предзаголовком в предварительном просмотре почты, что не является идеальным.</p>



<p>Далее между&nbsp;<code>&lt;!--[if (gte mso 9)|(IE)]&gt;</code>&nbsp;и&nbsp;<code>&lt;![endif]--&gt;</code>&nbsp;у нас есть условие CSS для Outlook, чтобы заставить его свернуть границы у всех таблиц и предотвратить нежелательные зазоры. Это правило нацелено на все версии Microsoft Outlook начиная с &lt; 9 версии, а также на все версии Outllok, которые используют IE для отображения (начиная Outllok 2000-2003).</p>



<p>В&nbsp;<code>body</code>&nbsp;мы, во-первых, имеем тег&nbsp;<code>center</code>, чтобы центрировать его контент и задать опору, по причине наличия нескольких глобальных CSS свойств (с тех пор как тег&nbsp;<code>body</code>&nbsp;часто ‛раздевают‛ в почтовых клиентах). Мы также имеем&nbsp;<code>&lt;div class="webkit"&gt;</code>&nbsp;для ранних версий основанных на webkit почтовых клиентах (в основном это Apple Mail 6 и ниже и Outlook 2011 в некоторых случаях). Эти ранние версии поддерживают только&nbsp;<code>max-width</code>&nbsp;на блочных элементах и это простой способ обеспечить нашим макетам показ с корректным размером, обернув его в этот&nbsp;<code>div</code>.</p>



<h2 class="wp-block-heading" id="glava2">2.Определяем стили</h2>



<p>Затем создайте пустой CSS файл styles.css. В этот файл вставьте следующий код:</p>



<h4 class="wp-block-heading">CSS</h4>



<pre class="wp-block-code"><code>/* Basics */
body {
Margin: 0;
    padding: 0;
    min-width: 100%;
    background-color: #ffffff;
}
table {
    border-spacing: 0;
    font-family: sans-serif;
    color: #333333;
}
td {
    padding: 0;
}
img {
    border: 0;
}
.wrapper {
    width: 100%;
    table-layout: fixed;
    -webkit-text-size-adjust: 100%;
    -ms-text-size-adjust: 100%;
}
.webkit {
    max-width: 600px;
}</code></pre>



<p>Здесь я обнуляю все поля и отступы у тега&nbsp;<code>body</code>, таблиц и ячеек таблицы и обнуляю любые границы, которые могут появиться у изображений. Наши стили для&nbsp;<code>table</code>&nbsp;и&nbsp;<code>td</code>&nbsp;занимают место атрибутов HTML&nbsp;<code>cellpadding</code>&nbsp;и&nbsp;<code>cellspacing</code>. Что использовать (атрибуты или стили) полностью зависит от вас; в прошлом я всегда использовала атрибуты HTML вместо CSS свойств там, где это возможно, однако при работе на более масштабных проектах я обнаружила, что определение стилей в CSS является более управляемым, особенно, если вы работаете на платформе, которая преобразует ваш CSS код во встроенный CSS.</p>



<p>Я обычно включаю&nbsp;<code>min-width</code>&nbsp;тегу&nbsp;<code>body</code>, чтобы избежать ситуаций, когда контент не принимает полную ширину на области просмотра мобильных устройств, и это всегда хорошая практика, чтобы установить цвет фона, даже если это белый цвет, чтобы избежать цвета обоев в Outlook или Lotus Notes.</p>



<p>Для элемента wrapper также есть несколько свойств для предотвращения странного поведения и изменением размеров у текста на Windows Phones and iOS, а также&nbsp;<code>table-layout: fixed</code>, чтобы обеспечить центрированному контенту актуальное центрирование в Yahoo mail. Мы устанавливаем&nbsp;<code>max-width 600px</code>&nbsp;нашему .<code>webkit</code>, чтобы ограничить содержимое в Apple Mail 6 (и ниже) и Outlook 2011.</p>


</br>



<h2 class="wp-block-heading" id="glava3">3 Создание структуры у внешнего контейнера</h2>



<p>Мы начнем с одного из ключевых строительных блоков для этого метода: условных таблиц для Outlook, которые скрыты от других клиентов. Нам требуется это потому, что мы будем использовать свойство <code>max-width</code>, которое не поддерживается Outlook. Поэтому нам требуется создать таблицы специально для Outlook с явной пиксельной шириной, чтобы заключить все в Outlook.</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/10/0041.svg" alt="" class="wp-image-2295" width="1155" height="530"/></figure></div>



<p>Используем &#171;условные&#187; таблицы для Outlook, так как Outlook не поддерживает свойство&nbsp;<code>max-width</code>.</p>



<p>Таким образом в нашем HTML файле давайте удалим [content goes here] и вставим в следующий код. Я придерживаюсь правила выравнивать условные комментарии по левому краю, но вы можете форматировать их как угодно.</p>



<h4 class="wp-block-heading">HTML</h4>



<pre class="wp-block-code"><code>&lt;!--&#091;if (gte mso 9)|(IE)]>
&lt;table width="600" align="center">
&lt;tr>
&lt;td>
&lt;!&#091;endif]-->
    &lt;table class="outer" align="center">
    &lt;tr>
            &lt;td>
                &#091;content goes here]
            &lt;/td>
        &lt;/tr>
    &lt;/table>
&lt;!--&#091;if (gte mso 9)|(IE)]>
&lt;/td>
&lt;/tr>
&lt;/table>
&lt;!&#091;endif]--></code></pre>



<p><strong>Примечание</strong>: здесь нет стилей для ‛условных‛ таблицах. Я собираюсь использовать инструмент <a href="http://inliner.cm/" target="_blank" rel="noreferrer noopener nofollow">inliner.cm</a>, который также встраивает стили внутрь ‛условных‛ таблиц. Если вы собираетесь использовать другой инструмент, то он может не делать этого, так что удостоверьтесь, что ваш инструмент добавляет <code>cellpadding="0" cellspacing="0" border="0"</code> в ваши условные Outlook таблицы.</p>



<p>Внутри условных таблиц вы можете увидеть элемент&nbsp;<code>&lt;table class="outer"&gt;</code>, который является ключевым внешним блоком для всех клиентов кроме Outlook.</p>



<p>Мы хотим, чтобы эта внешняя таблица имела <code>100%</code> ширину на маленьких экранах, но на больших экранах максимальная ширина должна быть <code>600px</code>. Поэтому нам необходимо установить ширину в <code>100%</code> и задать <code>max-width 600px</code>.</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/10/0051.svg" alt="" class="wp-image-2296" width="983" height="1424"/></figure></div>



<p>Ширина нашей таблицы равна&nbsp;<code>100%</code>, пока она не достигет&nbsp;<code>600px</code>.</p>



<p>Итак вставьте следующий код в наш styles.css:</p>



<h4 class="wp-block-heading">CSS</h4>



<pre class="wp-block-code"><code>.outer {
Margin: 0 auto;
    width: 100%;
    max-width: 600px;
}</code></pre>



<p>Мы также имеем объявление <code>Margin: 0 auto;</code> с целью центрировать таблицу в Yahoo при просмотре в Chrome. Хотя margin применяется ради Yahoo, я всегда пишу <code>Margin</code> с заглавной буквы, таким образом Outlook не сможет его обрезать; за этот хак спасибо – <a href="https://www.campaignmonitor.com/blog/post/3921/outlook.com-drops-margin-and-float-support-entirely" target="_blank" rel="noreferrer noopener nofollow">ссылка</a>.</p>



<p>Сейчас мы имеем внешнюю структуру, самое время перейти к добавлению контента.</p>


</br>



<h2 class="wp-block-heading" id="glava4">4. Добавление баннера на всю ширину</h2>



<p>Во-первых, скачайте файлы статьи и переместите директорию /images в ту же папку, где у вас лежит index.html.</p>



<p>Сейчас давайте добавим класс&nbsp;<code>full-width-image</code>&nbsp;к&nbsp;<code>td</code>&nbsp;внутри нашей таблицы&nbsp;<code>.outer</code>, затем заменим наш заполнитель [content goes here] на тег&nbsp;<code>img</code>, таким образом наша таблица выглядит так:</p>



<h4 class="wp-block-heading">HTML</h4>



<pre class="wp-block-code"><code>&lt;table class="outer" align="center">
    &lt;tr>
        &lt;td class="full-width-image">
            &lt;img src="images/header.jpg" width="600" alt="" />
        &lt;/td>
    &lt;/tr>
&lt;/table></code></pre>



<p>Ширина для изображения задана в пикселях и в HTML, таким образом Outlook будет отображать изображение корректно, но нам необходимо перезаписать ширину в CSS на&nbsp;<code>100%</code>, чтобы изображение могло свободно менять размер в других клиентах.</p>



<h4 class="wp-block-heading">CSS</h4>



<pre class="wp-block-code"><code>.full-width-image img {
    width: 100%;
    max-width: 600px;
    height: auto;
}</code></pre>



<p>Мы также установили&nbsp;<code>max-width</code>&nbsp;в соответствии с&nbsp;<code>px</code>&nbsp;шириной, которая установлена в HTML (<code>600px</code>) так как Windows Phone не всегда хороши, когда&nbsp;<code>max-width</code>&nbsp;установлено&nbsp;<code>100%</code>. Мы установили высоте&nbsp;<code>auto</code>, чтобы наше изображение не ‛разваливалось‛ при ошибочном соотношении сторон (aspect ratio).&nbsp;(метод).</p>



<h2 class="wp-block-heading" id="glava5">5. Добавляем одинарною колонку</h2>



<p>Добавьте строку к&nbsp;<code>outer</code>&nbsp;таблице со следующей разметкой:</p>



<h4 class="wp-block-heading">HTML</h4>



<pre class="wp-block-code"><code>&lt;tr>
&lt;td class="one-column">
        &lt;table width="100%">
            &lt;tr>
                &lt;td class="inner contents">
                    &lt;p class="h1">Lorem ipsum dolor sit amet&lt;/p>
                    &lt;p>Maecenas sed ante pellentesque, posuere leo id, eleifend dolor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Praesent laoreet malesuada cursus. Maecenas scelerisque congue eros eu posuere. Praesent in felis ut velit pretium lobortis rhoncus ut erat.&lt;/p>
                &lt;/td>
            &lt;/tr>
        &lt;/table>
    &lt;/td>
&lt;/tr></code></pre>



<p>И следующие стили в наш CSS файл:</p>



<h4 class="wp-block-heading">CSS</h4>



<pre class="wp-block-code"><code>.inner {
padding: 10px;
}
p {
    Margin: 0;
}
a {
    color: #ee6a56;
    text-decoration: underline;
}
.h1 {
    font-size: 21px;
    font-weight: bold;
    Margin-bottom: 18px;
}
.h2 {
    font-size: 18px;
    font-weight: bold;
    Margin-bottom: 12px;
}

/* One column layout */
.one-column .contents {
    text-align: left;
}
.one-column p {
    font-size: 14px;
    Margin-bottom: 10px;
}</code></pre>



<p>Отметьте, что я использую тег&nbsp;<code>p&nbsp;</code>и набор классов для его стилизации. Мне нравится использовать параграф, чтобы стилизовать текст, и вы можете управлять им достаточно легко, благодаря хаку с М, который я упоминал выше. Также я использую класс&nbsp;<code>.h1</code>&nbsp;вместо тега&nbsp;<code>h1</code>, так как Outlook имеет&nbsp;<code>h1</code>,&nbsp;<code>h2</code>&nbsp;и&nbsp;<code>h3</code>&nbsp;стили, которые всегда переопределяют ваши стили.</p>



<p>Таким образом в CSS выше мы создали&nbsp;<code>10px</code>&nbsp;<code>padding</code>&nbsp;для нашей колонке, мы сбросили все поля для параграфов, установили базовые стили для ссылок и задали классы&nbsp;<code>.h1</code>,&nbsp;<code>.h2</code>&nbsp;, затем выровняли тест по левому краю и стилизовали параграфы.</p>


</br>



<h2 class="wp-block-heading" id="glava6">6. Добавляем двухколончатый макет*</h2>



<p>* который может быть центрирован в случае стека (см. скрин ниже).</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/10/0061.svg" alt="" class="wp-image-2297" width="1147" height="725"/></figure></div>



<p>Мы намерены создать двухколончатый макет для десктопа, который на мобильных устройствах расположится вертикально, образуя единую центрированную колонку.</p>



<p>Для начала добавьте новую строку в&nbsp;<code>table.outer</code>. Она содержит ячейку с классом&nbsp;<code>.two-column</code>, внутри которого расположены ‛условные‛ таблицы для Outlook с двумя колонками шириной в&nbsp;<code>50%</code>:</p>



<h4 class="wp-block-heading">HTML</h4>



<pre class="wp-block-code"><code>&lt;tr>
&lt;td class="two-column">
        &lt;!--&#091;if (gte mso 9)|(IE)]>
        &lt;table width="100%">
        &lt;tr>
        &lt;td width="50%" valign="top">
        &lt;!&#091;endif]-->
        &#091;column to go here]
        &lt;!--&#091;if (gte mso 9)|(IE)]>
        &lt;/td>&lt;td width="50%" valign="top">
        &lt;!&#091;endif]-->
        &#091;column to go here]
        &lt;!--&#091;if (gte mso 9)|(IE)]>
        &lt;/td>
        &lt;/tr>
        &lt;/table>
        &lt;!&#091;endif]-->
    &lt;/td>
&lt;/tr></code></pre>



<p>Эти условные колонки важны так как без них Outlook не позволит расположить ваши 2 таблицы бок о бок. Outlook не поддерживает <code>max-width</code>, поэтому эти колонки помогут ограничить каждый столбец до корректного размера.</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/10/0071.svg" alt="" class="wp-image-2298" width="1143" height="719"/></figure></div>



<p>Сейчас замените каждый [content goes here] заменитель на следующий:</p>



<h4 class="wp-block-heading">HTML</h4>



<pre class="wp-block-code"><code>&lt;div class="column">
&lt;table width="100%">
        &lt;tr>
            &lt;td class="inner">
                &#091;content goes here]
            &lt;/td>
        &lt;/tr>
    &lt;/table>
&lt;/div></code></pre>



<p>Мы собираемся расположить две колонки на больших десктопах и получить центрированный стек (одна под другой) на мобильных устройствах, используя комбинацию&nbsp;<code>text-align: center</code>&nbsp;и&nbsp;<code>display: inline-block</code>. Все строчные и строчно-блочные элементы подчиняются свойству&nbsp;<code>text-align</code>. Поэтому, если мы обернем наши таблицы в&nbsp;<code>div</code>, которому установлено&nbsp;<code>inline-block</code>, мы сможем легко установить нужное выравнивание при стеке, задав свойство&nbsp;<code>text-align</code>&nbsp;на контейнере. Вы можете выравнивать как угодно и&nbsp;<code>inline-block</code>&nbsp;подчинится. Вы можете обозначить таблицу как&nbsp;<code>inline-block</code>, но только если вы не будете вкладывать внутрь другие таблицы. Вещи могут начать вести себя странно, если вы начнете вкладывать таблицы внутрь&nbsp;<code>inline-block</code>&nbsp;таблиц, поэтому если вам нужна вложенность всегда используйте&nbsp;<code>inline-block</code>&nbsp;для&nbsp;<code>div</code>.</p>



<p>Давайте стилизуем наш контейнер (ячейку)&nbsp;<code>.two-column</code>&nbsp;с выбранным нами выравниванием. Мы также собираемся добавить&nbsp;<strong><code>font-size: 0</code>, чтобы избавиться от пробелов между колонками (нашими&nbsp;<code>inline-block</code>&nbsp;элементами) внутри ячейки</strong>.</p>



<h4 class="wp-block-heading">CSS</h4>



<pre class="wp-block-code"><code>/*Two column layout*/
.two-column {
text-align: center;
    font-size: 0;
}</code></pre>



<p>Сейчас давайте стилизуем наши inline-block div, которые фактически являются колонками.</p>



<h4 class="wp-block-heading">CSS</h4>



<pre class="wp-block-code"><code>.two-column .column {
width: 100%;
    max-width: 300px;
    display: inline-block;
    vertical-align: top;
}</code></pre>



<p>Мы используем ширину&nbsp;<code>100%</code>&nbsp;с&nbsp;<code>max-width 300px</code>, таким образом колонка будет иметь&nbsp;<code>100%</code>&nbsp;ширину на экранах с областью просмотра ниже&nbsp;<code>300px</code>.</p>



<p>Вы можете установить&nbsp;<code>vertical-align</code>&nbsp;(для вертикального выравнивания) что угодно:&nbsp;<code>top</code>,&nbsp;<code>center</code>&nbsp;или&nbsp;<code>bottom</code>.&nbsp;<code>vertical-align:top</code>&nbsp;означает, что каждая колонка как будто является ячейкой таблицы со свойством HTML&nbsp;<code>valign=‛top‛</code>;&nbsp;<code>middle</code>&nbsp;как будто имеет&nbsp;<code>valign=‛middle‛</code>. Отметьте, что вы можете иметь много строк из этих&nbsp;<code>div</code>&nbsp;внутри одной и той же ячейки и вертикальное выравнивание будет всегда диктоваться вертикальным выравниванием на row-by-row основе. Это довольно изящно. Также, чтобы подстраховаться, так как Outlook не понимает свойство&nbsp;<code>vertical-align</code>, задайте в ‛условных‛ таблицах нужное значение для атрибута&nbsp;<code>valign</code>.</p>



<p>Далее мы добавим таблицу с двумя строками в каждой колонке. Это объясняется тем, что когда складывается стек на мобильных устройствах, под каждым изображением находится текст.</p>



<p>Давайте наши два [content goes here] на следующий код:</p>



<h4 class="wp-block-heading">HTML</h4>



<pre class="wp-block-code"><code>&lt;table class="contents">
    &lt;tr>
            &lt;td>
                    &lt;img src="images/two-column-01.jpg" width="280" alt="" />
            &lt;/td>
    &lt;/tr>
    &lt;tr>
            &lt;td class="text">
                Maecenas sed ante pellentesque, posuere leo id, eleifend dolor.
                Class aptent taciti sociosqu ad litora torquent
                per conubia nostra, per inceptos himenaeos.
            &lt;/td>
    &lt;/tr>
&lt;/table></code></pre>



<p>Каждая колонка (<code>.inner</code>) имеет&nbsp;<code>300px</code>&nbsp;ширину с&nbsp;<code>10px</code>&nbsp;отступом с каждой стороны, оставляя&nbsp;<code>280px</code>&nbsp;для изображения.</p>



<p>Далее стилизуем класс&nbsp;<code>.contents</code>&nbsp;задав&nbsp;<code>100%</code>&nbsp;ширину.</p>



<h4 class="wp-block-heading">CSS</h4>



<pre class="wp-block-code"><code>.contents {
    width: 100%;
}</code></pre>



<p>И затем давайте добавим наши стили двухколончатому макету: поставим размер текста, выравнивание, обеспечим изображению&nbsp;<code>100%</code>&nbsp;ширину, добавим тексту небольшой верхний отступ.</p>



<h4 class="wp-block-heading">CSS</h4>



<pre class="wp-block-code"><code>.two-column .contents {
font-size: 14px;
    text-align: left;
}
.two-column img {
    width: 100%;
    max-width: 280px;
    height: auto;
}
.two-column .text {
    padding-top: 10px;
}</code></pre>



<p>Теперь вы имеетe двухколончатый макет, который складывается вертикально, когда вы меняете размер браузера или, когда ваша область просмотра уже чем&nbsp;<code>300px</code>.</p>


</br>



<h2 class="wp-block-heading" id="glava7">7. Добавляем трехколончатый макет</h2>



<p>Снова мы собираемся создать расположенные рядом колонки, которые складываются на мобильных устройствах посредством комбинации&nbsp;<code>text-align: center</code>&nbsp;и&nbsp;<code>display: inline-block</code>.</p>



<p>Мы будем использовать <code>text-align: center</code>, чтобы наши колонки центрировались в стеке, но вы всегда можете использовать левое или правой выравнивание. Вот пример выравнивания по центру и по левому краю.</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/10/0081.svg" alt="" class="wp-image-2299" width="1134" height="430"/></figure></div>



<p>Пример стека из трех колонок, при использовании <code>text-align: center</code> у контейнера.</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/10/0091.svg" alt="" class="wp-image-2300" width="1148" height="437"/></figure></div>



<p>Пример стека из трех колонок, при использовании&nbsp;<code>text-align: left</code>&nbsp;у контейнера.</p>



<p>Таким образом мы повторяем двухколончатый процесс с дополнительной колонкой. Добавьте новую строку в таблицу&nbsp;<code>.outer</code>. (Обычно я предпочитаю использовать ширину в&nbsp;<code>%</code>&nbsp;для ячеек в моей ‛Условной‛ Outlook таблицах, но в этом случае проще установить ширину каждой в&nbsp;<code>200px</code>).</p>



<h4 class="wp-block-heading">HTML</h4>



<pre class="wp-block-code"><code>&lt;tr>
&lt;td class="three-column">
        &lt;!--&#091;if (gte mso 9)|(IE)]>
        &lt;table width="100%">
        &lt;tr>
        &lt;td width="200" valign="top">
        &lt;!&#091;endif]-->
        &#091;column to go here]
        &lt;!--&#091;if (gte mso 9)|(IE)]>
        &lt;/td>&lt;td width="200" valign="top">
        &lt;!&#091;endif]-->
        &#091;column to go here]
        &lt;!--&#091;if (gte mso 9)|(IE)]>
        &lt;/td>&lt;td width="200" valign="top">
        &lt;!&#091;endif]-->
        &#091;column to go here]
        &lt;!--&#091;if (gte mso 9)|(IE)]>
        &lt;/td>
        &lt;/tr>
        &lt;/table>
        &lt;!&#091;endif]-->
    &lt;/td>
&lt;/tr></code></pre>



<p>Сейчас добавим следующий CSS, чтобы придать дополнительный отступ нашей строке и зададим другие необходимые свойства. Также зададим стили для наших&nbsp;<code>div</code>&nbsp;столбцов, которые мы вскоре добавим, и ширина которых в данном случае будет составлять 200px.</p>



<h4 class="wp-block-heading">CSS</h4>



<pre class="wp-block-code"><code>/*Three column layout*/
.three-column {
    text-align: center;
    font-size: 0;
    padding-top: 10px;
    padding-bottom: 10px;
}
.three-column .column {
    width: 100%;
    max-width: 200px;
    display: inline-block;
    vertical-align: top;
}
.three-column .contents {
    font-size: 14px;
    text-align: center;
}
.three-column img {
    width: 100%;
    max-width: 180px;
    height: auto;
}
.three-column .text {
    padding-top: 10px;
}</code></pre>



<p>Сейчас давайте вставим наши колонки заменив заполнитель [content goes here].</p>



<h4 class="wp-block-heading">HTML</h4>



<pre class="wp-block-code"><code>&lt;table class="column">
    &lt;tr>
        &lt;td class="inner">
            &lt;table class="contents">
                &lt;tr>
                    &lt;td>
                        &lt;img src="images/three-column-01.jpg" width="180" alt="" />
                    &lt;/td>
                &lt;/tr>
                &lt;tr>
                    &lt;td class="text">
                        Scelerisque congue eros eu posuere. Praesent in felis ut velit pretium lobortis rhoncus ut erat.
                    &lt;/td>
                &lt;/tr>
            &lt;/table>
        &lt;/td>
    &lt;/tr>
&lt;/table></code></pre>



<p>Вот и все. Теперь вы должны иметь трехколончатый макет, где колонки будут складываться на узких экранах.</p>



<p>Благодаря тому, что этот макет имеет нечетное число колонок, иногда вы сможете найти у себя две колонки наверху и одну колонку внизу. Иногда это может выглядеть немного разбалансированно (хотя в основном хорошо). Часто лучший способ обойти это использовать выравнивание по левому краю, либо использовать несколько строк из трех колонок (на экранах со средним разрешение остается четное количество колонок в строке).</p>



<h3 class="wp-block-heading">Добавляем трехколончатый макет с несколькими строками</h3>



<p>Когда вы хотите добавить несколько строк в ваш макет, вы можете добавить сколько угодно <code>inline-block</code> элементов к контейнеру ячейки. Ниже вы можете увидеть, что на узких экранах идет простое переформатирование колонок.</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/10/0101.svg" alt="" class="wp-image-2301" width="1133" height="713"/></figure></div>



<p>Хотя вам не требуется разделять сроки div-ов для большинства клиентов, вам потребуется добавить дополнительный <code>&lt;tr></code> к вашей ‛условной‛ таблице в Outlook.</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/10/0111.svg" alt="" class="wp-image-2302" width="1134" height="504"/></figure></div>



<p>Здесь вы можете увидеть как в ‛условной‛ таблице был добавлен новый ряд, который содержит три дополнительных ячейки каждая шириной в&nbsp;<code>200px</code>.</p>



<h4 class="wp-block-heading">HTML</h4>



<pre class="wp-block-code"><code>&lt;tr>
    &lt;td class="three-column">
        &lt;!--&#091;if (gte mso 9)|(IE)]>
        &lt;table width="100%">
        &lt;tr>
        &lt;td width="200" valign="top">
        &lt;!&#091;endif]-->
        &#091;column to go here]
        &lt;!--&#091;if (gte mso 9)|(IE)]>
        &lt;/td>&lt;td width="200" valign="top">
        &lt;!&#091;endif]-->
        &#091;column to go here]
        &lt;!--&#091;if (gte mso 9)|(IE)]>
        &lt;/td>&lt;td width="200" valign="top">
        &lt;!&#091;endif]-->
        &#091;column to go here]
        &lt;!--&#091;if (gte mso 9)|(IE)]>
        &lt;/td>
        &lt;/tr>
        &lt;tr>
        &lt;td width="200" valign="top">
        &lt;!&#091;endif]-->
        &#091;column to go here]
        &lt;!--&#091;if (gte mso 9)|(IE)]>
        &lt;/td>&lt;td width="200" valign="top">
        &lt;!&#091;endif]-->
        &#091;column to go here]
        &lt;!--&#091;if (gte mso 9)|(IE)]>
        &lt;/td>&lt;td width="200" valign="top">
        &lt;!&#091;endif]-->
        &#091;column to go here]
        &lt;!--&#091;if (gte mso 9)|(IE)]>
        &lt;/td>
        &lt;/tr>
        &lt;/table>
        &lt;!&#091;endif]-->
    &lt;/td>
&lt;/tr></code></pre>



<p>Далее добавим таблицу, заменив заполнитель [content goes here] у каждой ‛условной‛ ячейки.</p>


</br>



<h4 class="wp-block-heading">HTML</h4>



<pre class="wp-block-code"><code>&lt;table class="column">
    &lt;tr>
        &lt;td class="inner contents">
            &lt;p class="h2">Heading&lt;/p>
            &lt;p>Class eleifend aptent taciti sociosqu ad litora torquent conubia&lt;/p>
            &lt;p>&lt;a href="#">Read more&lt;/a>&lt;/p>
        &lt;/td>
    &lt;/tr>
&lt;/table></code></pre>



<p>Теперь, если вы измените размеры вашего окна, вы снова увидите, что стек колонок заполняет свободное пространство. Три колонки будут уменьшены до двух с тремя рядами, пока они не схлопнутся наконец до одной колонки с шестью рядами.</p>



<h3 class="wp-block-heading">Еще больше колонок</h3>



<p>Вы можете иметь столько колонок сколько хотите, но не забывайте о том, что ширина блоков должна быть равна ширине контейнера и не забывайте об ‛условных‛ таблицах для Outlook.</p>



<h2 class="wp-block-heading" id="glava8">8. Добавляем двухколончатый ‛Sidebar‛ макет</h2>



<p>Далее мы создадим двухколончатый макет с&nbsp;<code>500px</code>&nbsp;колонкой и затем&nbsp;<code>100px</code>&nbsp;sidebar для иконки.</p>



<p>Во-первых, добавим строку и ячейку с классом&nbsp;<code>.left-sidebar</code>&nbsp;и внутри ячейки расположим ‛условную‛ таблицу для Outlook, которая имеет простую строку и две неравные колонки.</p>



<h4 class="wp-block-heading">HTML</h4>



<pre class="wp-block-code"><code>&lt;tr>
    &lt;td class="left-sidebar">
        &lt;!--&#091;if (gte mso 9)|(IE)]>
        &lt;table width="100%">
        &lt;tr>
        &lt;td width="100">
        &lt;!&#091;endif]-->
        &#091;column to go here]
        &lt;!--&#091;if (gte mso 9)|(IE)]>
        &lt;/td>&lt;td width="500">
        &lt;!&#091;endif]-->
        &#091;column to go here]
        &lt;!--&#091;if (gte mso 9)|(IE)]>
        &lt;/td>
        &lt;/tr>
        &lt;/table>
        &lt;!&#091;endif]-->
    &lt;/td>
&lt;/tr></code></pre>



<p>Примечание: здесь я использую несколько классов для одного элемента. Некоторые inliner инструменты не поддерживают это, поэтому сперва проверьте вашу систему или платформу. Как уже говорилось выше, я использую inliner.cm, чтобы встроить свой CSS, который поддерживает множественные классы.</p>



<p>В первую колонку добавьте таблицу, которая содержит нашу иконку:</p>



<h4 class="wp-block-heading">HTML</h4>



<pre class="wp-block-code"><code>&lt;table class="column left">
    &lt;tr>
        &lt;td class="inner">
            &lt;img src="images/sidebar-01.jpg" width="80" alt="" />
        &lt;/td>
    &lt;/tr>
&lt;/table></code></pre>



<p>Во вторую колонку добавьте таблицу с текстом и ссылкой:</p>



<h4 class="wp-block-heading">HTML</h4>



<pre class="wp-block-code"><code>&lt;table class="column right">
    &lt;tr>
        &lt;td class="inner contents">
            Praesent laoreet malesuada cursus. Maecenas scelerisque congue eros eu posuere. Praesent in felis ut velit pretium lobortis rhoncus ut erat. &lt;a href="#">Read&amp;nbsp;on&lt;/a>
        &lt;/td>
    &lt;/tr>
&lt;/table></code></pre>



<p>Эти таблицы просты и не имеют никаких вложений, чтобы сэкономить на разметке я собираюсь установить таблицам&nbsp;<code>display: inline-block</code>, а не оборачивать их в&nbsp;<code>div</code>. Как говорилось выше, эта практика имеет смысл только когда вы используете много вложенных элементов. Если вы будете использовать много вложений, оберните таблицу в&nbsp;<code>div</code>&nbsp;и вставьте&nbsp;<code>.column .right</code>&nbsp;в него.</p>



<p>Теперь давайте стилизуем контейнер и настроим колонки:</p>



<h4 class="wp-block-heading">CSS</h4>



<pre class="wp-block-code"><code>/* Left sidebar layout */
.left-sidebar {
text-align: center;
    font-size: 0;
}
.left-sidebar .column {
    width: 100%;
    display: inline-block;
    vertical-align: middle;
}
.left-sidebar .left {
    max-width: 100px;
}
.left-sidebar .right {
    max-width: 500px;
}
.left-sidebar .img {
    width: 100%;
    max-width: 80px;
    height: auto;
}</code></pre>



<p>Наконец, давайте настроим текст и цвет ссылки:</p>



<h4 class="wp-block-heading">CSS</h4>



<pre class="wp-block-code"><code>.left-sidebar .contents {
font-size: 14px;
    text-align: center;
}
.left-sidebar a {
    color: #85ab70;
}</code></pre>



<p>Теперь вы должны иметь левую колонку и когда вы уменьшите окно вашего браузера, иконка центрируется и встанет выше текста.</p>


</br>



<h2 class="wp-block-heading" id="glava9">9. Добавляем перевернутый sidebar</h2>



<p>Теперь расположим текст по левой стороне, а иконку по правой. Но порядок расположения иконки и текста на мобильных устройствах оставим тем же.</p>



<p>Во-первых, мы собираемся скопировать и вставить весь ряд с нашей&nbsp;<code>.left-sidebar</code>&nbsp;ячейкой, и единственное что мы поменяем это класс&nbsp;<code>.left-sidebar</code>&nbsp;на&nbsp;<code>.right-sidebar</code>.</p>



<h4 class="wp-block-heading">HTML</h4>



<pre class="wp-block-code"><code>&lt;tr>
&lt;td class="right-sidebar">
        &lt;!--&#091;if (gte mso 9)|(IE)]>
        &lt;table width="100%">
        &lt;tr>
        &lt;td width="100">
        &lt;!&#091;endif]-->
        &lt;table class="column left">
            &lt;tr>
                &lt;td class="inner contents">
                    &lt;img src="images/sidebar-02.jpg" width="80" alt="" />
                &lt;/td>
            &lt;/tr>
        &lt;/table>
        &lt;!--&#091;if (gte mso 9)|(IE)]>
        &lt;/td>&lt;td width="500">
        &lt;!&#091;endif]-->
        &lt;table class="column right">
            &lt;tr>
                &lt;td class="inner contents">
                    Maecenas sed ante pellentesque, posuere leo id, eleifend dolor. Class aptent taciti sociosqu ad litora torquent per conubia nostra. &lt;a href="#">Per&amp;nbsp;inceptos&lt;/a>
                &lt;/td>
            &lt;/tr>
        &lt;/table>
        &lt;!--&#091;if (gte mso 9)|(IE)]>
        &lt;/td>
        &lt;/tr>
        &lt;/table>
        &lt;!&#091;endif]-->
    &lt;/td>
&lt;/tr></code></pre>



<p>Остальное все также.</p>



<p>Что мы собираемся делать это использовать <code>dir=‛rtl‛</code> (означает направление справа налево) к нашей выгоде. Это свойство используется для алфавитов, которые работают справа налево, такие как арабский. Но в нашем случае это используется для того, чтобы рассказать почтовым клиентам о том, что необходимо отображать элементы в обратном порядке.</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/10/0121.jpg" alt="" class="wp-image-2303" width="1136" height="495" srcset="https://clip-clap.ru/wp-content/uploads/2020/10/0121.jpg 589w, https://clip-clap.ru/wp-content/uploads/2020/10/0121-300x131.jpg 300w" sizes="(max-width: 1136px) 100vw, 1136px" /></figure></div>



<p>Во-первых, контейнеру (<code>.right-sidebar</code>) необходимо добавить&nbsp;<code>dir=‛rtl‛</code>. Этим мы отображаем наши таблицы справа налево. Таким образом наш открывающий тег должен выглядеть так:</p>



<h4 class="wp-block-heading">HTML</h4>



<pre class="wp-block-code"><code>&lt;td class="right-sidebar" dir="rtl"></code></pre>



<p>Затем в Outlook условных комментариях, нам также потребуется добавить&nbsp;<code>dir=‛rtl‛</code>&nbsp;таблице, так как мы говорим таблице отобразить&nbsp;<code>&lt;td&gt;</code>&nbsp;в обратном порядке.</p>



<p>Таким образом начало у наших условных комментариев должно выглядеть так:</p>



<h4 class="wp-block-heading">HTML</h4>



<pre class="wp-block-code"><code>&lt;!--&#091;if (gte mso 9)|(IE)]>
&lt;table width="100%" dir="rtl">
&lt;tr>
&lt;td width="100">
&lt;!&#091;endif]--></code></pre>



<p>Наконец, нам потребуется добавить&nbsp;<code>dir=‛ltr‛</code>&nbsp;к&nbsp;<code>.column-left</code>&nbsp;и&nbsp;<code>.column-right</code>&nbsp;таблицам, так как внутри них есть контент (англ. Текст, который требует слева направо). Если мы не сделаем это, то они будут наследовать направление от родителя &#8212; справа налево.</p>


</br>



<p>Итак колонки&nbsp;<code>.column-left</code>&nbsp;и&nbsp;<code>.column-right</code>&nbsp;должны выглядеть так:</p>



<h4 class="wp-block-heading">HTML</h4>



<pre class="wp-block-code"><code>&lt;table class="column left" dir="ltr">
    &lt;tr>
        &lt;td class="inner contents">
            &lt;img src="images/sidebar-02.jpg" width="80" alt="" />
        &lt;/td>
    &lt;/tr>
&lt;/table></code></pre>



<h4 class="wp-block-heading">HTML</h4>



<pre class="wp-block-code"><code>&lt;table class="column right" dir="ltr">
&lt;tr>
        &lt;td class="inner contents">
            Maecenas sed ante pellentesque, posuere leo id, eleifend dolor. Class aptent taciti sociosqu ad litora torquent per conubia nostra. &lt;a href="#">Per&amp;nbsp;inceptos&lt;/a>
        &lt;/td>
    &lt;/tr>
&lt;/table></code></pre>



<p>Таким образом наша полная строка должна выглядеть так:</p>



<h4 class="wp-block-heading">HTML</h4>



<pre class="wp-block-code"><code>&lt;tr>
    &lt;td class="right-sidebar" dir="rtl">
        &lt;!--&#091;if (gte mso 9)|(IE)]>
        &lt;table width="100%" dir="rtl">
        &lt;tr>
        &lt;td width="100">
        &lt;!&#091;endif]-->
        &lt;table class="column left" dir="ltr">
            &lt;tr>
                &lt;td class="inner contents">
                    &lt;img src="images/sidebar-02.jpg" width="80" alt="" />
                &lt;/td>
            &lt;/tr>
        &lt;/table>
        &lt;!--&#091;if (gte mso 9)|(IE)]>
        &lt;/td>&lt;td width="500">
        &lt;!&#091;endif]-->
        &lt;table class="column right" dir="ltr">
            &lt;tr>
                &lt;td class="inner contents">
                    Maecenas sed ante pellentesque, posuere leo id, eleifend dolor. Class aptent taciti sociosqu ad litora torquent per conubia nostra. &lt;a href="#">Per&amp;nbsp;inceptos&lt;/a>
                &lt;/td>
            &lt;/tr>
        &lt;/table>
        &lt;!--&#091;if (gte mso 9)|(IE)]>
        &lt;/td>
        &lt;/tr>
        &lt;/table>
        &lt;!&#091;endif]-->
    &lt;/td>
&lt;/tr></code></pre>



<p>Наконец добавим стили, которые почти те же, что и у&nbsp;<code>.left-sidebar</code>, кроме цвета ссылки.</p>



<h4 class="wp-block-heading">CSS</h4>



<pre class="wp-block-code"><code>/* Right sidebar layout */
.right-sidebar {
text-align: center;
    font-size: 0;
}
.right-sidebar .column {
    width: 100%;
    display: inline-block;
    vertical-align: middle;
}
.right-sidebar .left {
    max-width: 100px;
}
.right-sidebar .right {
    max-width: 500px;
}
.right-sidebar .img {
    width: 100%;
    max-width: 80px;
    height: auto;
}
.right-sidebar .contents {
    font-size: 14px;
    text-align: center;
}
.right-sidebar a {
    color: #70bbd9;
}</code></pre>



<h2 class="wp-block-heading" id="glava10">10. Прогрессивное улучшение с медиа-запросами</h2>



<p>Сейчас у вас есть полный email шаблон, который отзывчив везде без медиа-запросов. Но, конечно, есть немало почтовых клиентов, которые поддерживают медиа-запросы, таким образом мы можем сделать прогрессивное улучшение для таких клиентов как IOS Mail.</p>



<p>Во-первых, для всех столбцов сделаем ширину&nbsp;<code>100%</code>&nbsp;при области просмотра уже&nbsp;<code>400px</code>. Для трех колонок у изображений установим ширину в&nbsp;<code>50%</code>; для двух в&nbsp;<code>100%</code>. Для колонок переопределяем лишь&nbsp;<code>max-width</code>, так как именно она ограничивает ширину.</p>



<p>Итак добавьте следующий CSS:</p>



<h4 class="wp-block-heading">CSS</h4>



<pre class="wp-block-code"><code>/*Media Queries*/
@media screen and (max-width: 400px) {
.two-column .column,
    .three-column .column {
        max-width: 100% !important;
    }
    .two-column img {
        max-width: 100% !important;
    }
    .three-column img {
        max-width: 50% !important;
    }
}</code></pre>



<p>Между&nbsp;<code>401px</code>&nbsp;и&nbsp;<code>600px</code>&nbsp;добавим следующий код, чтобы сделать появление двух и трех колонок более отзывчивым.</p>



<h4 class="wp-block-heading">CSS</h4>



<pre class="wp-block-code"><code>@media screen and (min-width: 401px) and (max-width: 620px) {
.three-column .column {
        max-width: 33% !important;
    }
    .two-column .column {
        max-width: 50% !important;
    }
}</code></pre>



<h2 class="wp-block-heading" id="glava11">11. Встраивание вашего кода</h2>



<p>Если ваша потовая платформа не заботится о встраивании для вас, то вам потребуется сделать это вручную. Во-первых, уберите тег <code>&lt;link rel="stylesheet" type="text/css" href="styles.css" /></code> в <code>&lt;head></code> вашего документа и замените его на <code>&lt;style type="text/css"></code>. Скопируйте содержимое <code>style.css</code> и вставьте его в элемент <code>&lt;style></code>. Наконец, скопируйте и вставьте полный файл в <a href="http://inliner.cm/" target="_blank" rel="noreferrer noopener nofollow">inliner.cm</a> и дождитесь результата. После окончания результат скопируйте контент и вы готовы к работе.</p>


</br>



<h2 class="wp-block-heading" id="glava12">Несколько финальных замечаний и подсказок</h2>



<p>Бывает непорядок, когда имеются различные отступы у различных колонок. В примере выше вы можете отметить класс <code>.inner</code>, который используется для <code>10px</code> отступов к каждому макету. Это важно, так как когда складывается стек на мобильных устройствах вы задумываете, чтобы все было однородным.</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/10/0131.svg" alt="" class="wp-image-2304" width="1163" height="664"/></figure></div>



<p>Для модернизации определенных проектов этот метод может быть довольно сложным.</p>



<p>Безусловно есть определенные места, для которых можно выйти за пределы&nbsp;<code>600px</code>&nbsp;(но не для Outlook).</p>



<p>Не нужно использовать объявление CSS&nbsp;<code>&lt;style&gt;</code>&nbsp;в HTML-теге&nbsp;<code>&lt;head&gt;</code>, как часто делают при вёрстке веб-страниц. Вместо этого объявление&nbsp;<code>&lt;style&gt;</code>&nbsp;нужно разместить сразу за тегом&nbsp;<code>&lt;body&gt;</code>&nbsp;— однако Gmail ищет любые теги&nbsp;<code>style</code>&nbsp;в письме и удаляет их.</p>



<p>Кроме того стоит рассмотреть вариант, при котором изображение становится фоном для новой HTML-таблицы, которая заключает в себе все строки и столбцы той таблицы, в которой будут отображаться части картинки. Этим можно добиться такого же эффекта, как при «нарезке» изображения при меньшем количестве кода. Нужно помнить, что Outlook 2007 не показывает фоновые изображения — всегда важно тестировать письмо на наиболее важном и популярном у подписчиков почтовом сервисе.</p>



<h2 class="wp-block-heading" id="glava13">Тестируем</h2>



<p>Первыми средствами тестирования писем являются браузеры Firefox и Internet Explorer. Если сообщение хорошо выглядит в них, то велик шанс на то, что и в клиентах вроде Outlook, Yahoo, Gmail и других с ним не будет больших проблем. Если это возможно, следует даже пойти дальше и протестировать письмо в IE 6 — это позволит увидеть, как письмо будет «рендериться» в Outlook 2003. Чтобы примерно понять, как письмо будет показываться на iPhone и iPad нужно протестировать его в Safari.</p>


</br>



<p><a href="http://webdesign.tutsplus.com/ru/tutorials/creating-a-future-proof-responsive-email-without-media-queries--cms-23919" target="_blank" rel="noreferrer noopener nofollow">Источник</a></p>
<p>Сообщение <a href="https://clip-clap.ru/it/html/%d1%81%d0%be%d0%b7%d0%b4%d0%b0%d0%bd%d0%b8%d0%b5-%d0%be%d1%82%d0%b7%d1%8b%d0%b2%d1%87%d0%b8%d0%b2%d1%8b%d1%85-%d0%bf%d0%b8%d1%81%d0%b5%d0%bc-%d0%b1%d0%b5%d0%b7-%d0%bc%d0%b5%d0%b4%d0%b8%d0%b0-%d0%b7/">Создание отзывчивых писем без медиа-запросов</a> появились сначала на <a href="https://clip-clap.ru">Clip-Clap</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://clip-clap.ru/it/html/%d1%81%d0%be%d0%b7%d0%b4%d0%b0%d0%bd%d0%b8%d0%b5-%d0%be%d1%82%d0%b7%d1%8b%d0%b2%d1%87%d0%b8%d0%b2%d1%8b%d1%85-%d0%bf%d0%b8%d1%81%d0%b5%d0%bc-%d0%b1%d0%b5%d0%b7-%d0%bc%d0%b5%d0%b4%d0%b8%d0%b0-%d0%b7/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Подробное руководство по MongoDB, Mongoose</title>
		<link>https://clip-clap.ru/it/%d0%b0%d0%b4%d0%bc%d0%b8%d0%bd%d0%b8%d1%81%d1%82%d1%80%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d0%bf%d0%be%d0%b4%d1%80%d0%be%d0%b1%d0%bd%d0%be%d0%b5-%d1%80%d1%83%d0%ba%d0%be%d0%b2%d0%be%d0%b4%d1%81%d1%82%d0%b2%d0%be-%d0%bf%d0%be-mongodb-mongoose/</link>
					<comments>https://clip-clap.ru/it/%d0%b0%d0%b4%d0%bc%d0%b8%d0%bd%d0%b8%d1%81%d1%82%d1%80%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d0%bf%d0%be%d0%b4%d1%80%d0%be%d0%b1%d0%bd%d0%be%d0%b5-%d1%80%d1%83%d0%ba%d0%be%d0%b2%d0%be%d0%b4%d1%81%d1%82%d0%b2%d0%be-%d0%bf%d0%be-mongodb-mongoose/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Sun, 04 Oct 2020 18:32:54 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[Администрирование]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[Софт и ОС]]></category>
		<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[Mongoose]]></category>
		<guid isPermaLink="false">https://clip-clap.ru/?p=2280</guid>

					<description><![CDATA[<p>Обновление статьи Введение MongoDB состоит из БД, которые состоят из коллекций. Коллекции, в свою очередь, состоят из документов. Каждый документ состоит из полей.www.mongodb.com</p>
<p>Сообщение <a href="https://clip-clap.ru/it/%d0%b0%d0%b4%d0%bc%d0%b8%d0%bd%d0%b8%d1%81%d1%82%d1%80%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d0%bf%d0%be%d0%b4%d1%80%d0%be%d0%b1%d0%bd%d0%be%d0%b5-%d1%80%d1%83%d0%ba%d0%be%d0%b2%d0%be%d0%b4%d1%81%d1%82%d0%b2%d0%be-%d0%bf%d0%be-mongodb-mongoose/">Подробное руководство по MongoDB, Mongoose</a> появились сначала на <a href="https://clip-clap.ru">Clip-Clap</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p><a href="https://clip-clap.ru/it/%d1%81%d0%be%d1%84%d1%82-%d0%b8-%d0%be%d1%81/mongodb-%d0%bf%d0%be%d0%b4%d1%80%d0%be%d0%b1%d0%bd%d0%be%d0%b5-%d1%80%d1%83%d0%ba%d0%be%d0%b2%d0%be%d0%b4%d1%81%d1%82%d0%b2%d0%be/" target="_blank" rel="noreferrer noopener">Обновление статьи</a> </p>



<h2 class="wp-block-heading" id="target-0">Введение</h2>



<p>MongoDB состоит из БД, которые состоят из <strong>коллекций</strong>. Коллекции, в свою очередь, состоят из <strong>документов</strong>. Каждый документ состоит из <strong>полей</strong>.<br><a href="https://www.mongodb.com/" target="_blank" rel="noreferrer noopener nofollow">www.mongodb.com</a></p>



<h2 class="wp-block-heading" id="target-1">MongoDB клиенты</h2>



<ul><li><a href="https://robomongo.org/" target="_blank" rel="noreferrer noopener nofollow">https://robomongo.org/</a></li><li><a href="https://studio3t.com/" target="_blank" rel="noreferrer noopener nofollow">https://studio3t.com/</a> (+)</li></ul>


</br>



<h2 class="wp-block-heading" id="target-2">Конфиг mongodb для windows</h2>



<p>В папке&nbsp;<code>bin</code>&nbsp;создайте файл и назовите его&nbsp;<code>mongodb.config</code>.</p>



<p>Указываем в этом файле путь, где будем хранить БД, например, для windows:</p>



<pre class="wp-block-preformatted">dbpath=c:\mongodb\data</pre>



<h2 class="wp-block-heading" id="target-3">Запуск</h2>



<ul><li><code>mongod</code>&nbsp;&#8212; это&nbsp;<strong>сервер</strong>&nbsp;баз данных MongoDB. Он обрабатывает запросы, управляет форматом данных и выполняет различные операции в фоновом режиме по управлению БД. Командная строка отобразит нам ряд служебной информации, например, что сервер запускается на&nbsp;<code>localhost</code>&nbsp;на порту&nbsp;<code>27017</code>.</li><li><code>mongo</code>&nbsp;&#8212; клиентская&nbsp;<strong>консоль</strong>&nbsp;для взаимодействия с базами данных.</li><li><code>mongodump</code>&nbsp;&#8212; утилита создания бэкапа БД.</li><li><code>mongorestore</code>&nbsp;&#8212; позволяет записывать данные из дампа, созданного&nbsp;<code>mongodump</code>, в новую или существующую БД.</li></ul>



<h3 class="wp-block-heading" id="target-4">Стартуем mongod (сервер):</h3>



<pre class="wp-block-preformatted">mongod --config c:\mongodb\bin\mongodb.config
// не забудьте заранее создать папку data</pre>



<h3 class="wp-block-heading" id="target-5">Подключаемся к запущенному серверу</h3>



<p>Команда&nbsp;<code>mongo</code>&nbsp;позволяет подключиться к запущенному серверу (стартуем&nbsp;<code>mongo</code>&nbsp;оболочку/shell).</p>



<pre class="wp-block-preformatted">mongo</pre>



<h2 class="wp-block-heading" id="target-6">Работаем с БД</h2>



<h3 class="wp-block-heading" id="target-7">Выводим все БД в mongo:</h3>



<pre class="wp-block-preformatted">show dbs</pre>



<h3 class="wp-block-heading" id="target-8">Переходим (и одновременно создаем) к нужной БД</h3>



<pre class="wp-block-preformatted">use name_bd</pre>



<p>Команды&nbsp;<code>db</code>&nbsp;возвращает имя БД, внутри которой мы сейчас находимся:</p>



<pre class="wp-block-preformatted">db
//test</pre>


</br>



<h2 class="wp-block-heading" id="target-9">Коллекции</h2>



<h3 class="wp-block-heading" id="target-10">Показать все коллекции в БД</h3>



<pre class="wp-block-preformatted">show collections</pre>



<h3 class="wp-block-heading" id="target-11">Метод find()</h3>



<p>Показать весь контент нужной коллекции:</p>



<pre class="wp-block-preformatted">db.collection_name.find()</pre>



<p>Пример:</p>



<pre class="wp-block-preformatted">db.band.find()</pre>



<pre class="wp-block-preformatted">db.band.find().pretty()</pre>



<p>Метод&nbsp;<code>pretty</code>&nbsp;выводит результат в удобном для чтения виде.</p>



<h3 class="wp-block-heading" id="target-12">Метод count()</h3>



<p>Метод&nbsp;<code>count</code>&nbsp;выводит количество документов в коллекции:</p>



<pre class="wp-block-preformatted">db.band.count()</pre>



<h3 class="wp-block-heading" id="target-13">Метод remove()</h3>



<p>Метод&nbsp;<code>remove</code>&nbsp;используется, чтобы удалить документ из коллекции (или всю коллекцию).</p>



<pre class="wp-block-preformatted">db.unicorns.remove({name: "Leto"})</pre>



<h3 class="wp-block-heading" id="target-14">Метод insert()</h3>



<p>Заносим данные в коллекцию&nbsp;<code>band</code>&nbsp;(создаем тем самым коллекцию&nbsp;<code>band</code>, если ее нет):</p>



<pre class="wp-block-preformatted">db.band.insert({name: 'Queen', bid: '3'})</pre>



<p>Добавим составы груп в коллекцию&nbsp;<code>band</code>:</p>



<pre class="wp-block-preformatted">db.band.update({bid: '1'}, {$set: {members: [
    {name: "Jimmy Page", id: "1"},
    {name: "robert Plant", id: "2"},
    {name: "John Bonham", id: "3"},
    {name: "John Paul Jones", id: "4"}]}})

db.band.update({bid: '2'}, {$set: {members: [
    {name: "Syd Barret", id: "5"},
    {name: "Roger Waters", id: "6"},
    {name: "Nick Mason", id: "7"},
    {name: "Richard Wright", id: "8"},
    {name: "David Gilmour", id: "9"}]}})</pre>


</br>



<p>Мы можем добавлять данные, не декларируя их предварительно: свойство&nbsp;<code>members</code>. Отсутствует схема: легко добавили массив объектов.</p>



<p>Мы не обязаны создавать коллекции явно. Мы просто можем&nbsp;<strong>вставить документ в новую коллекцию</strong>. Чтобы это сделать, используйте команду&nbsp;<code>insert</code>, передав ей вставляемый документ:</p>



<pre class="wp-block-preformatted">db.unicorns.insert({name: 'Aurora', gender: 'f', weight: 450})</pre>



<h2 class="wp-block-heading" id="target-15">Модификация данных</h2>



<h3 class="wp-block-heading" id="target-16">Оператор $set</h3>



<p>Оператор&nbsp;<code>$set</code>&nbsp;заставляет команду&nbsp;<code>update</code>&nbsp;модифицировать лишь те ключи, которые ему переданы (см. пример выше).</p>



<h3 class="wp-block-heading" id="target-17">Оператор $unset</h3>



<p>Оператор&nbsp;<code>$unset</code>&nbsp;удаляет указанный ключ</p>



<pre class="wp-block-preformatted">db.collection.update({id: 2}, {$unset: {myKey: 1}});
db.example.update({}, {$unset: {words:1}}, false, true);</pre>



<h3 class="wp-block-heading" id="target-18">Оператор $inc</h3>



<p>Оператор&nbsp;<code>$inc</code>&nbsp;увеличивает значение поля на указанную величину</p>



<pre class="wp-block-preformatted">db.collection.update({id: 2}, {$inc: {myCounter: 111}});
db.collection.update( {"players.playerName":"Joe"}, { $inc : { "players.$.playerScore" : 1 } }</pre>



<h3 class="wp-block-heading" id="target-19">Оператор $rename</h3>



<p>Оператор&nbsp;<code>$rename</code>&nbsp;позволяет переименовать поля</p>



<pre class="wp-block-preformatted">db.collection.update({id: 2}, {$rename: {"old_name": "new_name"}});
db.band.update({bid: "1"},{$rename:{"members":"members_new"}});</pre>



<h2 class="wp-block-heading" id="target-20">Индексы</h2>



<p>Индексация поддерживает эффективное выполнение запросов. Без индексов MongoDB необходимо сканировать каждый документ коллекции для выбора тех документов, которые соответствуют запросу. Данный процесс крайне неэффективен и требует обработки большого количества данных.</p>



<p><strong>Индексы MongoDB</strong>&nbsp;– это специальные структуры данных, которые хранят небольшие части данных в форме, которая легко распознаётся. Они хранят значение определённого поля или набора полей, упорядоченных по значению поля, указанному в индексе.</p>



<p>Построим индекс по ключу&nbsp;<code>bid</code>:</p>



<pre class="wp-block-preformatted">db.band.ensureIndex({bid: 1}) // deprecated
 db.users.createIndex({"name" : 1}) // actual</pre>



<p><code>ensureIndex</code>&nbsp;устарел, начиная с версии 3.0, в данный момент является псевдонимом для&nbsp;<code>db.collection.createIndex()</code>.</p>



<p>Полезные материалы: на <a href="http://metanit.com/nosql/mongodb/2.12.php" target="_blank" rel="noreferrer noopener nofollow">metanit.com/nosql/mongodb</a> &#8212; Работа с индексами,<br><a href="https://proselyte.net/tutorials/mongodb/indexing/" target="_blank" rel="noreferrer noopener nofollow">proselyte.net/tutorials/mongodb/indexing</a> &#8212; Индексация в MongoDB</p>


</br>



<h2 class="wp-block-heading" id="target-21">Схемы и модели</h2>



<p>Схемы определяют структуру документов внутри коллекции, а модели используются для создания копий данных, хранящихся в документах.</p>



<h2 class="wp-block-heading" id="target-22">Основы MongoDB</h2>



<ul><li><strong>1</strong>&nbsp;&nbsp;&nbsp; Внутри MongoDB может быть ноль или более баз данных.</li><li><strong>2</strong>&nbsp;&nbsp;&nbsp; База данных может иметь ноль или более «<strong>коллекций</strong>» (коллекция практически тоже что и таблица).</li><li><strong>3</strong>&nbsp;&nbsp;&nbsp; Коллекции состоят из нуля или более «<strong>документов</strong>». Опять же, документ можно рассматривать как «<strong>строку</strong>».</li><li><strong>4</strong>&nbsp;&nbsp;&nbsp; Документ состоит из одного или более «<strong>полей</strong>», которые — как можно догадаться — подобны «<strong>колонкам</strong>».</li><li><strong>5</strong>&nbsp;&nbsp;&nbsp; «<strong>Индексы</strong>» в MongoDB почти идентичны таковым в реляционных базах данных.</li><li><strong>6</strong>&nbsp;&nbsp;&nbsp; Важно понимать, что когда мы запрашиваем у MongoDB какие-либо данные, то она возвращает&nbsp;<strong>курсор</strong>, с которыми мы можем делать все что угодно.</li></ul>



<h3 class="wp-block-heading" id="target-23">Отличия MongoDB от реляционных БД</h3>



<p>Основное различие в том, что реляционные базы данных определяют «колонки» на уровне «таблицы», в то время как документ-ориентированные базы данных определяют «поля» (в реляционных &#171;колонки&#187;) на уровне «документа» (в релационных &#171;запись&#187;).</p>



<p>В конечном счёте дело в том, что коллекция не содержит информации о структуре содержащихся в ней данных. Информацию о полях содержит каждый отдельный документ.</p>



<h2 class="wp-block-heading" id="target-24">Селекторы запросов</h2>



<p><strong>Селектор запросов</strong>&nbsp;MongoDB (это JSON-объект) аналогичен предложению&nbsp;<code>where</code>&nbsp;SQL-запроса. Как таковой он используется для поиска, подсчёта, обновления и удаления документов из коллекций.</p>



<p><strong>Селектор</strong>&nbsp;— это JSON-объект, в простейшем случае это может быть даже&nbsp;<code>{}</code>, что означает выборку всех документов (аналогичным образом работает&nbsp;<code>null</code>). Если нам нужно выбрать всех единорогов (англ. «unicorns») женского рода, можно воспользоваться селектором&nbsp;<code>{gender:'f'}</code>.</p>



<p><code>{поле: значение}</code>&nbsp;используется для поиска всех документов, у которых есть &#8216;поле&#8217; и у него есть &#8216;значение&#8217;.</p>



<p><code>{поле1: значение1, поле2: значение2}</code>&nbsp;работает как логическое&nbsp;<code>И</code>.</p>



<h3 class="wp-block-heading" id="target-25">Оператор $lt, $lte, $gt, $gte, $ne</h3>



<p>Специальные операторы&nbsp;<code>$lt</code>,&nbsp;<code>$lte</code>,&nbsp;<code>$gt</code>,&nbsp;<code>$gte</code>&nbsp;и&nbsp;<code>$ne</code>&nbsp;используются для выражения операций «меньше», «меньше или равно», «больше», «больше или равно», и «не равно».</p>



<p>Пример использовани селекторов с командой&nbsp;<code>find</code>&nbsp;(но также селекторы могут быть использованы с&nbsp;<code>remove</code>,&nbsp;<code>count</code>,&nbsp;<code>update</code>):</p>



<p>Например, чтобы получить всех самцов единорога, весящих более 700 фунтов, мы можем написать:</p>



<pre class="wp-block-preformatted">db.unicorns.find({gender: 'm', weight: {$gt: 700}})</pre>



<h3 class="wp-block-heading" id="target-26">Оператор $exists</h3>



<p>Оператор&nbsp;<code>$exists</code>&nbsp;используется для проверки наличия или отсутствия поля, например:</p>



<pre class="wp-block-preformatted">db.unicorns.find({vampires: {$exists: false}})</pre>



<h3 class="wp-block-heading" id="target-27">Оператор $or</h3>



<p>Оператор&nbsp;<code>$or</code>&nbsp;используется как ИЛИ</p>



<pre class="wp-block-preformatted">db.unicorns.find({gender: 'f', $or: [{loves: 'apple'}, {loves: 'orange'}, {weight: {$lt: 500}}]})
//... или любят яблоки, или любят апельсины, или весят менее 500 фунтов</pre>



<p>Отметьте</p>



<pre class="wp-block-preformatted">db.unicorns.insert({name: 'Leia', dob: new Date(2001, 9, 8, 14, 53), loves: ['apple', 'watermelon'],
    weight: 601, gender: 'f', vampires: 33});</pre>



<p>Поле&nbsp;<code>loves</code>&nbsp;это массив. MongoDB поддерживает массивы как объекты первого класса. Самое интересное это та простота, с которой делается выборка по значению массива:&nbsp;<code>{loves: 'watermelon'}</code>&nbsp;вернёт нам все документы, у которых&nbsp;<code>watermelon</code>&nbsp;является одним из значений поля&nbsp;<code>loves</code>.</p>


</br>



<h3 class="wp-block-heading" id="target-28">Оператор $where</h3>



<p>Оператор&nbsp;<code>$where</code>&nbsp;(в след. разделах)</p>



<p>Самый гибкий оператор —&nbsp;<code>$where</code>, позволяющий нам передавать JavaScript для его выполнения на сервере.</p>



<h3 class="wp-block-heading" id="target-29">Оператор ObjectId</h3>



<p><code>ObjectId</code>, сгенерированный MongoDB для поля&nbsp;<code>_id</code>, подставляется в селектор следующим образом:</p>



<pre class="wp-block-preformatted">db.unicorns.find({_id: ObjectId("TheObjectId")})</pre>



<h2 class="wp-block-heading" id="target-30">update</h2>



<p>В простейшей форме,&nbsp;<code>update</code>&nbsp;принимает 2 аргумента: селектор для выборки и то, чем обновить соответствующее поле.&nbsp;<strong>Второй параметр используется для полной замены оригинала</strong>:</p>



<pre class="wp-block-preformatted">db.unicorns.update({name: 'Roooooodles'}, {weight: 590})</pre>



<p>По умолчанию,&nbsp;<code>update</code>&nbsp;обновляет лишь первый найденный документ</p>



<h3 class="wp-block-heading" id="target-31">Модификатор $set</h3>



<p>Модификатор&nbsp;<code>$set</code>&nbsp;обновляет конкретные поля, а не весь документ.</p>



<p>Если вам нужно всего лишь изменить пару полей, лучше всего использовать модификатор&nbsp;<code>$set</code>:</p>



<pre class="wp-block-preformatted">db.unicorns.update({weight: 590},
    {$set: {name: 'Roooooodles', dob: new Date(1979, 7, 18, 18, 44), loves: ['apple'], gender: 'm', vampires: 99}})

db.unicorns.update({name: 'Roooooodles'}, {$set: {weight: 590}})</pre>



<p>Не забывайте использовать модификатор&nbsp;<code>$set</code>, если вам нужно обновить лишь некоторые поля.</p>



<h3 class="wp-block-heading" id="target-32">модификатор $inc</h3>



<p>Модификатор&nbsp;<code>$inc</code>&nbsp;&#8212; увеличить или уменьшить значение поля. Модификатор воздействуют непосредственно на поля, а не на весь документ.</p>



<pre class="wp-block-preformatted">db.unicorns.update({name: 'Pilot'}, {$inc: {vampires: -2}})</pre>


</br>



<h3 class="wp-block-heading" id="target-33">Модификатор $push</h3>



<p>Модификатор&nbsp;<code>$push</code>&nbsp;&#8212; позволяет добавить данные в массив. Модификатор воздействуют непосредственно на поля, а не на весь документ.</p>



<pre class="wp-block-preformatted">db.unicorns.update({name: 'Aurora'}, {$push: {loves: 'sugar'}})</pre>



<h3 class="wp-block-heading" id="target-34">Разрешаем вставку при обновлении (3-й параметр)</h3>



<p>Обновление/вставка: обновляет документ, если он найден, или создаёт новый — если не найден. Чтобы разрешить вставку при обновлении (<strong>если элемент не будет найден</strong>), установите&nbsp;<strong>третий параметр в</strong>&nbsp;<code>true</code>.</p>



<pre class="wp-block-preformatted">db.hits.update({page: 'unicorns'}, {$inc: {hits: 1}});
db.hits.find();</pre>



<p>Вставки и обновления не будет, так как 3-й параметр опущен, а документа с&nbsp;<code>{page: 'unicorns'}</code>&nbsp;отсутствует в коллекции.</p>



<pre class="wp-block-preformatted">db.hits.update({page: 'unicorns'}, {$inc: {hits: 1}}, true);
db.hits.find();</pre>



<p>Поскольку документы с полем&nbsp;<code>page</code>, равным&nbsp;<code>unicorns</code>, не существуют, то будет создан новый документ. Если выполнить это вторично, существующий документ будет обновлён, и поле&nbsp;<code>hits</code>&nbsp;увеличится до 2.</p>



<p>Одновременно создастся коллекция&nbsp;<code>hits</code>, если она отсутствует.</p>



<h3 class="wp-block-heading" id="target-35">Множественные обновления (4-й параметр)</h3>



<p>Чтобы обновить множество документов нужно установить&nbsp;<strong>четвертый параметр</strong>&nbsp;в&nbsp;<code>true</code>:</p>



<pre class="wp-block-preformatted">db.unicorns.update({}, {$set: {vaccinated: true }}, false, true);</pre>



<p>Этим мы обновили все поля добавив везде поле&nbsp;<code>vaccinated</code>&nbsp;со значением&nbsp;<code>true</code></p>



<h2 class="wp-block-heading" id="target-36">find (курсор)</h2>



<p><strong>Курсор базы данных</strong>&nbsp;&#8212; это объект БД, который позволяет приложениям работать с записями &#171;по-одной&#187;, а не с множеством сразу. То есть курсор (как мы помним это объект), который позволяет передвигаться по выборке (назад на одно, вперед на одну, в конец/начало) при помощи своих методов.</p>



<p>Как уже упоминалось, результатом&nbsp;<code>find</code>&nbsp;является курсор.&nbsp;<strong>Второй необязательный параметр</strong>&nbsp;у&nbsp;<code>find</code>&nbsp;это список полей, которые мы хотим получить.</p>



<pre class="wp-block-preformatted">db.unicorns.find(null, {name: 1});
cursor = db.unicorns.find(null, {name: 1});</pre>



<p><code>_id</code>&nbsp;по умолчанию возвращается всегда. Но мы можем исключить<code>&nbsp;_id</code>&nbsp;следующим образом:&nbsp;<code>{name:1, _id: 0}</code>.</p>



<p>Получаем все поля, кроме поля&nbsp;<code>name</code>:</p>



<pre class="wp-block-preformatted">db.unicorns.find({}, {name: 0})</pre>



<p>Как уже упоминалось, результатом&nbsp;<code>find</code>&nbsp;является&nbsp;<strong>курсор</strong>. Поэтому мы&nbsp;<strong>можем присоединить к нему ряд методов</strong>:</p>



<h3 class="wp-block-heading" id="target-37">Сортировка (метод sort)</h3>



<p>Синтаксис метода&nbsp;<code>sort:</code>&nbsp;мы указываем поля, по которым надо сортировать, используя&nbsp;<code>1</code>&nbsp;для сортировки по возрастанию и&nbsp;<code>-1</code>&nbsp;для сортировки по убыванию. Например:</p>



<pre class="wp-block-preformatted">db.unicorns.find().sort({weight: -1})
// по убыванию 999,998,997 ...</pre>



<pre class="wp-block-preformatted">db.unicorns.find({}, {name: true}).sort({name: -1})</pre>



<p>Но для сортировки большого объема данных в MongoDB необходимо использовать&nbsp;<strong>индексы</strong>.</p>


</br>



<h3 class="wp-block-heading" id="target-38">Метод limit()</h3>



<pre class="wp-block-preformatted">db.unicorns.find({}, {name: true}).sort({name: -1}).limit(3)</pre>



<h3 class="wp-block-heading" id="target-39">Метод skip()</h3>



<p>Метод&nbsp;<code>skip()</code>&nbsp;позволяет пропустить определенное количество записей.</p>



<pre class="wp-block-preformatted">db.unicorns.find({}, {name: true}).sort({name: -1}).limit(3).skip(1)</pre>



<p>Обратите внимание как мы соединяем методы в&nbsp;<strong>цепочки</strong>.</p>



<h2 class="wp-block-heading" id="target-40">Моделирование данных</h2>



<p>MongoDB не поддерживает&nbsp;<code>JOIN</code>. По существу мы должны делать второй запрос, чтобы найти связанные данные.</p>



<p>p.s: Для создания нового&nbsp;<code>ObjectID</code>&nbsp;используется следующий код:&nbsp;<code>NewObjectId = ObjectId()</code></p>



<h3 class="wp-block-heading" id="target-41">Моделируем &#8216;один-ко-многим&#8217; или &#8216;многие-ко-многим&#8217;</h3>



<p>Когда требуется смоделировать отношения «<strong>один-ко-многим</strong>» или «<strong>многие-ко-многим</strong>» можно использовать массивы ( в MongoDB массивы это объекты первого класса).</p>



<pre class="wp-block-preformatted">db.employees.insert({_id: ObjectId("4d85c7039ab0fd70a117d733"), name: 'Siona',
manager: [ObjectId("4d85c7039ab0fd70a117d730"), ObjectId("4d85c7039ab0fd70a117d732")] })</pre>



<p>При этом следующий&nbsp;<code>find</code>&nbsp;сработает:</p>



<pre class="wp-block-preformatted">db.employees.find({manager: ObjectId("4d85c7039ab0fd70a117d730")})</pre>



<p>Массивы значений намного удобнее в использовании, нежели таблицы связи «многие-ко-многим»</p>



<h2 class="wp-block-heading" id="target-42">Вложенные документы</h2>



<p>MongoDB поддерживает вложенные документы:</p>



<pre class="wp-block-preformatted">db.employees.insert({_id: ObjectId("4d85c7039ab0fd70a117d734"),
name: 'Ghanima', family: {mother: 'Chani', father: 'Paul', brother: ObjectId("4d85c7039ab0fd70a117d730")}})</pre>



<p>Вложенные документы можно запрашивать с помощью точечной нотации:</p>



<pre class="wp-block-preformatted">db.employees.find({'family.mother': 'Chani'})</pre>


</br>



<h2 class="wp-block-heading" id="target-43">Денормализация</h2>



<p>Традиционный путь ассоциировать пользователя с его постом — это колонка&nbsp;<code>userid</code>&nbsp;в таблице&nbsp;<code>posts</code>. С такой моделью нельзя отобразить список постов без дополнительного извлечения данных (JOIN) из таблицы пользователей. Возможное решение — хранить имя пользователя (<code>name</code>) вместе с&nbsp;<code>userid</code>&nbsp;для каждого поста.</p>



<h2 class="wp-block-heading" id="target-44">Команды (выжимка)</h2>



<pre class="wp-block-preformatted">db.version()
//показывает номер версии сервера</pre>



<pre class="wp-block-preformatted">db.getCollectionNames()
//получить список коллекций внутри нашей БД</pre>



<pre class="wp-block-preformatted">db.unicorns.find()
//вернет список документов (записей)</pre>



<pre class="wp-block-preformatted">db.unicorns.remove()
//поскольку мы не передали селектора, произойдёт удаление всех документов</pre>



<pre class="wp-block-preformatted">//Например, чтобы получить всех самцов единорога, весящих более 700 фунтов, мы можем написать:
db.unicorns.find({gender: 'm', weight: {$gt: 700}})</pre>



<pre class="wp-block-preformatted">//Оператор $exists используется для проверки наличия или отсутствия поля, например:
db.unicorns.find({vampires: {$exists: false}})</pre>



<pre class="wp-block-preformatted">//Оператор $or используется как ИЛИ
db.unicorns.find({gender: 'f', $or: [{loves: 'apple'}, {loves: 'orange'}, {weight: {$lt: 500}}]})</pre>



<pre class="wp-block-preformatted">//update принимает 2 аргумента: селектор (where) для выборки и то, чем обновить соответствующее поле.
//Второй параметр используется для полной замены оригинала
db.unicorns.update({name: 'Roooooodles'}, {weight: 590})</pre>



<pre class="wp-block-preformatted">//Модификатор $set обновляет конкретные поля, а не весь документ
db.unicorns.update({name: 'Roooooodles'}, {$set: {weight: 590}})</pre>



<pre class="wp-block-preformatted">//модификатор $inc - увеличить или уменьшить значение поля
db.unicorns.update({name: 'Pilot'}, {$inc: {vampires: -2}})</pre>



<pre class="wp-block-preformatted">//модификатор $push - позволяет добавить данные в массив
db.unicorns.update({name: 'Aurora'}, {$push: {loves: 'sugar'}})</pre>



<pre class="wp-block-preformatted">//Обновление/вставка обновляет документ, если он найден, или создаёт новый — если не найден.
//Чтобы разрешить вставку при обновлении, установите третий параметр в true (ниже мы создаем коллекцию hits, если ее нет)
db.hits.update({page: 'unicorns'}, {$inc: {hits: 1}}, true);</pre>



<pre class="wp-block-preformatted">//если установить 4-й параметр в true, то обновятся все документы
db.unicorns.update({}, {$set: {vaccinated: true }}, false, true);</pre>



<pre class="wp-block-preformatted">//Второй необязательный параметр у find указывает на список полей, которые мы хотим получить.
db.unicorns.find(null, { name:true })</pre>


</br>



<pre class="wp-block-preformatted">//получаем все поля, кроме поля name:
db.unicorns.find({}, {name: 0})</pre>



<pre class="wp-block-preformatted">//сортировка по убыванию
db.unicorns.find().sort({weight: -1})</pre>



<pre class="wp-block-preformatted">// сортируем по весу, но получаем 2 и 3 по весу единорога, пропуская 1-го
db.unicorns.find().sort({weight: -1}).limit(2).skip(1)</pre>



<pre class="wp-block-preformatted">//подсчитать кол-во единорогов на счету которых более 60 вампиров
db.unicorns.count({vampires: {$gt: 50}})</pre>



<pre class="wp-block-preformatted">//Когда требуется смоделировать отношения «один-ко-многим» или «многие-ко-многим» можно использовать массивы
db.employees.insert({_id: ObjectId("4d85c7039ab0fd70a117d733"), name: 'Siona',
manager: [ObjectId("4d85c7039ab0fd70a117d730"), ObjectId("4d85c7039ab0fd70a117d732")] })</pre>



<pre class="wp-block-preformatted">// Ищем значение в массиве manager
db.employees.find({manager: ObjectId("4d85c7039ab0fd70a117d730")})</pre>



<pre class="wp-block-preformatted">//MongoDB поддерживает вложенные документы:
db.employees.insert({_id: ObjectId("4d85c7039ab0fd70a117d734"), name: 'Ghanima',
family: {mother: 'Chani', father: 'Paul', brother: ObjectId("4d85c7039ab0fd70a117d730")}})</pre>



<pre class="wp-block-preformatted">//Вложенные документы можно запрашивать с помощью точечной нотации:
db.employees.find({'family.mother': 'Chani'})</pre>



<pre class="wp-block-preformatted">// версия &gt; 3.2
db.employees.updateOne(…);
db.employees.updateMany (…);</pre>



<p><a href="https://docs.mongodb.com/manual/reference/operator/update/" target="_blank" rel="noreferrer noopener nofollow">operator update</a> (документация)</p>



<pre class="wp-block-preformatted">// другие команды для работы с коллекциями:

insertOne
insertMany

    // получаем кол-во документов в коллекции
db.cats.count()

// модификаторы:
    // длина массива равен 3
{$size: 3}
    // выборка по типу
{$type: number}
</pre>



<h2 class="wp-block-heading" id="target-45">Подключение MongoDB в Node.js</h2>



<pre class="wp-block-preformatted">// Retrieve
var MongoClient = require('mongodb').MongoClient;

// Connect to the db
MongoClient.connect("mongodb://localhost:27017/exampleDb", function(err, db) {
  if(!err) {
    console.log("We are connected");
  }
});</pre>



<p><a href="https://mongodb.github.io/node-mongodb-native/api-articles/nodekoarticle1.html#getting-that-connection-to-the-database" target="_blank" rel="noreferrer noopener nofollow">соединение с БД mongo DB</a></p>


</br>



<h2 class="wp-block-heading" id="target-46">Mongoose</h2>



<p>ODM – Object-Document Mapper (объектно-документное отображение). У MongoDB нет жесткой структуры, а вот&nbsp;<strong>Mongoose</strong>&nbsp;позволяет нам ввести понятие схемы.</p>



<h3 class="wp-block-heading" id="target-47">Установка и подключение (работаем через mongoose)</h3>



<pre class="wp-block-preformatted">//install
$ npm install mongoose</pre>



<pre class="wp-block-preformatted">//use
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');

var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function() {
    // connect
});</pre>



<h3 class="wp-block-heading" id="target-48">Установка и подключение (работаем через нативный клиент &#8212; MongoClient)</h3>



<pre class="wp-block-preformatted">var MongoClient = require("mongodb").MongoClient;
mongoClient.connect("mongodb://localhost:27017/test", function(err, db){
    if(err){
        return console.log(err);
    }

    // работаем с БД

    db.close();
});</pre>



<h3 class="wp-block-heading" id="target-49">Схема</h3>



<p><strong>Схема</strong> в Mongoose определяет метаданные модели &#8212; ее свойства, типы данных и ряд другой информации. <a href="http://mongoosejs.com/docs/guide.html" target="_blank" rel="noreferrer noopener nofollow">mongoosejs.com/docs/guide.html</a></p>



<pre class="wp-block-preformatted">var mongoose = require('mongoose');
var Schema = mongoose.Schema;


// внутри перечисляем наши поля
var blogSchema = new Schema({
    title:  String,
    author: String,
    body:   String,
    comments: [{ body: String, date: Date }],
    date: {
        type: Date,
        default: Date.now,
        required: [true, 'Укажите дату']
    },
    hidden: Boolean,
    meta: {
        votes: Number,
        favs:  Number
    }
});


var Blog = mongoose.model('Blog', blogSchema);
// ready to go!</pre>



<h3 class="wp-block-heading" id="target-50">Типы схем</h3>



<p><a href="http://mongoosejs.com/docs/schematypes.html" target="_blank" rel="noreferrer noopener nofollow">mongoosejs.com/docs/schematypes.html</a></p>



<ul><li><a href="http://mongoosejs.com/docs/api.html#schema-string-js" target="_blank" rel="noreferrer noopener nofollow">String</a></li><li><a href="http://mongoosejs.com/docs/api.html#schema-number-js" target="_blank" rel="noreferrer noopener nofollow">Number</a></li><li><a href="http://mongoosejs.com/docs/api.html#schema-date-js" target="_blank" rel="noreferrer noopener nofollow">Date</a></li><li><a href="http://mongoosejs.com/docs/api.html#schema-buffer-js" target="_blank" rel="noreferrer noopener nofollow">Buffer</a> (например, для изображений)</li><li>Boolean</li><li>Mixed (любой тип данных)</li><li><a href="http://mongoosejs.com/docs/api.html#schema-objectid-js" target="_blank" rel="noreferrer noopener nofollow">Objectid</a></li><li>Array</li></ul>



<h3 class="wp-block-heading" id="target-51">Модель</h3>



<p><strong>Модели</strong> (<a href="http://mongoosejs.com/docs/models.html" target="_blank" rel="noreferrer noopener nofollow">http://mongoosejs.com/docs/models.html</a>) &#8212; это конструкторы, составленные из определения нашей схемы. Экземпляры модели представляют собой документы, которые могут быть сохранены и извлечены из нашей БД.</p>



<pre class="wp-block-preformatted">var schema = new mongoose.Schema({ name: 'string', size: 'string' });
var Tank = mongoose.model('Tank', schema);</pre>



<p>Первый параметр в методе&nbsp;<code>mongoose.model</code>&nbsp;указывает на название модели, а второй параметр &#8212; схема.</p>



<h3 class="wp-block-heading" id="target-52">Сохраняем объект в БД</h3>



<p>Кроме метода&nbsp;<code>save()</code>&nbsp;также можно использовать метод&nbsp;<code>Person.create()</code>&nbsp;(см. код ниже). Первый параметр метода &#8212; сохраняемый объект.</p>


</br>



<pre class="wp-block-preformatted">var Person = mongoose.model('Person', yourSchema);

var subject = new Person({name: 'John'});

subject.save(function(err) {
    if (err) return handkeError(err);
})

// или

Person.create({name: 'John'}, function(err, subject) {
    if (err) return handkeError(err);
})</pre>



<h3 class="wp-block-heading" id="target-53">Поиск (find, findById, findOne)</h3>



<p><a href="http://mongoosejs.com/docs/queries.html" target="_blank" rel="noreferrer noopener nofollow">http://mongoosejs.com/docs/queries.html</a></p>



<p>Методы Для получения данных:</p>



<p><code>find</code>&nbsp;&#8212; возвращает все объекты, которые соответствуют условию фильтрации.&nbsp;<code>find()</code>&nbsp;в качестве первого параметра принимает условие фильтрации; второй параметр метода&nbsp;<code>find()</code>&nbsp;&#8212; функция обратного вызова, в которую передаются полученные из БД документы. Если в качестве условия фильтрации передаются пустые фигурные скобки (<code>{}</code>), то возвращаются все объекты.</p>



<p><code>findById</code>&nbsp;&#8212; возвращает один объект по значению поля&nbsp;<code>_id</code>. Метод возвращает документ с определенным идентификатором.</p>



<p><code>findOne</code>&nbsp;&#8212; возвращает один объект, который соответствует критерию фильтрации. В отличие от метода&nbsp;<code>find</code>, метод&nbsp;<code>findOne()</code>&nbsp;возвращает один объект.</p>



<pre class="wp-block-preformatted">var Person = mongoose.model('Person', yourSchema);

//  { 'name.last': 'Ghost' }       - условие
//  'name occupation'              - выбираем нужные поля
//  function (err, person) { ...   - обрабатываем данные в callback'е

// find each person with a last name matching 'Ghost', selecting the `name` and `occupation` fields
Person.findOne({ 'name.last': 'Ghost' }, 'name occupation', function (err, person) {
  if (err) return handleError(err);
  console.log('%s %s is a %s.', person.name.first, person.name.last, person.occupation)
  // Space Ghost is a talk show host.
})

// находим все
Person.find({ 'name.last': 'Ghost' }, 'name occupation', function (err, docs) {})
// альтернатива callback

Person.find({ 'name.last': 'Ghost' }, 'name occupation').exec(function (err, docs) {})</pre>



<h3 class="wp-block-heading" id="target-54">Редактирование (update, findByIdAndUpdate)</h3>



<p>Каждая модель имеет метод&nbsp;<code>update()</code>, который позволяет обновить документы в БД.&nbsp;<strong>Первый параметр</strong>&nbsp;метода — условие фильтрации.&nbsp;<strong>Второй параметр</strong>&nbsp;описывает, что и как надо изменить. В функцию обратного вызова передается результат операции.</p>



<p>Нередко для обновления используется фильтрация по&nbsp;<code>_id</code>. И на этот случай мы можем использовать метод&nbsp;<code>findByIdAndUpdate()</code>.</p>



<p>Первый параметр метода&nbsp;<code>findByIdAndUpdate()</code>&nbsp;&#8212; значения для поля&nbsp;<code>_id</code>&nbsp;у обновляемого документа, а второй — набор новых значений для полей объекта. В функцию обратного вызова передается обновленный документ.</p>



<pre class="wp-block-preformatted">// меняем Karl на Johny
var query = {name: 'Karl'};

Model.update(query, {name: 'Johny'}, options, callback); // все заменит на  {name: 'Johny'}

Model.update(query, {$set: {name: 'Johny'}}, options, callback); // меняем только поле name

Person.findOne({name: 'Johny'}, function (err, person) {
    if (err) return handleError(err);
    person.name = 'Johny';
    person.save();
})</pre>



<h3 class="wp-block-heading" id="target-55">Удаление (remove, findOneAndRemove)</h3>



<p>Для удаления применяется метод&nbsp;<code>remove()</code>. В метод&nbsp;<code>remove()</code>&nbsp;передается критерий фильтрации документов на удаление. Объект, который передается в функцию обратного вызова, содержит информацию об операции удаления.</p>



<p>Метод&nbsp;<code>findOneAndRemove()</code>&nbsp;позволяет удалить один документ. В функцию обратного вызова метод&nbsp;<code>findOneAndRemove()</code>&nbsp;передается удаленный документ.</p>



<p>И частная разновидность этого метода &#8212; удаление по полю&nbsp;<code>_id</code>&nbsp;в виде метода&nbsp;<code>findByIdAndRemove()</code>.</p>



<pre class="wp-block-preformatted">// удаление:
Model.remove({name: 'Johny'}, function (err, person) {
    if (err) return handleError(err);
})</pre>



<h3 class="wp-block-heading" id="target-56">Валидация в Mongoose</h3>



<p><code>Mongoose</code>&nbsp;имеет ряд встроенных правил валидации, которые следует указывать в схеме:</p>



<ul><li><code>required</code>&nbsp;&#8212; обязательно наличие значения для свойства.</li><li><code>min и max</code>&nbsp;&#8212; задают минимальное и максимальное значения для числовых данных.</li><li><code>minlength</code>&nbsp;и&nbsp;<code>maxlength</code>&nbsp;&#8212; задают минимальную и максимальную длину для строк.</li><li><code>enum</code>&nbsp;&#8212; строка должна представлять одно из значений в указанном массиве строк.</li><li><code>match</code>&nbsp;&#8212; строка должна соответствовать регулярному выражению.</li></ul>


</br>



<p>Если мы попытаемся добавить некорректные данные в БД, то запрос на добавление вернет ошибку.</p>



<p>Пример:</p>



<pre class="wp-block-preformatted">const personScheme = new Schema({
    name: {
        type: String,
        required: true,
        minlength:3,
        maxlength:15
    },
    age: {
        type: Number,
        required: true,
        min: 1,
        max:125
    }
});</pre>



<h3 class="wp-block-heading" id="target-57">Promise и Mongoose</h3>



<p>С MongoDB можно использовать&nbsp;<code>Promise</code>.</p>



<p>С помощью метода then мы можем получить данные, которые возвратил нам сервер и выполнить обработку результата.</p>



<pre class="wp-block-preformatted">user.save()
.then(function(data){
    console.log("Сохранен объект", data);
    mongoose.disconnect();  // отключение от базы данных
})
.catch(function (err){
    console.log(err);
    mongoose.disconnect();
});</pre>



<h2 class="wp-block-heading" id="target-58">mlab.com</h2>



<p><a href="https://mlab.com/" target="_blank" rel="noreferrer noopener nofollow">mlab.com</a> &#8212; это облачный сервис по предоставлению БД mongoDB.</p>



<ul><li>Создаем БД</li><li>Добавляем пользователя</li><li>И подключаемся в своем приложении:</li></ul>



<pre class="wp-block-preformatted">mongoose
    .connect(`mongodb://user_name:pass@ds147072.mlab.com:47072/name_bd`);</pre>



<h2 class="wp-block-heading" id="target-59">CRUD</h2>



<p><strong>CRUD</strong>&nbsp;— (create, read, update, delete — «создание, чтение,обновление, удаление») &#8212; 4 основные функции, используемые при работе базами данных.</p>



<h2 class="wp-block-heading" id="target-60">Послесловие</h2>



<p>Node.js, а значит и Mongo, применяется для высоконагруженных проектов, там, где необходимо передавать много информации, чаты, игры.</p>
<p>Сообщение <a href="https://clip-clap.ru/it/%d0%b0%d0%b4%d0%bc%d0%b8%d0%bd%d0%b8%d1%81%d1%82%d1%80%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d0%bf%d0%be%d0%b4%d1%80%d0%be%d0%b1%d0%bd%d0%be%d0%b5-%d1%80%d1%83%d0%ba%d0%be%d0%b2%d0%be%d0%b4%d1%81%d1%82%d0%b2%d0%be-%d0%bf%d0%be-mongodb-mongoose/">Подробное руководство по MongoDB, Mongoose</a> появились сначала на <a href="https://clip-clap.ru">Clip-Clap</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://clip-clap.ru/it/%d0%b0%d0%b4%d0%bc%d0%b8%d0%bd%d0%b8%d1%81%d1%82%d1%80%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/%d0%bf%d0%be%d0%b4%d1%80%d0%be%d0%b1%d0%bd%d0%be%d0%b5-%d1%80%d1%83%d0%ba%d0%be%d0%b2%d0%be%d0%b4%d1%81%d1%82%d0%b2%d0%be-%d0%bf%d0%be-mongodb-mongoose/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Настройка планировщика задач cron OpenServer</title>
		<link>https://clip-clap.ru/it/%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%be%d0%b9%d0%ba%d0%b0-%d0%bf%d0%bb%d0%b0%d0%bd%d0%b8%d1%80%d0%be%d0%b2%d1%89%d0%b8%d0%ba%d0%b0-%d0%b7%d0%b0%d0%b4%d0%b0%d1%87-cron-openserver/</link>
					<comments>https://clip-clap.ru/it/%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%be%d0%b9%d0%ba%d0%b0-%d0%bf%d0%bb%d0%b0%d0%bd%d0%b8%d1%80%d0%be%d0%b2%d1%89%d0%b8%d0%ba%d0%b0-%d0%b7%d0%b0%d0%b4%d0%b0%d1%87-cron-openserver/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Sat, 03 Oct 2020 08:09:43 +0000</pubDate>
				<category><![CDATA[CMS]]></category>
		<category><![CDATA[IT]]></category>
		<category><![CDATA[Администрирование]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[Софт и ОС]]></category>
		<category><![CDATA[cron]]></category>
		<guid isPermaLink="false">https://clip-clap.ru/?p=1991</guid>

					<description><![CDATA[<p>Приветствую вас, друзья&#160; Думаю, у всех вас когда-то возникала необходимость в создании скриптов для автоматического выполнения какого-либо действия через определённые</p>
<p>Сообщение <a href="https://clip-clap.ru/it/%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%be%d0%b9%d0%ba%d0%b0-%d0%bf%d0%bb%d0%b0%d0%bd%d0%b8%d1%80%d0%be%d0%b2%d1%89%d0%b8%d0%ba%d0%b0-%d0%b7%d0%b0%d0%b4%d0%b0%d1%87-cron-openserver/">Настройка планировщика задач cron OpenServer</a> появились сначала на <a href="https://clip-clap.ru">Clip-Clap</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Приветствую вас, друзья&nbsp;</p>



<p>Думаю, у всех вас когда-то возникала необходимость в создании скриптов для автоматического выполнения какого-либо действия через определённые промежутки времени.</p>



<p>На уровне операционных систем (<strong>Windows</strong>,&nbsp;<strong>Linux</strong>,&nbsp;<strong>MacOS</strong>) уже существуют программы для решения поставленной задачи. Они называются «<strong>Планировщики задач</strong>«. Данный софт является стандартным и доступным по умолчанию (сразу после установки ОС).</p>



<p>Но сегодня наш разговор будет посвящён не им, а планировщику заданий&nbsp;<strong>cron</strong>, встроенный в&nbsp;<strong>OpenServer</strong>, который является, пожалуй, самой популярной WAMP-платформой (сборка Apache+MySql+PHP для Windows) на сегодняшний день.</p>


</br>



<p>Более подробно об OpenServer вы можете прочитать в статье, посвящённой&nbsp;<a href="https://clip-clap.ru/it/%d1%81%d0%be%d1%84%d1%82-%d0%b8-%d0%be%d1%81/%d0%bf%d1%80%d0%be%d0%b3%d1%80%d0%b0%d0%bc%d0%bc%d1%8b-%d0%b4%d0%bb%d1%8f-%d1%81%d0%be%d0%b7%d0%b4%d0%b0%d0%bd%d0%b8%d1%8f-%d1%81%d0%b0%d0%b9%d1%82%d0%be%d0%b2-%d0%be%d0%b1%d0%b7%d0%be%d1%80-%d0%bf/" target="_blank" rel="noreferrer noopener">программам для создания сайтов</a>.</p>



<p>Если вам лень читать статью и вы любитель видео, предлагаю вашему вниманию ролик, в котором рассказывается об основных возможностях данного софта, а также где можно скачать OpenServer, как его установить и запустить сайт на базе OpenServer за считанные минуты:</p>



<figure class="wp-block-embed-youtube wp-block-embed is-type-video is-provider-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="Open Server - пожалуй, лучший локальный сервер" width="800" height="450" src="https://www.youtube.com/embed/F_E8A_UlsyE?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div></figure>


</br>



<h2 class="wp-block-heading" id="obshhaya-informatsiya-o-planirovshhike-zadanij-cron-openserver">Общая информация о планировщике заданий cron OpenServer</h2>



<p>Если вы являетесь пользователем&nbsp;<strong>UNIX-подобных систем</strong>, то программа «cron» должна быть вам знакома, т.к. это стандартный планировщик задач для данного класса операционных систем.</p>



<p>Первое, что бросается в глаза после общения со стандартным планировщиком задач Windows, – это то, что cron лишён графического интерфейса, и расписания задач нужно вносить в специальный файл crontab в соответствии с внутренним синтаксисом cron.</p>



<p>Скорее всего, это обстоятельство и стало причиной для интеграции данного планировщика задач в OpenServer, т.к. данный шаг лишал разработчиков необходимости выдумывать графический интерфейс.</p>



<p>В плане функционала лично я никаких отличий от стандартного cron Unix не заметил. Повторюсь, что cron OpenServer служит для создания расписаний выполнения различных задач (скриптов, программ и т.д.), как и стандартные планировщики на уровне ОС. Поэтому без него вполне можно обойтись&nbsp;</p>



<p>Единственный его плюс – это возможность использовать планировщик cron на ОС Windows, что может порадовать фанатов Unix-систем, которые вынуждены работать на детище Билла Гейтса и компании Microsoft.</p>



<p>Также cron OpenServer может подойти тем, кому не очень нравится стандартный планировщик задач Windows. Основной причиной может служить бедность настроек времени выполнения, т.к. возможности Task Scheduler (стандартный планировщик Windows) ограничены фиксированным списком, в котором нельзя вводить произвольные значения (несколько раз в час, день, периодичность и т.д.).</p>



<p>Кроме всего прочего, лично я открыл ещё один плюс cron OpenServer, который касается процесса тестирования и отладки скриптов, запускаемых с помощью OpenServer. Это возможность приостановки работы планировщика банальным выключением OpenServer.</p>



<p>Насколько я знаю, в стандартных планировщиках такой возможности нет. Если в UNIX cron задачу ещё можно приостановить, закомментировав её расписание в файле crontab, то в Windows вам придётся попросту удалять задачу и создавать её каждый раз, когда необходимо выполнять снова – всё это крайне неудобно.</p>



<p>Поэтому если вы, как и я, являетесь разработчиком, работающим с ОС Windows, то планировщик задач cron OpenServer будет для вас удобнее стандартного Task Scheduler.</p>



<p>Давайте теперь рассмотрим, как же его настроить и пользоваться.</p>


</br>



<h2 class="wp-block-heading" id="nastrojka-cron-openserver">Настройка cron OpenServer</h2>



<p>Для начала нам необходимо запустить сам OpenServer, для чего запускаем OpenServer.exe из директории, в которую у вас был установлен данный софт. Если вы вдруг забыли, куда его устанавливали, напомню, что по умолчанию OpenServer устанавливается в директорию «C:\OpenServer».</p>



<p>После старта программы в панели инструментов Windows, рядом с часами, появится красный флажок OpenServer, сигнализирующий о том, что программа запущена, но сервер ещё не работает.</p>



<p>Перед запуском веб-сервера нажимаем на флажке любой кнопкой мыши и выбираем пункт «Настройки» для конфигурации планировщика задач cron OpenServer:</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/09/nastrojki-openserver1.jpg" alt="" class="wp-image-1992" width="464" height="527" title="Настройки OpenServer"/></figure></div>



<p>В открывшемся окне находим вкладку с названием «Планировщик заданий», которая выглядит так:</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/09/planirovschik-zadanij-cron-openserver1.jpg" alt="" class="wp-image-1993" width="1011" height="709" title="Планировщик заданий cron OpenServer" srcset="https://clip-clap.ru/wp-content/uploads/2020/09/planirovschik-zadanij-cron-openserver1.jpg 588w, https://clip-clap.ru/wp-content/uploads/2020/09/planirovschik-zadanij-cron-openserver1-300x210.jpg 300w, https://clip-clap.ru/wp-content/uploads/2020/09/planirovschik-zadanij-cron-openserver1-130x90.jpg 130w" sizes="(max-width: 1011px) 100vw, 1011px" /></figure></div>



<p>Как видите, интерфейс весьма незамысловатый. Для добавления новой задачи вам необходимо ввести периодичность запуска задачи, указать путь к скрипту или исполнительному файлу программы и нажать на кнопку «Добавить». После данных действий задача появится в блоке со списком всех запланированных заданий.</p>


</br>



<p>Теперь поговорим о синтаксисе заданий более подробно. Он такой же, как и для cron UNIX, т.е. для указания времени можно использовать цифры и символ «*», который означает «для всех».</p>



<p>Синтаксис cron OpenServer сильно упрощён, т.к. в оригинальном UNIX-планировщике, к примеру, для дней недели и месяцев доступны сокращённые варианты названий на английском. В нашем же случае такая запись приведёт к ошибке. Дни недели, к примеру, нужно будет указывать только цифрами, где 1 – это понедельник, а 7 – воскресенье.</p>



<p>Если поговорить о реальных примерах, то:</p>



<ul><li>запись типа «1 * * * *» будет означать запуск задачи каждую первую минуту часа, т.е. она будет выполняться каждый час;</li><li>запись «*/2 * * * *» будет запускать задачу через каждые две минуты;</li><li>запись «2-4 * * * *» будет соответствовать запуску задачи 3 раза в течении каждого часа во 2,3 и 4 минуту;</li><li>запись «* * 1 * *» будет соответствовать ежемесячному запуску задачи первого числа месяца.</li></ul>



<p>Думаю, что логика понятна. Если вы хотите больше примеров, то можете обратиться за помощью к Википедии, где есть очень неплохая и понятная (что редкость для вики&nbsp;&nbsp;)&nbsp;<a href="http://zipansion.com/3l9Bh" rel="noreferrer noopener" target="_blank">статья</a>&nbsp;на русском языке по данному поводу.</p>



<p>Также хотелось бы посоветовать вам хороший&nbsp;<a href="http://zipansion.com/3l92t" rel="noreferrer noopener" target="_blank">сайт для тренировки</a>&nbsp;написания времени запуска заданий cron, где отображаются все параметры для каждого временного блока, а после ввода данных выдаётся периодичность выполнения (правда, на английском).</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>При пользовании ресурсом не забывайте, что в качестве параметров времени запуска cron OpenServer принимает только числовые значения.</p></blockquote>



<p>С временными параметрами выполнения задач cron OpenServer мы разобрались, теперь поговорим о том, как правильно задавать путь к запускаемому объекту.</p>


</br>



<h2 class="wp-block-heading" id="zapusk-php-skriptov-s-pomoshhyu-planirovshhika-zadach-cron-v-openserver">Запуск php-скриптов с помощью планировщика задач cron в OpenServer</h2>



<p>Объектом может быть как исполнительный (.exe) файл программы и php-скрипт, так и обычные файлы, которые будут открываться в тех программах, для которых они предназначены. ОС определяет последнее по ассоциациям расширений файлов и указанных для них программ, которые хранятся в системном реестре.</p>



<p>Поскольку планировщик задач cron встраивался в OpenServer для разработчиков (равно как сама WAMP-сборка создавалась для них же), то он, в первую очередь, предназначен для запуска программных скриптов.</p>



<p>Вообще идея написания сегодняшней статьи возникла у меня после моего недавнего знакомства с&nbsp;CMS/CMF Magento, в рамках которого мне необходимо было реализовать автоматическую отправку писем на email покупателей порциями через равные промежутки времени.</p>



<p>В самом Magento есть встроенный планировщик cron, задачу для которого я создал в коде сайта. Соответственно, моей задачей было запускать скрипт, отвечающий за автоматический запуск задачи в Magento, на веб-сервере через равные промежутки. Вот такая вот петрушка&nbsp;</p>



<p>Путь к скрипту у меня был известен, но, указав его просто в cron-задаче в OpenServer, он просто открывался как обычный файл с интервалами, которые я указал.</p>



<p>Для того, чтобы произвести его запуск, потребовалось помимо пути к файлу прописать в описании задачи ещё и путь к PHP-интерпретатору, а также файлу конфигурации PHP. В итоге, получилась следующая строка:</p>


</br>



<pre class="wp-block-code"><code>%progdir%\modules\php\%phpdriver%\php-win.exe -c %progdir%\userdata\temp\config\php.ini -q -f %sitedir%\sitename.com\cron.php
</code></pre>



<p>Первая её часть – это путь к интерпретатору PHP, вторая – ссылка на файл конфигурации, а третья – это путь к самому скрипту, который нам необходимо будет запускать.</p>



<p>Соответственно, для запуска своих скриптов, расположенных на OpenServer, вы будете менять лишь третью часть данной задачи – остальные две будут неизменными, т.к.&nbsp;расположение интерпретатора и файла конфигурации во всех версиях продукта одинаково.</p>



<p>Также создатели OpenServer учли ситуацию, когда через cron необходимо будет запустить удалённый скрипт, т.е. доступ к нему будет осуществляться по url.</p>



<p>В данной ситуации необходимо будет воспользоваться встроенной в OpenServer утилитой WGet. Пример такой задачи будет выглядеть следующим образом:</p>



<pre class="wp-block-code"><code>%progdir%\modules\wget\bin\wget.exe -q --no-cache http://sitename.com/cron.php -O %progdir%\userdata\temp\temp.txt</code></pre>



<p>В данном случае файл cron.php будет запрашиваться по протоколу http и ответ будет сохраняться во временный файл temp.txt, чтобы не скапливать мусор.</p>



<p>Таким образом, возможно будет у себя на локальном сервере запускать удалённые скрипты и сохранять результаты их выполнения для дальнейшего анализа.</p>



<p>Возможность заманчивая, но в реальной практике её использование встречается не так уж и часто. Просто имейте ввиду, что данное действие возможно&nbsp;</p>



<p>Возвращаясь к синтаксису задач&nbsp;<em>cron OpenServer</em>, вы могли заметить, что при описании задач допускается использовать предопределённые переменные. Они записываются в формате %переменная% и служат в качестве указателей различных директорий (каталога OpenServer, системного диска компьютера и т.д.)</p>



<p>С полным списком переменных и их значений вы можете познакомиться в&nbsp;<a href="http://zipansion.com/3l9Ou" rel="noreferrer noopener" target="_blank">официальной документации OpenServer</a>.</p>



<p>Итак, на данном этапе вы ввели все необходимые данные для создания задачи для планировщика cron.</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/09/sohranenie-zadach-cron-openserver1.jpg" alt="" class="wp-image-1994" width="981" height="688" title="Сохранение задач cron OpenServer" srcset="https://clip-clap.ru/wp-content/uploads/2020/09/sohranenie-zadach-cron-openserver1.jpg 588w, https://clip-clap.ru/wp-content/uploads/2020/09/sohranenie-zadach-cron-openserver1-300x210.jpg 300w, https://clip-clap.ru/wp-content/uploads/2020/09/sohranenie-zadach-cron-openserver1-130x90.jpg 130w" sizes="(max-width: 981px) 100vw, 981px" /></figure></div>


</br>



<p>Перед сохранением конфигурации планировщика проверьте ещё раз список всех задач на экране настройки cron OpenServer и убедитесь, что объект с указанными вами ранее данными там присутствует.</p>



<p>Теперь нам остаётся нажать на кнопку «Сохранить», чтобы занести введённую нами информацию в файл конфигурации cron. После этого веб-сервер автоматически перезагрузится.</p>



<p>Если веб-сервер на этапе ввода задачи был остановлен, его необходимо будет запустить вручную, после чего все изменения сохраняться и задача начнёт выполняться.</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/09/zapusk-openserver1.jpg" alt="" class="wp-image-1995" width="567" height="571" title="Запуск OpenServer" srcset="https://clip-clap.ru/wp-content/uploads/2020/09/zapusk-openserver1.jpg 279w, https://clip-clap.ru/wp-content/uploads/2020/09/zapusk-openserver1-150x150.jpg 150w" sizes="(max-width: 567px) 100vw, 567px" /></figure></div>



<p>После того, как мы добавили в cron OpenServer задачу, независимо от успешности её выполнения, автоматически начинают регистрироваться лог-файл планировщика.</p>



<p>Для доступа к нему необходимо в меню OpenServer выбрать пункт «Просмотр логов»:</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/09/prosmotr-logov-openserver1.jpg" alt="" class="wp-image-1996" width="562" height="590" title="Просмотр логов OpenServer"/></figure></div>



<p>В открывшемся окне ищем вкладку «Планировщик заданий» и видим там следующее:</p>


</br>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/09/logi-planirovschika-cron-openserver1.jpg" alt="" class="wp-image-1997" width="1085" height="411" title="Логи планировщика cron OpenServer" srcset="https://clip-clap.ru/wp-content/uploads/2020/09/logi-planirovschika-cron-openserver1.jpg 670w, https://clip-clap.ru/wp-content/uploads/2020/09/logi-planirovschika-cron-openserver1-300x113.jpg 300w" sizes="(max-width: 1085px) 100vw, 1085px" /></figure></div>



<p>Как видите, задача запустилась, причём, в путях к указанным файлам вместо внутренних переменных OpenServer появились реальные объекты.</p>



<p>К сожалению, на этом ценность данного лога исчерпывается, т.к., несмотря на различную информацию, информативности в ней нет никакой.</p>



<p>Не зависимо от результатов выполнения задачи в логах постоянно пишется ошибка при чтении taskinfo.txt и выдается «result: 0».</p>



<p>Так что при возникновении каких-либо проблем с запуском задачи под cron OpenServer на логии надежды нет – можете не тратить время на их изучение и не прикреплять их к сообщениям на форумах и блогах&nbsp;</p>



<p>О запуске скриптов и отдельных файлов через определённые промежутки времени с помощью планировщика задач cron, входящего в комплект OpenServer, мы поговорили. Самое время рассмотреть, как можно запускать приложения с его помощью. И возможно ли это?</p>


</br>



<h2 class="wp-block-heading" id="zapusk-prilozhenij-s-pomoshhyu-planirovshhika-zadach-cron-v-openserver">Запуск приложений с помощью планировщика задач cron в OpenServer</h2>



<p>Вообще, если честно, сомневаюсь, что данная возможность предполагалась разработчиками OpenServer, когда они встраивали cron в своё детище&nbsp;</p>



<p>Об этом говорит полное отсутствие какой-либо информации по данному вопросу в официальной документации и Интернете в целом.</p>



<p>Скорее всего, разработчики решили не заморачиваться данным мануалом, т.к. предполагали, что для запуска приложений будут использоваться стандартные планировщики на уровне ОС.</p>



<p>Но, как я и говорил ранее, не все нравятся стандартные утилиты. Cron обходит Windows Task Scheduler уже, как минимум, возможностью задания гибкого расписания запуска задачи в отличие от 5-6 стандартных пунктов типа «ежедневно», «каждый месяц» и т.д.</p>



<p>Мне, в частности, с данной точки зрения cron нравится намного больше. Соответственно, я решил попробовать использовать его для запуска приложений Windows.</p>



<p>На предполагаемый вопрос «Зачем изобретать велосипед?» могу сказать, что это банально вопрос удобства. К примеру, запуск PHP-скриптов можно также произвести через стандартный планировщик, указав путь к установленному предварительно интерпретатору PHP. И зачем тогда нужен cron OpenServer, если не для удобства?&nbsp;</p>



<p>Итак, в большинстве планировщиков для запуска программы, установленной на компьютере, необходимо прописать всего лишь путь к её exe-файлу через интерфейс планировщика для создания новой задачи.</p>



<p>Но в случае с cron OpenServer всё оказалось не так-то просто&nbsp;</p>



<p>Для примера я решил вызывать установленную у меня на ПК известную многим утилиту CCleaner. Что я только не делал для её запуска через cron OpenServer:</p>


</br>



<ol><li>прописывал прямой путь к exe-файлу программы;</li><li>делал ярлык экзешника и размещал его в корне диска (чтобы не было ошибки из-за пробелов в пути и названии);</li><li>заключал путь в кавычки;</li><li>пытался прописывать имя пользователя перед путём к CCleaner.exe.</li></ol>



<p>Но, к сожалению, традиционный запуск программы ни в одном случае из перечисленных не произошёл.</p>



<p>На каком-то этапе, заглянув в диспетчер задач Windows, я обнаружил, что процесс CCleaner.exe запускается, причём многократно, но графической оболочки программы я на экране монитора не наблюдал.</p>



<p>В итоге, после получасовых мучений способ был найден и заключался в создании bat-файла и прописывании в нём полного пути к исполнительному файлу необходимой программы.</p>



<p>Сам файл можно размещать в любой директории компьютера, но для надёжности рекомендуется использовать такой путь и имя для bat-файла, чтобы в них не было пробелов, т.к. данная задача может выполниться некорректно.</p>



<p>Если говорить о конкретном примере, то содержимое моего файла «D:\run.bat» было следующим:</p>



<pre class="wp-block-code"><code>"C:\Program Files\CCleaner\CCleaner.exe"</code></pre>



<p>Причём, путь лучше указывать в кавычках, чтобы не было проблем с пробелами в Program Files и именах программ и их каталогов.</p>



<p>После этого я создал задачу в cron OpenServer привычным способом, после чего увидел запуск программы воочию.</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/09/zapusk-prilozhenij-windows-v-planirovschike-zadach-cron-openserver1.jpg" alt="" class="wp-image-1998" width="900" height="631" title="Запуск приложений Windows в планировщике задач cron OpenServer" srcset="https://clip-clap.ru/wp-content/uploads/2020/09/zapusk-prilozhenij-windows-v-planirovschike-zadach-cron-openserver1.jpg 588w, https://clip-clap.ru/wp-content/uploads/2020/09/zapusk-prilozhenij-windows-v-planirovschike-zadach-cron-openserver1-300x210.jpg 300w, https://clip-clap.ru/wp-content/uploads/2020/09/zapusk-prilozhenij-windows-v-planirovschike-zadach-cron-openserver1-130x90.jpg 130w" sizes="(max-width: 900px) 100vw, 900px" /></figure></div>


</br>



<p>Пожалуй, соглашусь, что способ не самый изящный, но, тем не менее, рабочий. Если вы знаете что-то получше – не поленитесь отписаться в комментариях под статьёй. Думаю, что ваше сообщение будет интересно прочитать не только мне, но и остальным посетителям блога.</p>



<p>Из всего вышесказанного сделаю краткое резюме.</p>



<p>Если при разработке сайтов вы пользуетесь WAMP-платформой OpenServer, то для автоматического запуска скриптов, программ и просто отдельных файлов вы можете иcпользовать встроенный планировщик задач, который основывается на стандартном для UNIX-подобных систем планировщике cron.</p>



<p>При этом&nbsp;<strong>cron</strong>&nbsp;<strong>OpenServer</strong>&nbsp;очень прост в настройке и управлении, что делает его прекрасной альтернативой стандартным решениям.</p>



<p>На этом сегодняшняя статья подходит к концу. Мне было бы приятно услышать ваше мнение в виде комментариев и оценок с помощью звёзд под статьёй. Ну а если вы ещё и поделитесь ею со своими друзьями в соц. сетях – то за это вообще будет полагаться медаль за вклад в развитие проекта&nbsp;</p>



<p><a href="http://eepurl.com/duAlVH" target="_blank" rel="noreferrer noopener">Подписывайтесь на обновления</a>, следите за новостями проекта и вступайте в&nbsp;<a href="https://new.vk.com/cccpblog" target="_blank" rel="noreferrer noopener">сообщества проекта</a>&nbsp;в социальных сетях, помогая себе и другим в создании собственного сайта.</p>



<p>До новых встреч!&nbsp;</p>


</br>
<p>Сообщение <a href="https://clip-clap.ru/it/%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%be%d0%b9%d0%ba%d0%b0-%d0%bf%d0%bb%d0%b0%d0%bd%d0%b8%d1%80%d0%be%d0%b2%d1%89%d0%b8%d0%ba%d0%b0-%d0%b7%d0%b0%d0%b4%d0%b0%d1%87-cron-openserver/">Настройка планировщика задач cron OpenServer</a> появились сначала на <a href="https://clip-clap.ru">Clip-Clap</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://clip-clap.ru/it/%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%be%d0%b9%d0%ba%d0%b0-%d0%bf%d0%bb%d0%b0%d0%bd%d0%b8%d1%80%d0%be%d0%b2%d1%89%d0%b8%d0%ba%d0%b0-%d0%b7%d0%b0%d0%b4%d0%b0%d1%87-cron-openserver/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>MongoDB Подробное руководство</title>
		<link>https://clip-clap.ru/it/%d0%b0%d0%b4%d0%bc%d0%b8%d0%bd%d0%b8%d1%81%d1%82%d1%80%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/mongodb-%d0%bf%d0%be%d0%b4%d1%80%d0%be%d0%b1%d0%bd%d0%be%d0%b5-%d1%80%d1%83%d0%ba%d0%be%d0%b2%d0%be%d0%b4%d1%81%d1%82%d0%b2%d0%be/</link>
					<comments>https://clip-clap.ru/it/%d0%b0%d0%b4%d0%bc%d0%b8%d0%bd%d0%b8%d1%81%d1%82%d1%80%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/mongodb-%d0%bf%d0%be%d0%b4%d1%80%d0%be%d0%b1%d0%bd%d0%be%d0%b5-%d1%80%d1%83%d0%ba%d0%be%d0%b2%d0%be%d0%b4%d1%81%d1%82%d0%b2%d0%be/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Tue, 04 Aug 2020 08:03:46 +0000</pubDate>
				<category><![CDATA[Администрирование]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[Софт и ОС]]></category>
		<guid isPermaLink="false">https://clip-clap.ru/?p=2276</guid>

					<description><![CDATA[<p>MongoDB состоит из: БД, которые состоят из коллекций. Коллекции, в свою очередь, состоят из документов. Каждый документ состоит из полей. www.mongodb.com Обновленная статья MongoDB клиенты</p>
<p>Сообщение <a href="https://clip-clap.ru/it/%d0%b0%d0%b4%d0%bc%d0%b8%d0%bd%d0%b8%d1%81%d1%82%d1%80%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/mongodb-%d0%bf%d0%be%d0%b4%d1%80%d0%be%d0%b1%d0%bd%d0%be%d0%b5-%d1%80%d1%83%d0%ba%d0%be%d0%b2%d0%be%d0%b4%d1%81%d1%82%d0%b2%d0%be/">MongoDB Подробное руководство</a> появились сначала на <a href="https://clip-clap.ru">Clip-Clap</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>MongoDB состоит из: <strong>БД</strong>, которые состоят из <strong>коллекций</strong>. Коллекции, в свою очередь, состоят из <strong>документов</strong>. Каждый документ состоит из <strong>полей</strong>. <a rel="noreferrer noopener nofollow" href="https://www.mongodb.com/" target="_blank">www.mongodb.com</a></p>



<p><a href="https://clip-clap.ru/it/%d1%81%d0%be%d1%84%d1%82-%d0%b8-%d0%be%d1%81/%d0%bf%d0%be%d0%b4%d1%80%d0%be%d0%b1%d0%bd%d0%be%d0%b5-%d1%80%d1%83%d0%ba%d0%be%d0%b2%d0%be%d0%b4%d1%81%d1%82%d0%b2%d0%be-%d0%bf%d0%be-mongodb-mongoose/" target="_blank" rel="noreferrer noopener">Обновленная статья</a></p>



<div class="wp-block-image"><figure class="alignleft size-large is-resized"><img loading="lazy" decoding="async" src="https://clip-clap.ru/wp-content/uploads/2020/10/mdb1.png" alt="" class="wp-image-2277" width="306" height="306" srcset="https://clip-clap.ru/wp-content/uploads/2020/10/mdb1.png 225w, https://clip-clap.ru/wp-content/uploads/2020/10/mdb1-150x150.png 150w" sizes="(max-width: 306px) 100vw, 306px" /></figure></div>



<h2 class="wp-block-heading" id="glava0">MongoDB клиенты</h2>



<ul><li><a href="https://robomongo.org/" target="_blank" rel="noreferrer noopener nofollow">https://robomongo.org/</a></li><li><a href="https://studio3t.com/" target="_blank" rel="noreferrer noopener nofollow">https://studio3t.com/</a></li></ul>



<h2 class="wp-block-heading" id="glava1">Конфиг</h2>



<p>В папке&nbsp;<code>bin</code>&nbsp;создайте файл и назовите его&nbsp;<code>mongodb.config</code>.</p>



<p>Указываем в этом файле путь, где будем хранить БД, например, для windows:&nbsp;<code>dbpath=c:\mongodb\data</code></p>



<h2 class="wp-block-heading" id="glava2">Запуск</h2>



<ul><li><code>mongod</code>&nbsp;— это сервер</li><li><code>mongo</code>&nbsp;— клиентская консоль</li></ul>



<p>Стартуем mongod (сервер):</p>



<pre class="wp-block-code"><code>mongod --config c:\mongodb\bin\mongodb.config
// не забудьте заранее создать папку data</code></pre>



<p>Команда&nbsp;<code>mongo</code>&nbsp;позволяет подключиться к запущенному серверу (стартуем&nbsp;<code>mongo</code>&nbsp;оболочку/shell).</p>



<pre class="wp-block-code"><code>mongo</code></pre>


</br>



<h2 class="wp-block-heading" id="glava3">Работа с БД</h2>



<p>Выводим все БД в mongo:</p>



<pre class="wp-block-code"><code>show dbs</code></pre>



<p>Переходим (и одновременно создаем) к нужной БД</p>



<pre class="wp-block-code"><code>use name_bd</code></pre>



<p>Команды&nbsp;<code>db</code>&nbsp;возвращает имя БД, внутри которой мы сейчас находимся:</p>



<pre class="wp-block-code"><code>db
//test</code></pre>



<h2 class="wp-block-heading" id="glava4">Коллекции</h2>



<p>Показать все коллекции в БД</p>



<pre class="wp-block-code"><code>show collections</code></pre>



<h2 class="wp-block-heading" id="glava5">&nbsp;&nbsp;&nbsp;&nbsp;Метод find()</h2>



<p>Показать весь контент нужной коллекции</p>



<pre class="wp-block-code"><code>db.collection_name.find()</code></pre>



<p>Пример:</p>



<pre class="wp-block-code"><code>db.band.find()</code></pre>



<pre class="wp-block-code"><code>db.band.find().pretty()</code></pre>



<p>Метод&nbsp;<code>pretty</code>&nbsp;выводит результат в удобном для чтения виде.</p>



<h2 class="wp-block-heading" id="glava6">&nbsp;&nbsp;&nbsp;&nbsp;Метод count()</h2>



<p>Метод&nbsp;<code>count</code>&nbsp;выводит количество документов в коллекции:</p>



<pre class="wp-block-code"><code>db.band.count()</code></pre>



<h2 class="wp-block-heading" id="glava7">&nbsp;&nbsp;&nbsp;&nbsp;Метод remove()</h2>



<p>Метод&nbsp;<code>remove</code>&nbsp;используется, чтобы удалить документ из коллекции (или всю коллекцию).</p>



<pre class="wp-block-code"><code>db.unicorns.remove({name: "Leto"})</code></pre>



<h2 class="wp-block-heading" id="glava8">&nbsp;&nbsp;&nbsp;&nbsp;Метод insert()</h2>



<p>Заносим данные в коллекцию&nbsp;<code>band</code>&nbsp;(и, видимо, создаем тем самым коллекцию&nbsp;<code>band</code>, если ее нет):</p>



<pre class="wp-block-code"><code>db.band.insert({name: 'Queen', bid: '3'})</code></pre>



<p>Добавим составы в коллекцию&nbsp;<code>band</code>:</p>



<pre class="wp-block-code"><code>db.band.update({bid: '1'}, {$set: {members: &#091;{name: "Jimmy Page", id: "1"}, {name: "robert Plant", id: "2"}, {name: "John Bonham", id: "3"}, {name: "John Paul Jones", id: "4"}]}})

db.band.update({bid: '2'}, {$set: {members: &#091;{name: "Syd Barret", id: "5"}, {name: "Roger Waters", id: "6"}, {name: "Nick Mason", id: "7"}, {name: "Richard Wright", id: "8"}, {name: "David Gilmour", id: "9"}]}})</code></pre>



<p>Мы можем добавлять данные, не декларируя их предварительно: свойство&nbsp;<code>members</code>. Отсутствует схема: легко добавили массив объектов.</p>


</br>



<h2 class="wp-block-heading" id="glava9">Модификация данных</h2>



<h2 class="wp-block-heading" id="glava10">&nbsp;&nbsp;&nbsp;&nbsp;Оператор $set</h2>



<p>Оператор&nbsp;<code>$set</code>&nbsp;заставляет команду&nbsp;<code>update</code>&nbsp;модифицировать лишь те ключи, которые ему переданы (см. пример выше).</p>



<h2 class="wp-block-heading" id="glava11">&nbsp;&nbsp;&nbsp;&nbsp;Оператор $unset</h2>



<p>Оператор&nbsp;<code>$unset</code>&nbsp;удаляет указанный ключ</p>



<pre class="wp-block-code"><code>db.collection.update({id: 2}, {$unset: {myKey: 1}});
db.example.update({}, {$unset: {words:1}}, false, true);</code></pre>



<h2 class="wp-block-heading" id="glava12">&nbsp;&nbsp;&nbsp;&nbsp;Оператор $inc</h2>



<p>Оператор&nbsp;<code>$inc</code>&nbsp;увеличивает значение поля на указанную величину</p>



<pre class="wp-block-code"><code>db.collection.update({id: 2}, {$inc: {myCounter: 111}});
db.collection.update( {"players.playerName":"Joe"}, { $inc : { "players.$.playerScore" : 1 } }</code></pre>



<h2 class="wp-block-heading" id="glava13">&nbsp;&nbsp;&nbsp;&nbsp;Оператор $rename</h2>



<p>Оператор&nbsp;<code>$rename</code>&nbsp;позволяет переименовать поля</p>



<pre class="wp-block-code"><code>db.collection.update({id: 2}, {$rename: {"old_name": "new_name"}});
db.band.update({bid: "1"},{$rename:{"members":"members_new"}});</code></pre>



<h2 class="wp-block-heading" id="glava14">Индексы</h2>



<p><a href="http://metanit.com/nosql/mongodb/2.12.php" target="_blank" rel="noreferrer noopener nofollow">metanit.com/nosql/mongodb</a>&nbsp;<a href="https://proselyte.net/tutorials/mongodb/indexing/" target="_blank" rel="noreferrer noopener nofollow">proselyte.net/tutorials/mongodb/indexing</a></p>



<p>Индексация поддерживает эффективное выполнение запросов. Без индексов MongoDB необходимо сканировать каждый документ коллекции для выбора тех документов, которые соответствуют запросу. Данный процесс крайне не эффективен и требует обработки большого количества данных.</p>



<p><strong>Индексы</strong>&nbsp;– это специальные структуры данных, которые хранят небольшие части данных в форме, которая легко распознаётся. Они хранят значение определённого поля или набора полей, упорядоченных по значению поля, указанному в индексе.</p>



<p>Построим индекс по ключу&nbsp;<code>bid</code>:</p>



<pre class="wp-block-code"><code>db.band.ensureIndex({bid: 1})</code></pre>



<p><code>ensureIndex</code>&nbsp;устарел, начиная с версии 3.0, в данный момент является псевдонимом для&nbsp;<code>db.collection.createIndex()</code>.</p>



<h2 class="wp-block-heading" id="glava15">Схемы и модели</h2>



<p>Схемы определяют структуру документов внутри коллекции, а модели используются для создания копий данных, хранящихся в документах.</p>



<h2 class="wp-block-heading" id="glava16">I Основы</h2>



<ul><li>1. Внутри MongoDB может быть ноль или более баз данных.</li><li>2. База данных может иметь ноль или более «<strong>коллекций</strong>». (коллекция практически тоже что и таблица)</li><li>3. Коллекции состоят из нуля или более «<strong>документов</strong>». Опять же, документ можно рассматривать как «<strong>строку</strong>».</li><li>4. Документ состоит из одного или более «<strong>полей</strong>», которые — как можно догадаться — подобны «<strong>колонкам</strong>».</li><li>5. «<strong>Индексы</strong>» в MongoDB почти идентичны таковым в реляционных базах данных.</li><li>6. Важно понимать, что когда мы запрашиваем у MongoDB какие-либо данные, то она возвращает курсор, с которыми мы можем делать все что угодно.</li></ul>



<p>Но есть&nbsp;<strong>отличия</strong>:</p>



<p>Основное различие в том, что реляционные базы данных определяют «колонки» на уровне «таблицы», в то время как документ-ориентированные базы данных определяют «поля» (в реляционных &#171;колонки&#187;) на уровне «документа» (в релационных &#171;запись&#187;).</p>



<p>В конечном счёте дело в том, что коллекция не содержит информации о структуре содержащихся в ней данных. Информацию о полях содержит каждый отдельный документ.</p>



<h2 class="wp-block-heading" id="glava17">&nbsp;&nbsp;&nbsp;&nbsp;Метод insert</h2>



<p>Мы не обязаны создавать коллекции явно. Мы просто можем&nbsp;<strong>вставить документ в новую коллекцию</strong>. Чтобы это сделать, используйте команду&nbsp;<code>insert</code>, передав ей вставляемый документ:</p>



<pre class="wp-block-code"><code>db.unicorns.insert({name: 'Aurora', gender: 'f', weight: 450})</code></pre>


</br>



<h2 class="wp-block-heading" id="glava18">Селекторы запросов</h2>



<p>Селектор запросов MongoDB (это JSON-объект) аналогичен предложению&nbsp;<code>where</code>&nbsp;SQL-запроса. Как таковой он используется для поиска, подсчёта, обновления и удаления документов из коллекций.</p>



<p><strong>Селектор</strong>&nbsp;— это JSON-объект, в простейшем случае это может быть даже&nbsp;<code>{}</code>, что означает выборку всех документов (аналогичным образом работает&nbsp;<code>null</code>). Если нам нужно выбрать всех единорогов (англ. «unicorns») женского рода, можно воспользоваться селектором&nbsp;<code>{gender:'f'}</code>.</p>



<p><code>{поле: значение}</code>&nbsp;используется для поиска всех документов, у которых есть &#8216;поле&#8217; и у него есть &#8216;значение&#8217;.</p>



<p><code>{поле1: значение1, поле2: значение2}</code>&nbsp;работает как логическое&nbsp;<code>И</code>.</p>



<h2 class="wp-block-heading" id="glava19">&nbsp;&nbsp;&nbsp;&nbsp;Оператор $lt, $lte, $gt, $gte, $ne</h2>



<p>Специальные операторы&nbsp;<code>$lt</code>,&nbsp;<code>$lte</code>,&nbsp;<code>$gt</code>,&nbsp;<code>$gte</code>&nbsp;и&nbsp;<code>$ne</code>&nbsp;используются для выражения операций «меньше», «меньше или равно», «больше», «больше или равно», и «не равно».</p>



<p>Пример использовани селекторов с командой&nbsp;<code>find</code>&nbsp;(но также селекторы могут быть использованы с&nbsp;<code>remove</code>,&nbsp;<code>count</code>,&nbsp;<code>update</code>):</p>



<p>Например, чтобы получить всех самцов единорога, весящих более 700 фунтов, мы можем написать:</p>



<pre class="wp-block-code"><code>db.unicorns.find({gender: 'm', weight: {$gt: 700}})</code></pre>



<h2 class="wp-block-heading" id="glava20">&nbsp;&nbsp;&nbsp;&nbsp;Оператор $exists</h2>



<p>Оператор&nbsp;<code>$exists</code>&nbsp;используется для проверки наличия или отсутствия поля, например:</p>



<pre class="wp-block-code"><code>db.unicorns.find({vampires: {$exists: false}})</code></pre>



<h2 class="wp-block-heading" id="glava21">&nbsp;&nbsp;&nbsp;&nbsp;Оператор $or</h2>



<p>Оператор&nbsp;<code>$or</code>&nbsp;используется как ИЛИ</p>



<pre class="wp-block-code"><code>db.unicorns.find({gender: 'f', $or: &#091;{loves: 'apple'}, {loves: 'orange'}, {weight: {$lt: 500}}]})
//... или любят яблоки, или любят апельсины, или весят менее 500 фунтов</code></pre>



<p>Отметьте</p>



<pre class="wp-block-code"><code>db.unicorns.insert({name: 'Leia', dob: new Date(2001, 9, 8, 14, 53), loves: &#091;'apple', 'watermelon'], weight: 601, gender: 'f', vampires: 33});</code></pre>



<p>Поле&nbsp;<code>loves</code>&nbsp;это массив. MongoDB поддерживает массивы как объекты первого класса. Самое интересное это та простота, с которой делается выборка по значению массива: {loves: &#8216;watermelon&#8217;} вернёт нам все документы, у которых&nbsp;<code>watermelon</code>&nbsp;является одним из значений поля&nbsp;<code>loves</code>.</p>



<h2 class="wp-block-heading" id="glava22">&nbsp;&nbsp;&nbsp;&nbsp;Оператор $where</h2>



<p>Оператор&nbsp;<code>$where</code>&nbsp;(в след. разделах)</p>



<p>Самый гибкий оператор —&nbsp;<code>$where</code>, позволяющий нам передавать JavaScript для его выполнения на сервере.</p>



<h2 class="wp-block-heading" id="glava23">&nbsp;&nbsp;&nbsp;&nbsp;Оператор ObjectId</h2>



<p>ObjectId, сгенерированный MongoDB для поля _id, подставляется в селектор следующим образом:</p>



<pre class="wp-block-code"><code>db.unicorns.find({_id: ObjectId("TheObjectId")})</code></pre>


</br>



<h2 class="wp-block-heading" id="glava24">II update</h2>



<p>В простейшей форме,&nbsp;<code>update</code>&nbsp;принимает 2 аргумента: селектор для выборки и то, чем обновить соответствующее поле.&nbsp;<strong>Второй параметр используется для полной замены оригинала</strong>:</p>



<pre class="wp-block-code"><code>db.unicorns.update({name: 'Roooooodles'}, {weight: 590})</code></pre>



<p>По умолчанию, update обновляет лишь первый найденный документ</p>



<h2 class="wp-block-heading" id="glava25">&nbsp;&nbsp;&nbsp;&nbsp;Модификатор $set</h2>



<p>Модификатор&nbsp;<code>$set</code>&nbsp;обновляет конкретные поля, а не весь документ.</p>



<p>Если вам нужно всего лишь изменить пару полей, лучше всего использовать модификатор&nbsp;<code>$set</code>:</p>



<pre class="wp-block-code"><code>db.unicorns.update({weight: 590}, {$set: {name: 'Roooooodles', dob: new Date(1979, 7, 18, 18, 44), loves: &#091;'apple'], gender: 'm', vampires: 99}})
db.unicorns.update({name: 'Roooooodles'}, {$set: {weight: 590}})</code></pre>



<p>Другие модификаторы, которые действуют непосредственно на поля, а не на весь документ:</p>



<h2 class="wp-block-heading" id="glava26">&nbsp;&nbsp;&nbsp;&nbsp;модификатор $inc</h2>



<p>модификатор&nbsp;<code>$inc</code>&nbsp;&#8212; увеличить или уменьшить значение поля.</p>



<pre class="wp-block-code"><code>db.unicorns.update({name: 'Pilot'}, {$inc: {vampires: -2}})</code></pre>



<p>модификатор&nbsp;<code>$push</code>&nbsp;&#8212; позволяет добавить данные в массив</p>



<pre class="wp-block-code"><code>db.unicorns.update({name: 'Aurora'}, {$push: {loves: 'sugar'}})</code></pre>



<h2 class="wp-block-heading" id="glava27">Обновление/вставка (при отсутствии элемента)</h2>



<p>Не забывайте использовать модификатор&nbsp;<code>$set</code>, если вам нужно обновить лишь некоторые поля.</p>



<h2 class="wp-block-heading" id="glava28">&nbsp;&nbsp;&nbsp;&nbsp;Разрешаем вставку при обновлении (3-й параметр)</h2>



<p>Чтобы разрешить вставку при обновлении (<strong>если элемент не будет найден</strong>), установите&nbsp;<strong>третий параметр в</strong>&nbsp;<code>true</code>.</p>



<pre class="wp-block-code"><code>db.hits.update({page: 'unicorns'}, {$inc: {hits: 1}});
db.hits.find();</code></pre>



<p>Вставки и обновления не будет, так как 3-й параметр опущен, а документа с&nbsp;<code>{page: 'unicorns'}</code>&nbsp;отсутствует в коллекции.</p>



<pre class="wp-block-code"><code>db.hits.update({page: 'unicorns'}, {$inc: {hits: 1}}, true);
db.hits.find();</code></pre>



<p>Поскольку документы с полем&nbsp;<code>page</code>, равным&nbsp;<code>unicorns</code>, не существуют, то будет создан новый документ. Если выполнить это вторично, существующий документ будет обновлён, и поле&nbsp;<code>hits</code>&nbsp;увеличится до 2.</p>



<p>Одновременно создастся коллекция&nbsp;<code>hits</code>, если она отсутствует.</p>



<h2 class="wp-block-heading" id="glava29">&nbsp;&nbsp;&nbsp;&nbsp;Множественные обновления (4-й параметр)</h2>



<p>Чтобы обновить множество документов нужно установить&nbsp;<strong>четвертый параметр</strong>&nbsp;в&nbsp;<code>true</code>:</p>



<pre class="wp-block-code"><code>db.unicorns.update({}, {$set: {vaccinated: true }}, false, true);</code></pre>



<p>Этим мы обновили все поля добавив везде поле&nbsp;<code>vaccinated</code>&nbsp;со значением&nbsp;<code>true</code></p>



<h2 class="wp-block-heading" id="glava30">III Команда find (курсор)</h2>



<p><strong>Курсор базы данных</strong>&nbsp;&#8212; это объект БД, который позволяет приложениям работать с записями &#171;по-одной&#187;, а не с множеством сразу. То есть курсор (как мы помним это объект), который позволяет передвигаться по выборке (назад на одно, вперед на одну, в конец/начало) при помощи своих методов.</p>



<p>Как уже упоминалось, результатом&nbsp;<code>find</code>&nbsp;является курсор. Второй необязательный параметр у&nbsp;<code>find</code>&nbsp;это список полей, которые мы хотим получить.</p>



<pre class="wp-block-code"><code>db.unicorns.find(null, {name: 1});
cursor = db.unicorns.find(null, {name: 1});</code></pre>



<p><code>_id</code>&nbsp;по умолчанию возвращается всегда. Но мы можем исключить<code>&nbsp;_id</code>&nbsp;следующим образом:&nbsp;<code>{name:1, _id: 0}</code>.</p>



<p>Получаем все поля, кроме поля&nbsp;<code>name</code>:</p>



<pre class="wp-block-code"><code>db.unicorns.find({}, {name: 0})</code></pre>



<p>Как уже упоминалось, результатом&nbsp;<code>find</code>&nbsp;является&nbsp;<strong>курсор</strong>. Поэтому мы&nbsp;<strong>можем присоединить к нему ряд методов</strong>:</p>



<h2 class="wp-block-heading" id="glava31">&nbsp;&nbsp;&nbsp;&nbsp;Сортировка (метод sort)</h2>



<p>Синтаксис метода&nbsp;<code>sort:</code>&nbsp;мы указываем поля, по которым надо сортировать, используя&nbsp;<code>1</code>&nbsp;для сортировки по возрастанию и&nbsp;<code>-1</code>&nbsp;для сортировки по убыванию. Например:</p>



<pre class="wp-block-code"><code>db.unicorns.find().sort({weight: -1})
// по убыванию 999,998,997 ...</code></pre>



<pre class="wp-block-code"><code>db.unicorns.find({}, {name: true}).sort({name: -1})</code></pre>



<p>Но для сортировки большого объема данных в Mongo необходимо использовать&nbsp;<strong>индексы</strong>.</p>


</br>



<h2 class="wp-block-heading" id="glava32">&nbsp;&nbsp;&nbsp;&nbsp;Метод limit()</h2>



<pre class="wp-block-code"><code>db.unicorns.find({}, {name: true}).sort({name: -1}).limit(3)</code></pre>



<h2 class="wp-block-heading" id="glava33">&nbsp;&nbsp;&nbsp;&nbsp;Метод skip()</h2>



<p>Метод&nbsp;<code>skip()</code>&nbsp;позволяет пропустить определенное количество записей.</p>



<pre class="wp-block-code"><code>db.unicorns.find({}, {name: true}).sort({name: -1}).limit(3).skip(1)</code></pre>



<p>Обратите внимание как мы соединяем методы в&nbsp;<strong>цепочки</strong>.</p>



<h2 class="wp-block-heading" id="glava34">Моделирование данных</h2>



<p>MongoDB не поддерживает&nbsp;<code>JOIN</code>. По существу мы должны делать второй запрос, чтобы найти связанные данные.</p>



<p>p.s: Для создания нового&nbsp;<code>ObjectID</code>&nbsp;используется следующий код:&nbsp;<code>NewObjectId = ObjectId()</code></p>



<p>Когда требуется смоделировать отношения «<strong>один-ко-многим</strong>» или «<strong>многие-ко-многим</strong>» можно использовать массивы ( в MongoDB массивы это объекты первого класса).</p>



<pre class="wp-block-code"><code>db.employees.insert({_id: ObjectId("4d85c7039ab0fd70a117d733"), name: 'Siona', manager: &#091;ObjectId("4d85c7039ab0fd70a117d730"), ObjectId("4d85c7039ab0fd70a117d732")] })</code></pre>



<p>При этом данный&nbsp;<code>find</code>&nbsp;сработает:</p>



<pre class="wp-block-code"><code>db.employees.find({manager: ObjectId("4d85c7039ab0fd70a117d730")})</code></pre>



<p>Массивы значений намного удобнее в использовании, нежели таблицы связи «многие-ко-многим»</p>



<h2 class="wp-block-heading" id="glava35">&nbsp;&nbsp;&nbsp;&nbsp;Вложенные документы</h2>



<p>MongoDB поддерживает вложенные документы:</p>



<pre class="wp-block-code"><code>db.employees.insert({_id: ObjectId("4d85c7039ab0fd70a117d734"), name: 'Ghanima', family: {mother: 'Chani', father: 'Paul', brother: ObjectId("4d85c7039ab0fd70a117d730")}})</code></pre>



<p>Вложенные документы можно запрашивать с помощью точечной нотации:</p>



<pre class="wp-block-code"><code>db.employees.find({'family.mother': 'Chani'})</code></pre>



<h2 class="wp-block-heading" id="glava36">&nbsp;&nbsp;&nbsp;&nbsp;Денормализация</h2>



<p>Традиционный путь ассоциировать пользователя с его постом — это колонка&nbsp;<code>userid</code>&nbsp;в таблице&nbsp;<code>posts</code>. С такой моделью нельзя отобразить список постов без дополнительного извлечения данных (JOIN) из таблицы пользователей. Возможное решение — хранить имя пользователя (<code>name</code>) вместе с&nbsp;<code>userid</code>&nbsp;для каждого поста.</p>


</br>



<h2 class="wp-block-heading" id="glava37">Команды (выжимка)</h2>



<pre class="wp-block-code"><code>db.version()
//показывает номер версии сервера</code></pre>



<pre class="wp-block-code"><code>db.getCollectionNames()
//получить список коллекций внутри нашей БД</code></pre>



<pre class="wp-block-code"><code>db.unicorns.find()
//вернет список документов (записей)</code></pre>



<pre class="wp-block-code"><code>db.unicorns.remove()
//поскольку мы не передали селектора, произойдёт удаление всех документов</code></pre>



<pre class="wp-block-code"><code>//Например, чтобы получить всех самцов единорога, весящих более 700 фунтов, мы можем написать:
db.unicorns.find({gender: 'm', weight: {$gt: 700}})</code></pre>



<pre class="wp-block-code"><code>//Оператор $exists используется для проверки наличия или отсутствия поля, например:
db.unicorns.find({vampires: {$exists: false}})</code></pre>



<pre class="wp-block-code"><code>//Оператор $or используется как ИЛИ
db.unicorns.find({gender: 'f', $or: &#091;{loves: 'apple'}, {loves: 'orange'}, {weight: {$lt: 500}}]})</code></pre>



<pre class="wp-block-code"><code>//update принимает 2 аргумента: селектор (where) для выборки и то, чем обновить соответствующее поле.
//Второй параметр используется для полной замены оригинала
db.unicorns.update({name: 'Roooooodles'}, {weight: 590})</code></pre>



<pre class="wp-block-code"><code>//Модификатор $set обновляет конкретные поля, а не весь документ
db.unicorns.update({name: 'Roooooodles'}, {$set: {weight: 590}})</code></pre>



<pre class="wp-block-code"><code>//модификатор $inc - увеличить или уменьшить значение поля
db.unicorns.update({name: 'Pilot'}, {$inc: {vampires: -2}})</code></pre>



<pre class="wp-block-code"><code>//модификатор $push - позволяет добавить данные в массив
db.unicorns.update({name: 'Aurora'}, {$push: {loves: 'sugar'}})</code></pre>



<pre class="wp-block-code"><code>//Обновление/вставка обновляет документ, если он найден, или создаёт новый — если не найден.
//Чтобы разрешить вставку при обновлении, установите третий параметр в true (ниже мы создаем коллекцию hits, если ее нет)
db.hits.update({page: 'unicorns'}, {$inc: {hits: 1}}, true);</code></pre>



<pre class="wp-block-code"><code>//если установить 4-й параметр в true, то обновятся все документы
db.unicorns.update({}, {$set: {vaccinated: true }}, false, true);</code></pre>



<pre class="wp-block-code"><code>//Второй необязательный параметр у find указывает на список полей, которые мы хотим получить.
db.unicorns.find(null, { name:true })</code></pre>



<pre class="wp-block-code"><code>//получаем все поля, кроме поля name:
db.unicorns.find({}, {name: 0})</code></pre>



<pre class="wp-block-code"><code>//сортировка по убыванию
db.unicorns.find().sort({weight: -1})</code></pre>



<pre class="wp-block-code"><code>// сортируем по весу, но получаем 2 и 3 по весу единорога, пропуская 1-го
db.unicorns.find().sort({weight: -1}).limit(2).skip(1)</code></pre>



<pre class="wp-block-code"><code>//подсчитать кол-во единорогов на счету которых более 60 вампиров
db.unicorns.count({vampires: {$gt: 50}})</code></pre>



<pre class="wp-block-code"><code>//Когда требуется смоделировать отношения «один-ко-многим» или «многие-ко-многим» можно использовать массивы
db.employees.insert({_id: ObjectId("4d85c7039ab0fd70a117d733"), name: 'Siona', manager: &#091;ObjectId("4d85c7039ab0fd70a117d730"), ObjectId("4d85c7039ab0fd70a117d732")] })</code></pre>



<pre class="wp-block-code"><code>// Ищем значение в массиве manager
db.employees.find({manager: ObjectId("4d85c7039ab0fd70a117d730")})</code></pre>



<pre class="wp-block-code"><code>//MongoDB поддерживает вложенные документы:
db.employees.insert({_id: ObjectId("4d85c7039ab0fd70a117d734"), name: 'Ghanima', family: {mother: 'Chani', father: 'Paul', brother: ObjectId("4d85c7039ab0fd70a117d730")}})</code></pre>



<pre class="wp-block-code"><code>//Вложенные документы можно запрашивать с помощью точечной нотации:
db.employees.find({'family.mother': 'Chani'})</code></pre>



<pre class="wp-block-code"><code>// версия > 3.2
db.employees.updateOne(…);
db.employees.updateMany (…);</code></pre>



<p><a href="https://docs.mongodb.com/manual/reference/operator/update/" target="_blank" rel="noreferrer noopener nofollow">operator update</a>&nbsp;(документация)</p>


</br>



<h2 class="wp-block-heading" id="glava38">Подключение mongoDB в nodejs</h2>



<pre class="wp-block-code"><code>// Retrieve
var MongoClient = require('mongodb').MongoClient;

// Connect to the db
MongoClient.connect("mongodb://localhost:27017/exampleDb", function(err, db) {
  if(!err) {
    console.log("We are connected");
  }
});</code></pre>



<p><a href="https://mongodb.github.io/node-mongodb-native/api-articles/nodekoarticle1.html#getting-that-connection-to-the-database" target="_blank" rel="noreferrer noopener nofollow">соединение с БД mongo DB</a></p>



<h2 class="wp-block-heading" id="glava39">Mongoose</h2>



<p>ODM – Object-Document Mapper (объектно-документное отображение). У mongo нет жесткой структуры, а вот Mongoose позволяет нам ввести понятие схемы.</p>



<h2 class="wp-block-heading" id="glava40">&nbsp;&nbsp;&nbsp;&nbsp;Установка и подключение</h2>



<pre class="wp-block-code"><code>//install
$ npm install mongoose</code></pre>



<pre class="wp-block-code"><code>//use
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');

var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function() {
    // connect
});</code></pre>


</br>



<h2 class="wp-block-heading" id="glava41">&nbsp;&nbsp;&nbsp;&nbsp;Схема</h2>



<p>Схема в Mongoose определяет метаданные модели &#8212; ее свойства, типы данных и ряд другой информации.</p>



<p><a href="http://mongoosejs.com/docs/guide.html" target="_blank" rel="noreferrer noopener nofollow">mongoosejs.com/docs/guide.html</a></p>



<pre class="wp-block-code"><code>var mongoose = require('mongoose');
var Schema = mongoose.Schema;


// внутри перечисляем наши поля
var blogSchema = new Schema({
    title:  String,
    author: String,
    body:   String,
    comments: &#091;{ body: String, date: Date }],
    date: {
        type: Date,
        default: Date.now,
        required: &#091;true, 'Укажите дату']
    },
    hidden: Boolean,
    meta: {
        votes: Number,
        favs:  Number
    }
});


var Blog = mongoose.model('Blog', blogSchema);
// ready to go!</code></pre>



<h2 class="wp-block-heading" id="glava42">&nbsp;&nbsp;&nbsp;&nbsp;Типы схем</h2>



<p><a href="http://mongoosejs.com/docs/schematypes.html" target="_blank" rel="noreferrer noopener nofollow">mongoosejs.com/docs/schematypes.html</a></p>



<ul><li><a href="http://mongoosejs.com/docs/api.html#schema-string-js" target="_blank" rel="noreferrer noopener nofollow">String</a></li><li><a href="http://mongoosejs.com/docs/api.html#schema-number-js" target="_blank" rel="noreferrer noopener nofollow">Number</a></li><li><a href="http://mongoosejs.com/docs/api.html#schema-date-js" target="_blank" rel="noreferrer noopener nofollow">Date</a></li><li><a href="http://mongoosejs.com/docs/api.html#schema-buffer-js" target="_blank" rel="noreferrer noopener nofollow">Buffer</a>&nbsp;(например, для изображений)</li><li>Boolean</li><li>Mixed (любой тип данных)</li><li><a href="http://mongoosejs.com/docs/api.html#schema-objectid-js" target="_blank" rel="noreferrer noopener nofollow">Objectid</a></li><li>Array</li></ul>


</br>



<h2 class="wp-block-heading" id="glava43">&nbsp;&nbsp;&nbsp;&nbsp;Модель</h2>



<p><a href="http://mongoosejs.com/docs/models.html" target="_blank" rel="noreferrer noopener nofollow">http://mongoosejs.com/docs/models.html</a>&nbsp;<strong>Модели</strong>&nbsp;&#8212; это конструкторы, составленные из определения нашей схемы. Экземпляры модели представляют собой документы, которые могут быть сохранены и извлечены из нашей БД.</p>



<pre class="wp-block-code"><code>var schema = new mongoose.Schema({ name: 'string', size: 'string' });
var Tank = mongoose.model('Tank', schema);</code></pre>



<p>Первый параметр в методе mongoose.model указывает на название модели, а второй параметр &#8212; собственно схема.</p>



<h2 class="wp-block-heading" id="glava44">&nbsp;&nbsp;&nbsp;&nbsp;Поиск</h2>



<p><a href="http://mongoosejs.com/docs/queries.html" target="_blank" rel="noreferrer noopener nofollow">http://mongoosejs.com/docs/queries.html</a></p>



<pre class="wp-block-code"><code>var Person = mongoose.model('Person', yourSchema);

//  { 'name.last': 'Ghost' }       - условие
//  'name occupation'              - выбираем нужные поля
//  function (err, person) { ...   - обрабатываем данные в callback'е

// find each person with a last name matching 'Ghost', selecting the `name` and `occupation` fields
Person.findOne({ 'name.last': 'Ghost' }, 'name occupation', function (err, person) {
  if (err) return handleError(err);
  console.log('%s %s is a %s.', person.name.first, person.name.last, person.occupation)
  // Space Ghost is a talk show host.
})

// находим все
Person.find({ 'name.last': 'Ghost' }, 'name occupation', function (err, docs) {})
// альтернатива callback
Person.find({ 'name.last': 'Ghost' }, 'name occupation').exec(function (err, docs) {})
</code></pre>



<h2 class="wp-block-heading" id="glava45">&nbsp;&nbsp;&nbsp;&nbsp;Сохраняем объект в БД</h2>



<pre class="wp-block-code"><code>var Person = mongoose.model('Person', yourSchema);

var subject = new Person({name: 'John'});

subject.save(function(err) {
    if (err) return handkeError(err);
})

//или

Person.create({name: 'John'}, function(err, subject) {
    if (err) return handkeError(err);
})</code></pre>


</br>



<h2 class="wp-block-heading" id="glava46">&nbsp;&nbsp;&nbsp;&nbsp;Редактирование и удаление</h2>



<pre class="wp-block-code"><code>

// меняем Karl на Johny
var query = {name: 'Karl'};

Model.update(query, {name: 'Johny'}, options, callback); // все заменит на  {name: 'Johny'}

Model.update(query, {$set: {name: 'Johny'}}, options, callback); // меняем только поле name

Person.findOne({name: 'Johny'}, function (err, person) {
    if (err) return handleError(err);
    person.name = 'Johny';
    person.save();
})

//удаление:
Model.remove({name: 'Johny'}, function (err, person) {
    if (err) return handleError(err);
})</code></pre>



<p>С mongo можно использовать Promise от node.</p>



<h2 class="wp-block-heading" id="glava47">mlab.com</h2>



<p><a href="https://mlab.com/" target="_blank" rel="noreferrer noopener nofollow">mlab.com</a>&nbsp;&#8212; это облачный сервис по предоставлению БД mongoDB.</p>



<ul><li>Создаем БД</li><li>Добавляем пользователя</li><li>И подключаемся в своем приложении:</li></ul>



<pre class="wp-block-code"><code>mongoose
    .connect(`mongodb://user_name:pass@ds147072.mlab.com:47072/name_bd`);</code></pre>


</br>



<h2 class="wp-block-heading" id="glava48">CRUD</h2>



<p><strong>CRUD</strong>&nbsp;— (create, read, update, delete — «создание, чтение,обновление, удаление») &#8212; 4 основные функции, используемые при работе базами данных.<strong>p.s.</strong></p>



<p>node, а значит и mongo, применяется для высоконагруженных проектов, там, где необходимо передавать много информации, чаты, игры.</p>



<p><a href="https://clip-clap.ru/it/%d1%81%d0%be%d1%84%d1%82-%d0%b8-%d0%be%d1%81/%d0%bf%d0%be%d0%b4%d1%80%d0%be%d0%b1%d0%bd%d0%be%d0%b5-%d1%80%d1%83%d0%ba%d0%be%d0%b2%d0%be%d0%b4%d1%81%d1%82%d0%b2%d0%be-%d0%bf%d0%be-mongodb-mongoose/" target="_blank" rel="noreferrer noopener">Обновленная статья</a></p>
<p>Сообщение <a href="https://clip-clap.ru/it/%d0%b0%d0%b4%d0%bc%d0%b8%d0%bd%d0%b8%d1%81%d1%82%d1%80%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/mongodb-%d0%bf%d0%be%d0%b4%d1%80%d0%be%d0%b1%d0%bd%d0%be%d0%b5-%d1%80%d1%83%d0%ba%d0%be%d0%b2%d0%be%d0%b4%d1%81%d1%82%d0%b2%d0%be/">MongoDB Подробное руководство</a> появились сначала на <a href="https://clip-clap.ru">Clip-Clap</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://clip-clap.ru/it/%d0%b0%d0%b4%d0%bc%d0%b8%d0%bd%d0%b8%d1%81%d1%82%d1%80%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5/mongodb-%d0%bf%d0%be%d0%b4%d1%80%d0%be%d0%b1%d0%bd%d0%be%d0%b5-%d1%80%d1%83%d0%ba%d0%be%d0%b2%d0%be%d0%b4%d1%81%d1%82%d0%b2%d0%be/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Настройка postfix + dovecot + mysql база + postfixadmin + roundcube + dkim на CentOS 7</title>
		<link>https://clip-clap.ru/it/%d1%81%d0%be%d1%84%d1%82-%d0%b8-%d0%be%d1%81/linux/centos/%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%be%d0%b9%d0%ba%d0%b0-postfix-dovecot-mysql-%d0%b1%d0%b0%d0%b7%d0%b0-postfixadmin-roundcube-dkim-%d0%bd%d0%b0-centos-7/</link>
					<comments>https://clip-clap.ru/it/%d1%81%d0%be%d1%84%d1%82-%d0%b8-%d0%be%d1%81/linux/centos/%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%be%d0%b9%d0%ba%d0%b0-postfix-dovecot-mysql-%d0%b1%d0%b0%d0%b7%d0%b0-postfixadmin-roundcube-dkim-%d0%bd%d0%b0-centos-7/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Sat, 18 Jul 2020 21:56:20 +0000</pubDate>
				<category><![CDATA[CentOS]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[Сервер]]></category>
		<category><![CDATA[Софт и ОС]]></category>
		<category><![CDATA[roundcube]]></category>
		<guid isPermaLink="false">https://clip-clap.ru/?p=1198</guid>

					<description><![CDATA[<p>Некоторое время назад я рассказывал, как настроить почтовый сервер на базе готовой сборки&#160;iredmail. Я подробно разобрал все актуальные вопросы почтового</p>
<p>Сообщение <a href="https://clip-clap.ru/it/%d1%81%d0%be%d1%84%d1%82-%d0%b8-%d0%be%d1%81/linux/centos/%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%be%d0%b9%d0%ba%d0%b0-postfix-dovecot-mysql-%d0%b1%d0%b0%d0%b7%d0%b0-postfixadmin-roundcube-dkim-%d0%bd%d0%b0-centos-7/">Настройка postfix + dovecot + mysql база + postfixadmin + roundcube + dkim на CentOS 7</a> появились сначала на <a href="https://clip-clap.ru">Clip-Clap</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Некоторое время назад я рассказывал, как настроить почтовый сервер на базе готовой сборки&nbsp;<a href="https://clip-clap.ru/it/%d1%81%d0%be%d1%84%d1%82-%d0%b8-%d0%be%d1%81/linux/%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%be%d0%b9%d0%ba%d0%b0-%d0%bf%d0%be%d1%87%d1%82%d0%be%d0%b2%d0%be%d0%b3%d0%be-%d1%81%d0%b5%d1%80%d0%b2%d0%b5%d1%80%d0%b0-iredmail-%d0%bd%d0%b0-centos-7/" target="_blank" aria-label="undefined (откроется в новой вкладке)" rel="noreferrer noopener">iredmail</a>. Я подробно разобрал все актуальные вопросы почтового сервера на linux на базе postfix. Сейчас хочу настроить похожий функционал, но с нуля, за основу взяв postfix + dovecot. Я расскажу про установку и настройку postfix на centos 7, причем только тех модулей и дополнений, которые сам считаю нужными и полезными на почтовом сервере.</p>



<p>Я буду настраивать почтовый сервер на ОС linux, а точнее на&nbsp;<strong>CentOS 7</strong>. За основу будет взят&nbsp;<strong>postfix</strong>, который присутствует в этой системе из коробки. Инструкция получится универсальной, можно использовать и для других дистрибутивов. Все основные конфиги легко переносятся на разные&nbsp;системы, требуя минимальной правки, в основном путей.</p>



<p>Я напишу статью на самом что ни на есть реальном примере, без какой-либо правки доменов, ip и прочего, чтобы не ошибиться и показать максимально возможный реальный пример. У меня есть технический домен zeroxzed.ru. Я буду использовать его в своей работе. Почтовый сервер будет иметь имя mail.zeroxzed.ru. Всю теорию по подготовке dns к установке и настройке почтового сервера я рассказывал в&nbsp;<a href="https://clip-clap.ru/it/%d1%81%d0%be%d1%84%d1%82-%d0%b8-%d0%be%d1%81/linux/%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%be%d0%b9%d0%ba%d0%b0-%d0%bf%d0%be%d1%87%d1%82%d0%be%d0%b2%d0%be%d0%b3%d0%be-%d1%81%d0%b5%d1%80%d0%b2%d0%b5%d1%80%d0%b0-iredmail-%d0%bd%d0%b0-centos-7/" target="_blank" aria-label="undefined (откроется в новой вкладке)" rel="noreferrer noopener">предыдущей статье</a>&nbsp;о почтовом сервере. Не хочу здесь повторяться. Уточню только список действий, которые вам нужно проделать c ДНС:</p>


</br>



<ol><li>Создаем A запись в DNS — mail.zeroxzed.ru.</li><li>Добавляем или редактируем MX запись, указывая в качестве почтового сервера&nbsp;mail.zeroxzed.ru.</li><li>Просим провайдера прописать PTR для внешнего ip адреса, который будет использовать почтовый сервер. В качестве ptr записи просим установить имя нашего сервера — mail.zeroxzed.ru.</li></ol>



<div class="wp-block-image"><figure class="aligncenter"><img decoding="async" src="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-01.png" alt="Настройка dns записей для почтового сервера" class="wp-image-5028"/></figure></div>



<p>Я предпочитаю в качестве dns хостинга использовать&nbsp;<a href="https://pdd.yandex.ru/" target="_blank" rel="noreferrer noopener">сервера яндекса</a>, даже если не прикрепляю его почту к домену. На картинке показан минимально необходимый набор записей, кроме PTR. Этими записями управляете не вы, а провайдер, который вам выдает ip. Пока с днс все. Позже мы вернемся к этому вопросу, когда будем добавлять dkim и spf записи. Но обо всем по порядку.</p>



<p>Подготовим систему centos к установке и настройке почтового сервера postfix. Если у вас еще нет готовой системы, то рекомендую воспользоваться моими статьями по&nbsp;установке&nbsp;и&nbsp;настройке centos. Отдельно потратьте время на&nbsp;<a href="https://clip-clap.ru/it/%d1%81%d0%be%d1%84%d1%82-%d0%b8-%d0%be%d1%81/linux/centos/%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%be%d0%b9%d0%ba%d0%b0-iptables-%d0%b2-centos-7/" target="_blank" aria-label="undefined (откроется в новой вкладке)" rel="noreferrer noopener">настройку iptables</a>. Я не буду касаться этого вопроса в данной статье, чтобы не раздувать ее второстепенными вещами. Удобнее, когда все по отдельности рассказано и описано с должной глубиной. Сваливать все в одну кучу не хочется.</p>



<p>По вступлению вроде все, основное рассказал. Приступим к настройке нашего почтового сервера.Сразу хочу сделать предупреждение. Настройка почтового сервера достаточно трудоемкий процесс, требует определенных навыков, знаний и понимания принципов работы используемых средств. Я не ставлю для себя цель расписать максимально подробно так, чтобы было понятно даже неподготовленному администратору linux. Вы должны быть так или иначе подготовлены, либо запаситесь терпением и разбирайтесь внимательно сами в нюансах. Эта статья на полный копипаст не подходит, что-то остается за кадром для самостоятельного выполнения. Иначе нельзя, получится очень большой и громоздкий материал.</p>



<h2 class="wp-block-heading">Установка postfixadmin</h2>



<p>Начнем с установки и настройки панели управления почтовым сервером postfix —&nbsp;<strong>postfixadmin</strong>. Без него начинать что-то делать неудобно, так как управлять пользователями, ящиками, алиасами будет нечем. По своей сути postfixadmin — набор php скриптов для управления записями в mysql базе данных, которую использует сервер postfix во время своей работы. Соответственно, для работы postfixadmin нам нужен web сервер. Подробно о&nbsp;<a href="https://clip-clap.ru/it/%d1%81%d0%be%d1%84%d1%82-%d0%b8-%d0%be%d1%81/linux/centos/%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%be%d0%b9%d0%ba%d0%b0-web-%d1%81%d0%b5%d1%80%d0%b2%d0%b5%d1%80%d0%b0-apache-%d0%b2-centos-8/" target="_blank" aria-label="undefined (откроется в новой вкладке)" rel="noreferrer noopener">настройке web сервера на centos</a>&nbsp;читайте отдельно. Сейчас же мы быстро установим все необходимое. Привожу только команды, без комментариев. Все подробности по приведенной выше ссылке.</p>



<pre class="wp-block-preformatted"># yum install httpd php phpmyadmin&nbsp;mariadb mariadb-server php-imap</pre>



<p>Этих пакетов со всеми зависимостями будет достаточно для установки всех необходимых компонентов веб сервера. Я специально ставлю phpmyadmin, с ним удобно работать с базой. В&nbsp;нашем случае все пользователи будут храниться в mysql, иногда может понадобиться туда заглянуть. Подробнее с&nbsp;установкой и настройкой phpmyadmin&nbsp;можете ознакомиться отдельно.</p>



<p>Запускаем httpd и mariadb и добавляем их в автозагрузку.</p>



<pre class="wp-block-preformatted"># systemctl start httpd
# systemctl enable httpd
# systemctl start mariadb
# systemctl enable mariadb</pre>



<p>Задаем пароль root для mysql.</p>



<pre class="wp-block-preformatted"># /usr/bin/mysql_secure_installation</pre>



<p>Проверяем работу web сервера. Заходим по ip адресу сервера —&nbsp;<em>http://188.35.19.125/</em>, а также проверяем работу phpmyadmin —&nbsp;<em>http://188.35.19.125/phpmyadmin/</em>. Его нужно настроить, об этом рассказано в статье, которую я привел чуть выше. По-умолчанию в phpmyadmin доступ закрыт. Если все сделали правильно, то увидите примерно следующее.</p>



<div class="wp-block-image"><figure class="aligncenter"><a href="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-02.png"><img decoding="async" src="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-02.png" alt="Вход в phpmyadmin" class="wp-image-5029"/></a></figure></div>



<p>Сразу создадим тут пользователя postfix и одноименную базу данных. Запомните учетные данные, они нам далее понадобятся.</p>


</br>



<p>Веб сервер готов, продолжаем настройку почтового сервера. Скачиваем последнюю версию postfixadmin.</p>



<pre class="wp-block-preformatted"># cd /usr/src
# wget&nbsp;https://downloads.sourceforge.net/project/postfixadmin/postfixadmin/postfixadmin-3.0.2/postfixadmin-3.0.2.tar.gz</pre>



<p>Скорее всего во время вашей установки версия postfixadmin изменится и ссылка может быть неактуальной. Но даже если она будет актуальна, возможно выйдет более новая версия. Проверьте ее по ссылке&nbsp;<a href="https://sourceforge.net/projects/postfixadmin/" target="_blank" rel="noreferrer noopener">https://sourceforge.net/projects/postfixadmin/</a>&nbsp;и скачайте самую свежую версию.</p>



<p>Распаковываем архив и копируем в директорию веб сервера.</p>



<pre class="wp-block-preformatted"># tar -xvzf postfixadmin-*
# mv /usr/src/postfixadmin-3.0.2 /var/www/html/postfixadmin</pre>



<p>Назначаем владельцем пользователя веб сервера:</p>



<pre class="wp-block-preformatted"># chown -R apache. /var/www/html/postfixadmin/</pre>



<p>Дальше редактируем конфигурационный файл postfixadmin.</p>



<pre class="wp-block-preformatted"># mcedit&nbsp;/var/www/html/postfixadmin/config.inc.php</pre>



<p>Приводим параметры к следующему виду:</p>



<pre class="wp-block-preformatted">$CONF['configured'] = true;
$CONF['default_language'] = 'ru';
$CONF['database_type'] = 'mysqli';
$CONF['database_host'] = 'localhost';
$CONF['database_user'] = 'postfix';
$CONF['database_password'] = '12345678';
$CONF['database_name'] = 'postfix';
$CONF['admin_email'] = 'root@zeroxzed.ru';
$CONF['encrypt'] = '<strong>md5crypt</strong>';
$CONF['default_aliases'] = array (
 'abuse' =&gt; 'root',
 'hostmaster' =&gt; 'root',
 'postmaster' =&gt; 'root',
 'webmaster' =&gt; 'root'
);
$CONF['domain_path'] = 'YES';
$CONF['domain_in_mailbox'] = 'YES';</pre>



<p>Обращаю внимание на выделенный параметр. Он указывает на то, в каком виде хранить пароли пользователей в базе данных. Конечно, хранить обычным текстом без шифрования это дурной тон и может быть опасно. Я указал хранение в шифрованном виде. Но если мы говорим о небольшой компании без публичного доступа к серверу, можно использовать нешифрованные пароли. Для этого указываем значение параметра&nbsp;<strong>cleartext</strong>. Я сам так часто делаю просто из соображений удобства. Объясню, в чем удобство.</p>



<p>К примеру, у пользователя несколько устройств подключены к почте и он забыл свой пароль. Админ при создании почему-то тоже его никуда не записал, или забыл, или потерял. Вам придется сбросить пароль и перенастроить все устройства. Если же у вас пароль хранится в открытом виде, вы просто смотрите в базу и говорите пользователю пароль. Пароли в открытом виде удобно просто выгрузить дампом из базы, если понадобится кому-то все учетки передать. Но тут как посмотреть <img src="https://s.w.org/images/core/emoji/15.0.3/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /> С одной стороны плюс, с другой минус — кто-то очень просто может спереть все ваши пароли. В общем, тут от ситуации зависит, решайте сами, как вам удобнее хранить пароли.</p>



<p>У меня распространены ситуации, когда я удаленно администрирую сервера, а на месте эникеи работают с пользователями. Чаще всего это не очень аккуратные и ответственные люди, иначе они бы работали с серверами <img src="https://s.w.org/images/core/emoji/15.0.3/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Они часто забывают записать пароль, путают что-то и т.д. В итоге, когда один человек увольняется и приходит другой, оказывается, что найти пароли на некоторые ящики просто невозможно. Тут очень выручает возможность посмотреть пароль в базе. Я новому админу либо пароль говорю, либо весь дамп сразу отдаю, пусть работает.</p>



<p>Если вы сами работаете с сервером и все аккуратно ведете, записываете, например, в keepass все пароли от почтовых ящиков, то смело шифруйте все пароли, так будет спокойнее.</p>



<p>Последние 2 параметра&nbsp;<strong>domain_path</strong>&nbsp;и&nbsp;<strong>domain_in_mailbox</strong>&nbsp;указывайте по своему усмотрению. В файле конфигурации в комментариях расписано, за что они отвечают и в чем отличие. Мне кажется, удобно хранить директории именно в таком виде, как я указал. Получится следующий путь до ящика, если у вас архив почты будет жить, к примеру, в директории&nbsp;<em>/mnt/mail</em>&nbsp;—&nbsp;/mnt/mail/zeroxzed.ru/root@zeroxzed.ru.</p>



<p>С параметрами разобрались. Сохраняем конфиг. Идем по адресу&nbsp;<em>http://188.35.19.125/postfixadmin/setup.php</em>&nbsp;и начинаем установку postfixadmin. Первым делом идет проверка всех необходимых для установки и работы компонентов. Для продолжения установки у вас должна быть такая картинка.</p>



<div class="wp-block-image"><figure class="aligncenter"><img decoding="async" src="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-03.png" alt="Установка postfixadmin" class="wp-image-5030"/></figure></div>



<p>Если чего-то не хватает, разбирайтесь по месту. Если делаете по моей инструкции, то все должно быть в порядке. Указывайте пароль установки и продолжайте. Вы должны получить строку с хэшем этого пароля.</p>



<div class="wp-block-image"><figure class="aligncenter"><img decoding="async" src="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-04.png" alt="Добавление пароля установки" class="wp-image-5031"/></figure></div>



<p>Добавляем полученную строку в файл конфигурации postfixadmin.</p>


</br>



<pre class="wp-block-preformatted"># mcedit&nbsp;/var/www/html/postfixadmin/config.inc.php</pre>



<pre class="wp-block-preformatted">$CONF['setup_password'] = '67e46bdcc7aeb431f7af9a6d02f43352:30672e5a9deacaf505d32807b967caf9fd0c32ef';</pre>



<p>Используя этот пароль, можно создать учетную запись администратора панели управления. Делаем это, учитывая, что пароль должен содержать не менее двух цифр. Если все сделали правильно, то увидите сообщение.</p>



<div class="wp-block-image"><figure class="aligncenter"><img decoding="async" src="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-05.png" alt="Добавление администратора postfixadmin" class="wp-image-5032"/></figure></div>



<p>Переходим по ссылке и авторизуемся с помощью учетной записи администратора, которую только что сделали. Вы должны увидеть основную страницу интерфейса postfixadmin.</p>



<div class="wp-block-image"><figure class="aligncenter"><img decoding="async" src="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-06.png" alt="Главная страница postfixadmin" class="wp-image-5033"/></figure></div>



<p>Теперь нам нужно добавить домен в панель управления. Идем в раздел&nbsp;Список доменов -&gt; Новый домен&nbsp;и добавляем свой домен.</p>



<div class="wp-block-image"><figure class="aligncenter"><img decoding="async" src="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-07.png" alt="Создание нового домена" class="wp-image-5034"/></figure></div>



<p>При создании домена были добавлены стандартные алиасы, получателя для которых мы указали еще в конфиге — ящик root@zeroxzed.ru. Создание таких алиасов требование стандартов, но по факту, кроме спама, вы скорее всего ничего не будете получать по этим адресам. Так что их создание оставляйте на свое усмотрения. Я обычно их не делаю, так как ящик для этих алиасов все равно не читаю.</p>



<p>Далее создадим почтовый ящик администратора —&nbsp;root@zeroxzed.ru. Для этого идем в раздел&nbsp;Обзор -&gt; Создать ящик&nbsp;и заполняем поля.</p>



<div class="wp-block-image"><figure class="aligncenter"><img decoding="async" src="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-08.png" alt="Добавление почтового ящика" class="wp-image-5035"/></figure></div>



<p>Непосредственно ящик на диске создан не будет, так как у нас еще не настроена почтовая система, но запись в базе данных появится. Это можно проверить через phpmyadmin.</p>



<div class="wp-block-image"><figure class="aligncenter"><a href="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-09.png"><img decoding="async" src="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-09.png" alt="Проверка нового ящика в mysql базе" class="wp-image-5036"/></a></figure></div>



<p>Как мы видим, пароль указан в зашифрованном виде. На этом установку и настройку postfixadmin завершаем. Интерфейс для управления почтовым сервером мы подготовили. Теперь можно заняться непосредственно настройкой postfix.</p>



<h2 class="wp-block-heading">Настройка postfix</h2>



<p>Сердце нашего почтового сервера на linux — postfix. В дистрибутиве centos он уже установлен, можно сразу переходить к настройке. Рисуем следующий конфиг.</p>



<pre class="wp-block-preformatted"># mcedit&nbsp;/etc/postfix/main.cf</pre>



<pre class="wp-block-preformatted">soft_bounce = no
queue_directory = /var/spool/postfix
command_directory = /usr/sbin
daemon_directory = /usr/libexec/postfix
data_directory = /var/lib/postfix
mail_owner = postfix

myhostname = <strong>mail.zeroxzed.ru</strong>
mydomain = <strong>zeroxzed.ru</strong>
myorigin = $myhostname

inet_interfaces = all
inet_protocols = ipv4

mydestination = localhost.$mydomain, localhost
unknown_local_recipient_reject_code = 550
mynetworks = 127.0.0.0/8

alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases

smtpd_banner = $myhostname ESMTP $mail_name

debug_peer_level = 2
# Строки с PATH и ddd должны быть с отступом в виде табуляции от начала строки
debugger_command =
         PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin
         ddd $daemon_directory/$process_name $process_id &amp; sleep 5

sendmail_path = /usr/sbin/sendmail.postfix
newaliases_path = /usr/bin/newaliases.postfix
mailq_path = /usr/bin/mailq.postfix
setgid_group = postdrop
html_directory = no
manpage_directory = /usr/share/man
sample_directory = /usr/share/doc/postfix-2.10.1/samples
readme_directory = /usr/share/doc/postfix-2.10.1/README_FILES

relay_domains = mysql:/etc/postfix/mysql/relay_domains.cf
virtual_alias_maps = mysql:/etc/postfix/mysql/virtual_alias_maps.cf,
 mysql:/etc/postfix/mysql/virtual_alias_domain_maps.cf
virtual_mailbox_domains = mysql:/etc/postfix/mysql/virtual_mailbox_domains.cf
virtual_mailbox_maps = mysql:/etc/postfix/mysql/virtual_mailbox_maps.cf

smtpd_discard_ehlo_keywords = etrn, silent-discard
smtpd_forbidden_commands = CONNECT GET POST
broken_sasl_auth_clients = yes
smtpd_delay_reject = yes
smtpd_helo_required = yes
smtp_always_send_ehlo = yes
disable_vrfy_command = yes

smtpd_helo_restrictions = permit_mynetworks,
 permit_sasl_authenticated,
 reject_non_fqdn_helo_hostname,
 reject_invalid_helo_hostname

smtpd_data_restrictions = permit_mynetworks,
 permit_sasl_authenticated,
 reject_unauth_pipelining,
 reject_multi_recipient_bounce,

smtpd_sender_restrictions = permit_mynetworks,
 permit_sasl_authenticated,
 reject_non_fqdn_sender,
 reject_unknown_sender_domain

smtpd_recipient_restrictions = reject_non_fqdn_recipient,
 reject_unknown_recipient_domain,
 reject_multi_recipient_bounce,
 permit_mynetworks,
 permit_sasl_authenticated,
 reject_unauth_destination,

smtp_tls_security_level = may
smtpd_tls_security_level = may
smtpd_tls_loglevel = 1
smtpd_tls_received_header = yes
smtpd_tls_session_cache_timeout = 3600s
smtp_tls_session_cache_database = btree:$data_directory/smtp_tls_session_cache
smtpd_tls_key_file = /etc/postfix/certs/key.pem
smtpd_tls_cert_file = /etc/postfix/certs/cert.pem
tls_random_source = dev:/dev/urandom

# Ограничение максимального размера письма в байтах
message_size_limit = 20000000
smtpd_soft_error_limit = 10
smtpd_hard_error_limit = 15
smtpd_error_sleep_time = 20
anvil_rate_time_unit = 60s
smtpd_client_connection_count_limit = 20
smtpd_client_connection_rate_limit = 30
smtpd_client_message_rate_limit = 30
smtpd_client_event_limit_exceptions = 127.0.0.0/8
smtpd_client_connection_limit_exceptions = 127.0.0.0/8

maximal_queue_lifetime = 1d
bounce_queue_lifetime = 1d

smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/dovecot-auth

# Директория для хранения почты
virtual_mailbox_base = <strong>/mnt/mail</strong>
virtual_minimum_uid = 1000
virtual_uid_maps = static:1000
virtual_gid_maps = static:1000
virtual_transport = dovecot
dovecot_destination_recipient_limit = 1

sender_bcc_maps = hash:/etc/postfix/sender_bcc_maps
recipient_bcc_maps = hash:/etc/postfix/recipient_bcc_maps</pre>


</br>



<p>Я выделил жирным имя домена и путь для директории с почтовыми ящиками. Не забудьте поменять эти параметры на свои. Сохраняем конфиг и продолжаем настройку. В таком виде сервер еще не готов. Нужно теперь создать все то, что описано в файле конфигурации. Создаем папку для файлов с конфигурацией подключения к mysql и сами файлы подключения.</p>



<pre class="wp-block-preformatted"># mkdir /etc/postfix/mysql &amp;&amp; cd&nbsp;/etc/postfix/mysql</pre>



<pre class="wp-block-preformatted"># mcedit relay_domains.cf

hosts = localhost
user =&nbsp;postfix
password =&nbsp;12345678
dbname = postfix
query = SELECT domain FROM domain WHERE domain='%s' and backupmx = '1'</pre>



<pre class="wp-block-preformatted">#&nbsp;mcedit &nbsp;virtual_alias_domain_maps.cf

hosts = localhost
user = postfix
password = 12345678
dbname = postfix
query = SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' and alias.address = CONCAT('%u', '@', alias_domain.target_domain) AND alias.active = 1</pre>



<pre class="wp-block-preformatted">#&nbsp;mcedit&nbsp;virtual_alias_maps.cf

hosts = localhost
user = postfix
password = 12345678
dbname = postfix
query = SELECT goto FROM alias WHERE address='%s' AND active = '1'</pre>



<pre class="wp-block-preformatted">#&nbsp;mcedit&nbsp;virtual_mailbox_domains.cf</pre>



<pre class="wp-block-preformatted">hosts = localhost
user = postfix
password = 12345678
dbname = postfix
query = SELECT domain FROM domain WHERE domain='%s' AND backupmx = '0' AND active = '1'</pre>



<pre class="wp-block-preformatted">#&nbsp;mcedit&nbsp;virtual_mailbox_maps.cf

hosts = localhost
user = postfix
password = 12345678
dbname = postfix
query = SELECT maildir FROM mailbox WHERE username='%s' AND active = '1'</pre>



<p>Редактируем файл&nbsp;<em>/etc/postfix/master.cf</em>. Нам надо добавить строки, касающиеся настройки&nbsp;Submission для того, чтобы почтовый сервер работал на 587 порту. Смартфоны очень часто при настройке используют этот порт по-умолчанию, где-то даже без возможности изменить эту настройку. Приводим секцию, отвечающую за эту работу к следующему виду.</p>



<pre class="wp-block-preformatted">submission inet n - n - - smtpd
 -o syslog_name=postfix/submission
 -o smtpd_tls_wrappermode=no
 -o smtpd_tls_security_level=encrypt
 -o smtpd_sasl_auth_enable=yes
 -o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
 -o smtpd_relay_restrictions=permit_mynetworks,permit_sasl_authenticated,defer_unauth_destination
 -o milter_macro_daemon_name=ORIGINATING</pre>



<p>Обращаю внимание на пробел в начале строки, начиная со второй. Его надо обязательно оставить. Добавляем еще настройки для того, чтобы наш сервер поддерживал протокол SSL/TLS и слушал порт 465</p>



<pre class="wp-block-preformatted">smtps inet n - n - - smtpd
 -o syslog_name=postfix/smtps
 -o smtpd_tls_wrappermode=yes
 -o smtpd_sasl_auth_enable=yes
 -o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
 -o smtpd_relay_restrictions=permit_mynetworks,permit_sasl_authenticated,defer_unauth_destination
 -o milter_macro_daemon_name=ORIGINATING</pre>



<p>В этот же файл добавляем еще одну настройку, которая будет указывать postfix, что доставкой почты у нас будет заниматься dovecot, который мы настроим следом. Добавляем в master.cf в самый конец.</p>



<pre class="wp-block-preformatted">dovecot unix - n n - - pipe
 flags=DRhu user=vmail:vmail argv=/usr/libexec/dovecot/deliver -f ${sender} -d ${recipient}</pre>



<p>Сгенерируем самоподписанные ssl сертификаты для нашего почтового сервера. Позже отдельным пунктом я расскажу как использовать полноценные сертификаты. Они не всем нужны, поэтому показываю быструю настройку postfix на использование своих сертификатов, которые уже указаны в конфиге postfix.</p>



<p>Создаем директорию и сами сертификаты:</p>



<pre class="wp-block-preformatted"># mkdir /etc/postfix/certs
#&nbsp;openssl req -new -x509 -days 3650 -nodes -out /etc/postfix/certs/cert.pem -keyout /etc/postfix/certs/key.pem</pre>



<p>Для генерации вам зададут несколько вопросов по поводу данных о сертификате. В принципе, можете там писать все, что угодно. Вот мои данные.</p>



<div class="wp-block-image"><figure class="aligncenter"><a href="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-10.png"><img decoding="async" src="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-10.png" alt="Создание ssl сертификата для postfix" class="wp-image-5037"/></a></figure></div>


</br>



<p>Создадим файлы для информации о ящиках, куда будет собираться вся входящая и исходящая почта.</p>



<pre class="wp-block-preformatted"># mcedit&nbsp;/etc/postfix/recipient_bcc_maps</pre>



<pre class="wp-block-preformatted">@zeroxzed.ru all_in@zeroxzed.ru</pre>



<pre class="wp-block-preformatted"># mcedit /etc/postfix/sender_bcc_maps</pre>



<pre class="wp-block-preformatted">@zeroxzed.ru all_out@zeroxzed.ru</pre>



<p>Создаем индексированные базы данных из этих файлов. Это нужно делать каждый раз, после изменения.</p>



<pre class="wp-block-preformatted"># postmap&nbsp;/etc/postfix/recipient_bcc_maps&nbsp;/etc/postfix/sender_bcc_maps</pre>



<p>Теперь создайте два почтовых ящика&nbsp;all_in@zeroxzed.ru и&nbsp;all_out@zeroxzed.ru через postfixadmin.</p>



<p>Немного поясню по этим ящикам — для чего они нужны. Изначально я их делал, когда пользователи использовали протокол pop3 без сохранения писем на сервере. Это позволяло организовать бэкап всей переписки. Эти ящики очень быстро заполняются и занимают огромный объем, поэтому их обязательно надо чистить. Я просто скриптами регулярно собирал всю почту в архивы с именами в виде дат. Если нужно было какое-то письмо найти, то просто распаковывал нужный архив.</p>



<p>В случае с imap роль бэкапа отпадает, так как вся почта хранится на сервере. Но эти ящики все равно бывают полезны, когда пользователь, к примеру, удалил какое-то важное письмо и потом делает вид, что его и не было. Если это письмо пришло только сегодня и еще не успело улететь в бэкап, то кроме записи в логах об этом письме, вы не увидите само содержимое. А с такими ящиками все сразу будет понятно, и вопросы отпадут. Последнее применение — служба безопасности. Если у вас есть кто-то, кому положено читать всю переписку, то реализовать этот функционал можно таким простым способом.</p>



<p>Все основные настройки для postfix мы сделали. Некоторые из них завязаны на работу с dovecot, который мы еще не настроили. Поэтому больше postfix не трогаем, не перезапускаем. Идем настраивать dovecot — imap сервер нашей почтовой системы.</p>



<h2 class="wp-block-heading">Настройка dovecot</h2>



<p>Займемся настройкой dovecot — сервер доставки почты пользователю по протоколам pop3 и imap. Я не вижу причин использовать pop3. Он неудобен по сравнению с&nbsp;imap. Чаще всего pop3 отключаю вовсе. Но это уже на ваше усмотрение. Приведу пример с настройкой обоих протоколов. Помимо основного функционала по доставке почты, я настрою несколько полезных плагинов. Расскажу о них поподробнее:</p>



<ul><li><strong>Sieve</strong>&nbsp;— выполняет фильтрацию почты по заданным правилам в момент локальной доставки на почтовом сервере. Удобство такого подхода в том, что вы один раз можете настроить правило сортировки, и оно будет работать во всех клиентах, которыми вы будете получать почту по imap. Правила создаются, хранятся и исполняются на самом сервере.</li><li><strong>Acl</strong>&nbsp;— позволяет пользователям расшаривать папки в своем почтовом ящике и предоставлять доступ к этим папкам другим пользователям. Не часто видел, чтобы этот функционал настраивали и использовали. Думаю, просто по незнанию. По мне так очень удобный и полезный функционал.</li></ul>



<p>Часто вижу, что люди настраивают плагин&nbsp;<strong>quota</strong>, который позволяет ограничивать максимальный размер почтового ящика. Я лично в своей работе его не использую. Возможно, когда у тебя клиентов сотни и тысячи это имеет значение и надо обязательно настроить ограничение. Когда же ящиков меньше, нет смысла напрягать людей постоянной чисткой. Сейчас диски стоят не так дорого. Мне кажется, проще и дешевле увеличить место на сервере, нежели постоянно беспокоить пользователей необходимостью чистки ящика. Лучше ограничить максимальный размер письма, скажем 20-ю мегабайтами. Тогда сильно забить ящик даже при большом желании быстро не получится. А почта все-таки важный инструмент в работе. Мне кажется, ее лучше хранить как можно дольше.</p>



<p>Есть еще один полезный плагин&nbsp;<strong>expire</strong>, который позволяет удалять устаревшие письма в определенных папках. Например, удалять все письма старше 30-ти дней в корзине и папке со спамом. Но реально пользоваться им не получается по простой причине. Разные почтовые клиенты создают различные папки для корзины и спама. Thunderbird создает папки с латинскими именами trash и spam, outlook с русскими, которые на почтовом сервере преобразуются в кодировку UTF7, мобильные клиенты тоже используют разные имена папок. В итоге нет единообразия, плагин полноценно не работает.</p>



<p>Я рассказал об этих плагинах для наводки. Сам их не настраиваю, но если вам захочется реализовать описанный функционал, можете сами разобраться и настроить.</p>



<p>Небольшую теорию я дал, теперь переходим к практике. Устанавливаем необходимые для dovecot пакеты.</p>



<pre class="wp-block-preformatted"># yum install dovecot dovecot-mysql dovecot-pigeonhole</pre>



<p>Изначально конфиг dovecot разбит на отдельные сегменты и лежат в директории&nbsp;<em>/etc/dovecot/conf.d</em>. Каждый файл — отдельный функционал. Мне не нравится прыгать по файлам, поэтому я храню все в едином общем файле конфигурации&nbsp;<em>/etc/dovecot/dovecot.conf</em>. С ним мы и будем работать. Приводим его к следующему виду.</p>



<pre class="wp-block-preformatted"># mcedit&nbsp;/etc/dovecot/dovecot.conf</pre>



<pre class="wp-block-preformatted">listen = * [::]

mail_plugins = mailbox_alias acl

protocols = imap pop3 sieve lmtp

mail_uid = 1000
mail_gid = 1000

first_valid_uid = 1000
last_valid_uid = 1000

log_path = /var/log/dovecot/main.log
info_log_path = /var/log/dovecot/info.log
debug_log_path = /var/log/dovecot/debug.log

ssl_protocols = !SSLv2 !SSLv3
ssl = required
verbose_ssl = no
ssl_cert = &lt;/etc/postfix/certs/cert.pem
ssl_key = &lt;/etc/postfix/certs/key.pem

ssl_cipher_list = ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
ssl_dh_parameters_length = 2048
ssl_prefer_server_ciphers = yes

disable_plaintext_auth = yes

mail_location = maildir:/mnt/mail/%d/%u/

auth_default_realm = zeroxzed.ru

auth_mechanisms = PLAIN LOGIN

service auth {
 unix_listener /var/spool/postfix/private/dovecot-auth {
 user = postfix
 group = postfix
 mode = 0666
 }
unix_listener auth-master {
 user = vmail
 group = vmail
 mode = 0666
 }

unix_listener auth-userdb {
 user = vmail
 group = vmail
 mode = 0660
 }
}

service lmtp {
 unix_listener /var/spool/postfix/private/dovecot-lmtp {
 user = postfix
 group = postfix
 mode = 0600
 }

 inet_listener lmtp {
 address = 127.0.0.1
 port = 24
 }
}

userdb {
 args = /etc/dovecot/dovecot-mysql.conf
 driver = sql
 }

passdb {
 args = /etc/dovecot/dovecot-mysql.conf
 driver = sql
 }

auth_master_user_separator = *
 
plugin {
 auth_socket_path = /var/run/dovecot/auth-master

 acl = vfile
 acl_shared_dict = file:/mnt/mail/shared-folders/shared-mailboxes.db
 sieve = /mnt/mail/sieve/%u.sieve
 mailbox_alias_old = Sent
 mailbox_alias_new = Sent Messages
 mailbox_alias_old2 = Sent
 mailbox_alias_new2 = Sent Items
}

protocol lda {
 mail_plugins = $mail_plugins sieve
 auth_socket_path = /var/run/dovecot/auth-master
 deliver_log_format = mail from %f: msgid=%m %$
 log_path = /var/log/dovecot/lda-errors.log
 info_log_path = /var/log/dovecot/lda-deliver.log
 lda_mailbox_autocreate = yes
 lda_mailbox_autosubscribe = yes
 postmaster_address = root
}

protocol lmtp {
 info_log_path = /var/log/dovecot/lmtp.log
 mail_plugins = quota sieve
 postmaster_address = postmaster
 lmtp_save_to_detail_mailbox = yes
 recipient_delimiter = +
}

protocol imap {
 mail_plugins = $mail_plugins imap_acl
 imap_client_workarounds = tb-extra-mailbox-sep
 mail_max_userip_connections = 30
}

protocol pop3 {
 mail_plugins = $mail_plugins
 pop3_client_workarounds = outlook-no-nuls oe-ns-eoh
 pop3_uidl_format = %08Xu%08Xv
 mail_max_userip_connections = 30
}

service imap-login {
 service_count = 1
 process_limit = 500
 }

service pop3-login {
 service_count = 1
 }

service managesieve-login {
 inet_listener sieve {
 port = 4190
 }
}

namespace {
 type = private
 separator = /
 prefix =
 inbox = yes

 mailbox Sent {
 auto = subscribe
 special_use = \Sent
 }
 mailbox "Sent Messages" {
 auto = no
 special_use = \Sent
 }
 mailbox "Sent Items" {
 auto = no
 special_use = \Sent
 }
 mailbox Drafts {
 auto = subscribe
 special_use = \Drafts
 }
 mailbox Trash {
 auto = subscribe
 special_use = \Trash
 }
 mailbox "Deleted Messages" {
 auto = no
 special_use = \Trash
 }
 mailbox Junk {
 auto = subscribe
 special_use = \Junk
 }
 mailbox Spam {
 auto = no
 special_use = \Junk
 }
 mailbox "Junk E-mail" {
 auto = no
 special_use = \Junk
 }
 mailbox Archive {
 auto = no
 special_use = \Archive
 }
 mailbox Archives {
 auto = no
 special_use = \Archive
 }
}

namespace {
 type = shared
 separator = /
 prefix = Shared/%%u/
 location = maildir:%%h:INDEX=%h/shared/%%u
 subscriptions = yes
 list = children
}</pre>


</br>



<p>Создаем группу и пользователя с указанными в конфиге uid 1000.</p>



<pre class="wp-block-preformatted">#&nbsp;groupadd &nbsp;-g 1000 vmail
#&nbsp;useradd -d /mnt/mail/ -g 1000 -u 1000 vmail
# chown vmail. /mnt/mail</pre>



<p>Создаем конфигурационные файлы для доступа к mysql базе.</p>



<pre class="wp-block-preformatted"># mcedit /etc/dovecot/dovecot-mysql.conf</pre>



<pre class="wp-block-preformatted">driver = mysql
default_pass_scheme = CRYPT
connect = host=127.0.0.1 dbname=postfix user=postfix password=12345678
user_query = SELECT '/mnt/mail/%d/%u' as home, 'maildir:/mnt/mail/%d/%u' as mail, 1000 AS uid, 1000 AS gid, concat('*:bytes=', quota) AS quota_rule FROM mailbox WHERE username = '%u' AND active = '1'
password_query = SELECT username as user, password, '/mnt/mail/%d/%u' as userdb_home, 'maildir:/mnt/mail/%d/%u' as userdb_mail, 1000 as userdb_uid, 1000 as userdb_gid, concat('*:bytes=', quota) AS userdb_quota_rule FROM mailbox WHERE username = '%u' AND active = '1'</pre>



<p>Создадим директорию и файлы для логов.</p>



<pre class="wp-block-preformatted"># mkdir /var/log/dovecot
# cd /var/log/dovecot &amp;&amp; touch&nbsp;main.log&nbsp;info.log&nbsp;debug.log&nbsp;lda-errors.log&nbsp;lda-deliver.log&nbsp;lmtp.log
# chown -R&nbsp;vmail:dovecot /var/log/dovecot</pre>



<p>Создаем пару служебных папок для плагинов sieve и acl.</p>



<pre class="wp-block-preformatted"># mkdir&nbsp;/mnt/mail/sieve &amp;&amp; mkdir&nbsp;/mnt/mail/shared-folders
#&nbsp;chown -R vmail. /mnt/mail</pre>



<p>И небольшой штрих в завершении настройки.</p>



<pre class="wp-block-preformatted"># chown vmail.&nbsp;/var/run/dovecot/auth-master</pre>



<p>Уже не помню, зачем это было нужно, запись осталась в черновиках. Знаю только, что какая-то ошибка всплывала без этого.</p>



<p>На этом основная настройка почтового сервера на базе postfix и dovecot завершена. Можно перезапускать службы и проверять работу системы.</p>



<pre class="wp-block-preformatted"># systemctl restart postfix
# systemctl start dovecot
# systemctl enable dovecot</pre>



<h2 class="wp-block-heading">Проверка работы почтового сервера</h2>



<p>Самаый простой и быстрый способ проверить работу почтового сервера — отправить на него письмо. Я буду отправлять со своего почтового адреса zeroxzed@gmail.com на адрес root@zeroxzed.ru. Вот что должно быть в логе, если у вас все правильно настроено и почтовый сервер нормально работает.</p>



<pre class="wp-block-preformatted"># cat /var/log/maillog</pre>



<pre class="wp-block-preformatted">Mar 10 21:56:27 mail postfix/smtpd[28075]: connect from mail-yw0-f172.google.com[209.85.161.172]
Mar 10 21:56:28 mail postfix/smtpd[28075]: Anonymous TLS connection established from mail-yw0-f172.google.com[209.85.161.172]: TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)
Mar 10 21:56:28 mail postfix/smtpd[28075]: D4263420BB7B: client=mail-yw0-f172.google.com[209.85.161.172]
Mar 10 21:56:29 mail postfix/cleanup[28086]: D4263420BB7B: message-id=&lt;CAHWPLcOeqf6uNHRg34+wuppDUGPDLY=fp8s-E=o9fmxYMS48cQ@mail.gmail.com&gt;
Mar 10 21:56:29 mail postfix/qmgr[28042]: D4263420BB7B: from=&lt;zeroxzed@gmail.com&gt;, size=2533, nrcpt=2 (queue active)
Mar 10 21:56:29 mail postfix/pipe[28089]: D4263420BB7B: to=&lt;all_in@zeroxzed.ru&gt;, relay=dovecot, delay=0.39, delays=0.33/0.02/0/0.05, dsn=2.0.0, status=sent (delivered via dovecot service)
Mar 10 21:56:29 mail postfix/pipe[28090]: D4263420BB7B: to=&lt;root@zeroxzed.ru&gt;, relay=dovecot, delay=0.4, delays=0.33/0.03/0/0.04, dsn=2.0.0, status=sent (delivered via dovecot service)
Mar 10 21:56:29 mail postfix/qmgr[28042]: D4263420BB7B: removed
Mar 10 21:56:29 mail postfix/smtpd[28075]: disconnect from mail-yw0-f172.google.com[209.85.161.172]</pre>



<p>Пояснять тут нечего, по логу все понятно. Письмо было доставлено в указанный ящик и в общий ящик для сбора всей входящей почты. В директории&nbsp;<em>/mnt/mail</em>&nbsp;была создана директория с именем домена zeroxzed.ru, а в ней созданы 3 папки с именами ящиков:</p>



<ul><li>all_in@zeroxzed.ru</li><li>all_out@zeroxzed.ru</li><li>root@zeroxzed.ru</li></ul>



<p>Директории с почтовыми ящиками создаются в момент получения первого письма в ящик. Непрочитанное письмо помещается в директорию /new в почтовом ящике. После прочтения переносится в /cur.</p>



<p>Попробуем теперь подключиться к почтовому ящику по imap, прочитать письмо и отправить ответ. Настроим любой&nbsp;почтовый клиент для проверки работы настроенного почтового сервера. Я для этих целей буду использовать Thunderbird. Из всех почтовых клиентов мне он нравится больше всего. В основном из-за его портированной версии. Указываем следующие настройки.</p>



<div class="wp-block-image"><figure class="aligncenter"><a href="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-11.png"><img decoding="async" src="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-11.png" alt="Настройка подключения к почтовому серверу" class="wp-image-5038"/></a></figure></div>



<p>Так как мы используем самоподписанный сертификат ssl, почтовый клиент предупредит нас о том, что серверу нельзя доверять.</p>



<div class="wp-block-image"><figure class="aligncenter"><img decoding="async" src="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-12.png" alt="Добавление ssl сертификата в исключения" class="wp-image-5039"/></figure></div>



<p>Нас это не пугает, добавляем сертификат в список доверенных и продолжаем работать. Позже получим и настроим нормальный сертификат.</p>



<p>Я подключился к почтовому ящику и увидел тестовые письма. Отвечу на одно из них и посмотрю в логе, как прошла отправка. У меня еще раз выскочило окно с предупреждением о небезопасном сертификате. Еще раз добавляю его в исключения. Это нормально, сертификат проверяется во время получения почты в dovecot, а во время отправки в postfix. Так что нужны 2 подтверждения. Отправляю письмо еще раз и смотрю лог.</p>



<pre class="wp-block-preformatted"># cat /var/log/maillog</pre>


</br>



<pre class="wp-block-preformatted">Mar 10 22:10:12 mail postfix/smtpd[28764]: connect from broadband-75-37-235-139.moscow.gw.ru[75.37.235.139]
Mar 10 22:10:12 mail postfix/smtpd[28764]: Anonymous TLS connection established from broadband-75-37-235-139.moscow.gw.ru[75.37.235.139]: TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)
Mar 10 22:10:12 mail postfix/smtpd[28764]: B24C2420BB70: client=broadband-75-37-235-139.moscow.gw.ru[75.37.235.139], sasl_method=PLAIN, sasl_username=root@zeroxzed.ru
Mar 10 22:10:12 mail postfix/cleanup[28779]: B24C2420BB70: message-id=&lt;aaac96c3-197e-c6bd-4dfe-85d09bce216a@zeroxzed.ru&gt;
Mar 10 22:10:12 mail postfix/qmgr[28042]: B24C2420BB70: from=&lt;root@zeroxzed.ru&gt;, size=955, nrcpt=2 (queue active)
Mar 10 22:10:12 mail postfix/smtpd[28764]: disconnect from broadband-75-37-235-139.moscow.gw.ru[75.37.235.139]
Mar 10 22:10:12 mail postfix/pipe[28784]: B24C2420BB70: to=&lt;all_out@zeroxzed.ru&gt;, relay=dovecot, delay=0.14, delays=0.07/0.01/0/0.06, dsn=2.0.0, status=sent (delivered via dovecot service)
Mar 10 22:10:13 mail postfix/smtp[28783]: B24C2420BB70: to=&lt;zeroxzed@gmail.com&gt;, relay=gmail-smtp-in.l.google.com[64.233.163.26]:25, delay=0.62, delays=0.07/0.01/0.28/0.26, dsn=2.0.0, status=sent (250 2.0.0 OK 1489173013 13si2106703ljv.3 - gsmtp)
Mar 10 22:10:13 mail postfix/qmgr[28042]: B24C2420BB70: removed
</pre>



<p>Все в порядке. Видно подключение с моего ip, успешную sasl авторизацию, формирование письма на сервере, присваивание ему message-id, отправка копии письма в ящик для сбора исходящей почты и отправка оригинала в ящик получателя. Все этапы прошли без ошибок.</p>



<p>Расскажу, куда еще надо смотреть для отладки почтовой системы. Да и не только отладки, во время работы периодически придется разбираться, куда ушло то или иное письмо, кто и когда подключался к ящику. Разные ситуации бывают. В файле&nbsp;<em>/var/log/dovecot/lda-deliver.log</em>&nbsp;содержится информация обо всех пришедших письмах — когда, от кого и в какой ящик было положено.</p>



<pre class="wp-block-preformatted">Mar 10 22:25:29 lda(all_in@zeroxzed.ru): Info: mail from zeroxzed@gmail.com: msgid=&lt;CAHWPLcNG=WMOoWW2Y_Lw4qn9+V4TOrbxZpwtA=O+CSEBaiwuBg@mail.gmail.com&gt; saved mail to INBOX
Mar 10 22:25:29 lda(root@zeroxzed.ru): Info: mail from zeroxzed@gmail.com: msgid=&lt;CAHWPLcNG=WMOoWW2Y_Lw4qn9+V4TOrbxZpwtA=O+CSEBaiwuBg@mail.gmail.com&gt; saved mail to INBOX
Mar 10 22:25:49 lda(all_out@zeroxzed.ru): Info: mail from root@zeroxzed.ru: msgid=&lt;75358e4d-7c8e-24c2-a21f-7ee0df2a4704@zeroxzed.ru&gt; saved mail to INBOX</pre>



<p>В&nbsp;<em>/var/log/dovecot/info.log</em>&nbsp;информация о подключениях к почтовым ящикам — кто, когда, откуда и каким способом авторизовывался на сервере.</p>



<pre class="wp-block-preformatted">Mar 10 22:10:20 imap-login: Info: Login: user=&lt;root@zeroxzed.ru&gt;, method=PLAIN, rip=75.37.235.139, lip=188.35.19.125, mpid=28790, TLS, session=&lt;3tDeHGVKpQBNJeCL&gt;
Mar 10 22:19:39 imap-login: Info: Login: user=&lt;root@zeroxzed.ru&gt;, method=PLAIN, rip=75.37.235.139, lip=188.35.19.125, mpid=29248, TLS, session=&lt;7VY8PmVKbwBNJeCL&gt;</pre>



<p>Остальное уже не так полезно. Сами посмотрите, что собирается в остальных лог файлах.</p>



<p>На текущий момент сервер полностью работоспособен. В таком виде им без проблем можно пользоваться. Но функционал полностью не раскрыт. Использовать плагины sieve и acl через удаленные почтовые клиенты неудобно. Проще всего их настроить через web почту&nbsp;roundcube. Установим эту web панель на наш почтовый сервер.</p>



<h2 class="wp-block-heading">Установка web интерфейса roundcube</h2>



<p>Установим и настроим самый популярный web интерфейс для postfix — roundcube. Скачиваем исходники.</p>



<pre class="wp-block-preformatted"># cd /usr/src
# wget&nbsp;https://github.com/roundcube/roundcubemail/releases/download/1.2.9/roundcubemail-1.2.3-complete.tar.gz</pre>



<p>Не забудьте проверить в момент установки, какая версия является самой свежей на текущий момент. Нет необходимости устанавливать устаревшую версию. Рекомендую ставить самую последнюю на момент настройки.</p>



<pre class="wp-block-preformatted"># yum install php-pear php-mcrypt php-intl php-ldap php-pear-Net-SMTP php-pear-Net-IDNA2 php-pear-Mail-Mime php-pear-Net-Sieve
# tar -xzvf roundcubemail-*
# mv&nbsp;roundcubemail-1.2.9 /var/www/html/webmail
# chown -R apache.&nbsp;/var/www/html/webmail</pre>



<p>Переходим в браузер по следующей ссылке для установки roundcube —&nbsp;<em>http://188.35.19.125/webmail/installer/</em>. Вы увидите несколько незначительных замечаний. На них можно не обращать внимание, если установщик позволяет нажать кнопку NEXT. Единственное, рекомендую установить правильный часовой пояс в&nbsp;<em>/etc/php.ini</em>&nbsp;и перезапустить после этого httpd.</p>



<p>На следующем этапе нам надо указать настройки подключения к mysql базе. Предварительно ее следует создать через phpmyadmin. Я создал пользователя roundcube и такую же базу с полными правами пользователя на нее. Эти параметры указал в настройках.</p>



<div class="wp-block-image"><figure class="aligncenter"><img decoding="async" src="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-13.png" alt="Установка roundcube" class="wp-image-5040"/></figure></div>



<p>Так же на этой странице нужно будет указать несколько параметров:</p>



<ul><li>smtp_server —&nbsp;пусто (ничего не пишем)</li><li>language — ru_RU</li><li>Выбираем плагины —&nbsp;managesieve, userinfo, acl. Остальные на свое усмотрение.</li></ul>



<p>Жмем CREATE CONFIG. Должны увидеть сообщение:</p>



<pre class="wp-block-preformatted">The config file was saved successfully into RCMAIL_CONFIG_DIR directory of your Roundcube installation.</pre>



<p>Жмем CONTINUE. Открывается страница с проверкой настроек. Тут проверять неудобно, можно этого не делать. Зайдем в почтовый ящик и там все проверим. Если что, конфиг потом все равно можно вручную отредактировать. Папку&nbsp;<em>/var/www/html/webmail/installer</em>&nbsp;удаляем. Заходим в почтовый ящик через roundcube —&nbsp;<em>http://188.35.19.125/webmail/</em>&nbsp;Набирать нужно полное имя ящика и пароль. Если все сделали правильно, должны попасть в свой почтовый ящик.</p>



<div class="wp-block-image"><figure class="aligncenter"><a href="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-14.png"><img decoding="async" src="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-14.png" alt="Вход в почтовый ящик через roundcube" class="wp-image-5041"/></a></figure></div>



<p>Ну вот и все. Можно пользоваться web интерфейсом для почтового сервера. Рекомендую ее настроить, если есть необходимость. Тема качественная и добротная. Пользоваться удобно.</p>



<p>Дальше рассмотрим настройку и использование плагинов acl и sieve с помощью roundcube.</p>



<h2 class="wp-block-heading">Настройка фильтра почты sieve</h2>



<p>Sieve очень удобная штука, но вот хорошего интерфейса для управления через почтовый клиент я не знаю. Существует плагин для thunderbird, который так и называется sieve. Но лично мне он не понравился вообще, так как предлагает писать правила определенным кодом. Для этого надо знать синтаксис, тратить время. Можете сами на него посмотреть —&nbsp;<a href="https://github.com/thsmi/sieve" target="_blank" rel="noreferrer noopener">https://github.com/thsmi/sieve</a>.</p>



<p>К счастью, есть удобный способ писать правила фильтрации для sieve через roundcube. Там это реализовано отдельным плагином&nbsp;<strong>managesieve</strong>, который мы активировали во время установки. Для создания правила фильтрации, зайдите в свой почтовый ящик через&nbsp;roundcube. Переходите в раздел&nbsp;Настройки -&gt; Фильтры&nbsp;и создавайте новое правило.</p>



<div class="wp-block-image"><figure class="aligncenter"><a href="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-15.png"><img decoding="async" src="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-15.png" alt="Настройка фильтра почты в roundcube" class="wp-image-5042"/></a></figure></div>



<p>После этого письма будут обрабатываться правилом сразу после поступления в почтовый сервер, в независимости от вашего подключения к ящику. В папке&nbsp;<em>/mnt/mail/sieve</em>&nbsp;появилась запись с настроенным правилом. Можете познакомиться с синтаксисом написания правил. Он не сложный.</p>



<h2 class="wp-block-heading">Настройка&nbsp;автоответчика</h2>



<p>В roundcube есть замечательная возможность настроить автоответчик в почтовом ящике. Это актуально, к примеру, если вы уходите в отпуск. Вы можете сами настроить автоответчик, который будет отправлять письмо с указанной вами информацией всем, от кого будут приходить письма в ваш ящик. Возможность эта реализована на базе того же плагина&nbsp;managesieve. По-умолчанию она отключена. Активировать ее нужно вручную.</p>



<p>Для того, чтобы модуль автоответчика заработал, отредактируйте конфигурационный файл плагина. Для этого открываем его в моем случае по следующему адресу:</p>



<pre class="wp-block-preformatted"># mcedit /var/www/html/webmail/plugins/managesieve/config.inc.php</pre>



<p>Изменяем там параметр:</p>



<pre class="wp-block-preformatted">$config['managesieve_vacation'] = 1;</pre>



<p>После этого достаточно просто обновить веб интерфейс roundcube, и появятся новые настройки по адресу&nbsp;Настройки -&gt; Отпуск</p>



<div class="wp-block-image"><figure class="aligncenter"><a href="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-16.png"><img decoding="async" src="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-16.png" alt="Настройка автоответчика" class="wp-image-5043"/></a></figure></div>



<p>На вкладке&nbsp;Дополнительные настройки&nbsp;есть возможность настроить различные полезные действия, в том числе пересылку входящей почты вашему заместителю.</p>


</br>



<h2 class="wp-block-heading">Общие папки по imap</h2>



<p>Рассмотрим настройку необычного и полезного функционала в виде общих папок. С их помощью один пользователь почтового ящика может предоставить другому пользователю доступ к папке внутри своего почтового ящика. Где и как использовать этот функционал, каждый может придумать сам, в зависимости от своих потребностей. Мне кажется это удобным в том случае, когда создан какой-то общий ящик, на который только поступает информация и нет необходимости писать ответ от его имени. То есть по сути работает как обычный почтовый алиас. Но в случае с алиасом и несколькими почтовыми ящиками, письмо падает в каждый ящик. Если таких писем и получателей много, то идет большое дублирование одного и того же письма в рамках почтового сервера. Если сделать ящик и расшарить на нем папку, подключить ее всем пользователям, то дублирования почты не будет. Каждый сможет прочитать письмо, без необходимости его доставки в каждый конкретный ящик.</p>



<p>Настроим общую папку imap. Хотя настраивать нам, по сути, нечего. Мы уже все настроили ранее. Добавили соответствующие настройки в dovecot и активировали плагин&nbsp;<strong>acl</strong>&nbsp;в roundcube. Теперь нужно просто сделать папку и открыть ее для другого пользователя. Для этого идем в раздел&nbsp;Настройки -&gt; Папки. Создаем там любую папку. В моем случае это папка с названием Общая.</p>



<div class="wp-block-image"><figure class="aligncenter"><a href="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-17.png"><img decoding="async" src="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-17.png" alt="Настройка общей папки imap" class="wp-image-5044"/></a></figure></div>



<p>Добавляем необходимый доступ либо всем ящикам в домене, либо какому-то конкретному. Так же можно указать, какого рода это будет доступ:</p>



<ul><li>чтение</li><li>запись</li><li>удаление</li></ul>



<div class="wp-block-image"><figure class="aligncenter"><img decoding="async" src="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-18.png" alt="Права доступа на общую папку imap" class="wp-image-5045"/></figure></div>



<p>Заходим в ящик, которому добавили общий доступ и проверяем.</p>



<div class="wp-block-image"><figure class="aligncenter"><img decoding="async" src="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-19.png" alt="Проверка общей папки" class="wp-image-5046"/></figure></div>



<p>Все в порядке, общая папка imap настроена и подключена. В папке&nbsp;<em>/mnt/mail/shared-folders</em>&nbsp;появился файл с настроенным выше правилом.</p>



<p>На этом настройка пользовательского функционала закончена. В принципе, почтовый сервер полностью готов к работе. Но мы сделаем еще несколько полезных настроек на стороне сервера.</p>



<h2 class="wp-block-heading">Настройка dkim и spf</h2>



<p>Напишу своими словами как я понимаю работу&nbsp;<strong>dkim</strong>. С помощью dkim&nbsp;вся исходящая почта сервера подписывается электронной цифровой подписью, связанной с именем домена. Открытый ключ шифрования с помощью DNS публикуется в txt записи. Таким образом, удаленный сервер, при получении письма от вас, сравнивает цифровую подпись с опубликованным в dns открытым ключом вашего домена. Если все в порядке, то считает, что ваше письмо в самом деле пришло от вас, а не от мошенников. То есть с помощью этой технологии можно однозначно идентифицировать отправителя.</p>



<p>Некоторые считают, что эта технология помогает бороться со спамом. Не знаю, насколько это верно. Спамеру не составит большого труда настроить на своем сервере dkim и отправлять спам, но подписанный электронной цифровой подписью. Теоретически, dkim помогает защититься от подделки адреса отправителя, когда письмо якобы от вас шлет совсем другой сервер. Но с этим можно бороться и другими способами. В общем, я до конца не понимаю, зачем это надо. Прошу поделиться в комментариях тем, к кого есть бОльший опыт. Я только недавно стал шагать в ногу со временем и настраивать dkim. Раньше всегда без него обходился.Плюс настройки dkim я вижу только в том, что автоматические фильтры определения спама будут добавлять больше траста письмам с корректной dkim подписью. У вас будет больше шанса не попасть в спам при прочих равных условиях. В принципе, ради этого можно немного заморочиться.</p>



<p>Для настройки dkim устанавливаем соответствующий пакет:</p>



<pre class="wp-block-preformatted"># yum install opendkim</pre>



<p>Создаем директорию для хранения ключей:</p>



<pre class="wp-block-preformatted"># mkdir -p /etc/postfix/dkim &amp;&amp;&nbsp;cd /etc/postfix/dkim</pre>



<p>Генерируем ключи для домена:</p>



<pre class="wp-block-preformatted"># opendkim-genkey -D /etc/postfix/dkim/ -d zeroxzed.ru -s mail</pre>



<figure class="wp-block-table"><table><tbody><tr><td>zeroxzed.ru</td><td>имя почтового домена</td></tr><tr><td>mail</td><td>непосредственно имя сервера</td></tr></tbody></table></figure>



<p>На выходе получаете пару файлов — закрытый (приватный) и открытый ключ. Закрытый останется на сервере, открытый будет опубликован в dns. Переименуем их сразу, чтобы не путаться, если у вас будет несколько доменов. Ключи нужно будет делать для каждого домена.</p>



<pre class="wp-block-preformatted"># mv mail.private mail.zeroxzed.ru.private
# mv mail.txt mail.zeroxzed.ru.txt</pre>



<p>Создаем файл с таблицей ключей, в которой будут описаны все домены. В данном случае только один.</p>



<pre class="wp-block-preformatted"># mcedit&nbsp;keytable</pre>



<pre class="wp-block-preformatted">mail._domainkey.zeroxzed.ru zeroxzed.ru:mail:/etc/postfix/dkim/mail.zeroxzed.ru.private</pre>



<p>Тут же создаем еще один файл, в котором будет описано, каким ключом подписывать письма каждого домена. У нас один домен, поэтому только одна запись.</p>



<pre class="wp-block-preformatted"># mcedit&nbsp;signingtable</pre>



<pre class="wp-block-preformatted">*@zeroxzed.ru mail._domainkey.zeroxzed.ru</pre>



<p>Выставляем права доступа на все файлы:</p>



<pre class="wp-block-preformatted"># chown root:opendkim *
# chmod u=rw,g=r,o= *</pre>



<p>Рисуем конфиг службы.</p>



<pre class="wp-block-preformatted"># mcedit&nbsp;/etc/opendkim.conf</pre>



<pre class="wp-block-preformatted">AutoRestart Yes
AutoRestartRate 10/1h
PidFile /var/run/opendkim/opendkim.pid
Mode sv
Syslog yes
SyslogSuccess yes
LogWhy yes
UserID opendkim:opendkim
Socket inet:8891@localhost
Umask 022
Canonicalization relaxed/relaxed
Selector default
MinimumKeyBits 1024
KeyFile /etc/postfix/dkim/mail.zeroxzed.ru.private
KeyTable /etc/postfix/dkim/keytable
SigningTable refile:/etc/postfix/dkim/signingtable</pre>


</br>



<p>Добавляем в конфигурационный файл postfix в самый конец следующие параметры:</p>



<pre class="wp-block-preformatted"># mcedit /etc/postfix/main.cf</pre>



<pre class="wp-block-preformatted">smtpd_milters = inet:127.0.0.1:8891
non_smtpd_milters = $smtpd_milters
milter_default_action = accept
milter_protocol = 2</pre>



<p>Перезапускаем postfix и dkim, последний добавляем в автозагрузку.</p>



<pre class="wp-block-preformatted"># systemctl restart&nbsp;postfix
# systemctl restart opendkim.service
# systemctl enable opendkim.service</pre>



<p>Теперь нам надо добавить открытый ключ в dns. Идем в консоль управления dns и добавляем новую txt запись. Ее содержание берем из файла&nbsp;<em>/etc/postfix/dkim/mail.zeroxzed.ru.txt</em></p>



<pre class="wp-block-preformatted">cat /etc/postfix/dkim/mail.zeroxzed.ru.txt</pre>



<pre class="wp-block-preformatted">mail._domainkey IN TXT ( "v=DKIM1; k=rsa; "
 "p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQClZX2xWRDISlVLF4b4pUiinY5N9WN7VXEHeyPw8smHTamXh35wJoh+j0+MIQDWG/KtdCcETeawTuypXbvtbneXniYR0iiv6kt754T2WXBjz7O/uHL+vK58LhJsm4TGyhUN6ZBit+w22jG92zdeybSZeU/g7hQdkaAAi0I+0nIkUwIDAQAB" ) ; – - – DKIM key mail for zeroxzed.ru</pre>



<p>Убираем кавычки, лишние проблемы и вставляем. Должно получиться вот так:</p>



<div class="wp-block-image"><figure class="aligncenter"><img decoding="async" src="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-20.png" alt="Настройка dkim записи в dns" class="wp-image-5047"/></figure></div>



<p>Проверяю работу. Отправляю письмо на gmail и смотрю лог почтового сервера:</p>



<pre class="wp-block-preformatted"># cat&nbsp;/var/log/maillog</pre>



<pre class="wp-block-preformatted">Mar 17 17:40:26 mail postfix/smtpd[22352]: connect from localhost[127.0.0.1]
Mar 17 17:40:26 mail postfix/smtpd[22352]: BB1794195584: client=localhost[127.0.0.1]
Mar 17 17:40:26 mail postfix/cleanup[22364]: BB1794195584: message-id=&lt;baf63dcec016594d49f2d80f815e5d26@zeroxzed.ru&gt;
Mar 17 17:40:26 mail opendkim[21744]: BB1794195584: <strong>DKIM-Signature field added (s=mail, d=zeroxzed.ru)</strong>
Mar 17 17:40:26 mail postfix/qmgr[21990]: BB1794195584: from=&lt;root@zeroxzed.ru&gt;, size=593, nrcpt=2 (queue active)
Mar 17 17:40:26 mail postfix/pipe[22369]: BB1794195584: to=&lt;all_out@zeroxzed.ru&gt;, relay=dovecot, delay=0.14, delays=0.11/0.02/0/0.02, dsn=2.0.0, status=sent (delivered via dovecot service)
Mar 17 17:40:26 mail postfix/smtpd[22352]: disconnect from localhost[127.0.0.1]
Mar 17 17:40:27 mail postfix/smtp[22370]: BB1794195584: to=&lt;zeroxzed@gmail.com&gt;, relay=gmail-smtp-in.l.google.com[64.233.163.26]:25, delay=0.84, delays=0.11/0.02/0.31/0.4, dsn=2.0.0, status=sent (250 2.0.0 OK 1489761627 185si4568435lfa.398 - gsmtp)</pre>



<p>Все в порядке, электронная цифровая подпись установлена. Проверим, как гугл отреагировал на нашу подпись:</p>



<div class="wp-block-image"><figure class="aligncenter"><a href="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-21.png"><img decoding="async" src="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-21.png" alt="Проверка dkim" class="wp-image-5048"/></a></figure></div>



<p>Тоже все в порядке. Подпись выполнена корректно, проверку прошла. Дополнительно, проверить корректность dkim записи в dns можно онлайн сервисом —&nbsp;<a href="http://dkimcore.org/c/keycheck" target="_blank" rel="noreferrer noopener">http://dkimcore.org/c/keycheck</a>.</p>



<p>Настроим еще одно средство для повышения доверия к нашей почте со стороны других серверов —&nbsp;<strong>spf</strong>. Расскажу опять своими словами для чего это нужно. Spf запись добавляется в виде txt записи в dns вашего домена. С помощью этой записи вы указываете, какие ip адреса имеют право отправлять почту от вашего имени. Если кто-то из спамеров будет использовать ваше имя домена при рассылке спама, он не пройдет проверку по spf и скорее всего будет идентифицирован как спам.</p>



<p>Можно указать конкретные ip адреса в записи, а можно сказать, чтобы ip адреса проверялись по спискам A и MX записей. У нас простой случай и только 1 сервер с одним ip, поэтому укажем конкретно этот ip адрес. Идем в панель управления dns и добавляем новую txt запись.</p>



<pre class="wp-block-preformatted">zeroxzed.ru. TXT v=spf1 ip4:188.35.19.125 ~all</pre>



<div class="wp-block-image"><figure class="aligncenter"><img decoding="async" src="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-22.png" alt="Добавление spf записи в dns" class="wp-image-5049"/></figure></div>



<p>Больше ничего делать не надо. Снова отправляем письмо на gmail и смотрим логи.</p>



<div class="wp-block-image"><figure class="aligncenter"><a href="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-23.png"><img decoding="async" src="https://serveradmin.ru/wp-content/uploads/2017/03/centos-postfix-mail-server-23.png" alt="Проверка spf записи" class="wp-image-5050"/></a></figure></div>



<p>Обращаю внимание на прошлый скрин, когда мы проверяли dkim и еще не настроили spf, и этот. Видно, что запись работает, гугл определил наш ip, как доверенный для отправки писем с этого домена.</p>


</br>



<h2 class="wp-block-heading">Дополнительный функционал почтового сервера postfix</h2>



<p>Я рассмотрел и настроил наиболее актуальный с моей точки зрения функционал почтового сервера. В статье я основывался исключительно на своем опыте работы с почтой в малых и средних организациях, поэтому не претендую на истину в последней инстанции. Рекомендую осмысленно подходить к настройке своего сервера и решать, что нужно именно вам.&nbsp;Будет хорошо, если кто-то укажет на мои ошибки или подскажет какие-то более удобные и эффективные приемы для решения затронутых задач.</p>



<p>В данном виде почтовый сервер представляет собой готовое и законченное решение, но есть еще несколько вещей, которые ему бы не помешали. Я их сейчас перечислю, а затем постараюсь постепенно писать статьи на указанные темы и ставить на них ссылки в этой теме. Вот список того, что по моему мнению нужно еще настроить на почтовом сервере:</p>



<ol><li>Защиту от подбора паролей с помощью fail2ban.</li><li>Мониторинг почтового сервера postfix с помощью zabbix.</li><li>Сбор статистики с помощью pflogsumm или чего-то подобного.</li><li>Просмотр и анализ логов с помощью&nbsp;webmin.</li><li>Использование бесплатных сертификатов let’s encrypt.</li><li>Регулярную очистку служебных почтовых ящиков.</li><li>Бэкап всей почтовой базы.</li></ol>



<p>Расскажу еще почему я не настраиваю некоторые популярные программы, которые использую на почтовых серверах:</p>



<ol><li>lamav — известный антивирус. Считаю, что сейчас он не актуален, так как вирусов, от которых он способен защитить, я уже давно не видел. Сейчас вирусная эпидемия шифровальщиков. От них он не защищает.</li><li>Spamassasin — популярный бесплатный антиспам фильтр. Скажу честно, работал с ним очень мало и могу быть не объективен. Насколько я видел его настройку и работу — он требует к себе некоторого внимания, калибровки, особенно на начальном этапе. Мне обычно не хочется этим заниматься.</li><li>Graylist — эффективное средство борьбы со спамом. Я уже подробно его рассматривал, когда писал про&nbsp;<a href="https://clip-clap.ru/it/%d1%81%d0%be%d1%84%d1%82-%d0%b8-%d0%be%d1%81/linux/%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%be%d0%b9%d0%ba%d0%b0-%d0%bf%d0%be%d1%87%d1%82%d0%be%d0%b2%d0%be%d0%b3%d0%be-%d1%81%d0%b5%d1%80%d0%b2%d0%b5%d1%80%d0%b0-iredmail-%d0%bd%d0%b0-centos-7/" target="_blank" aria-label="undefined (откроется в новой вкладке)" rel="noreferrer noopener">iredmail</a>, так что не буду повторяться. Скажу лишь, что режет спам очень эффективно и бесплатно, но есть существенные неудобства, которые по моему мнению не перекрывают плюсы. Поэтому я не использую.</li></ol>



<p>В качестве антиспама я предпочитаю коммерческое решение — Kaspersky Anti-Spam. Я знаю этот продукт уже лет 8. Он действительно отлично фильтрует спам. Ложных срабатываний вообще не припоминаю, 95% спама фильтрует, может больше. С ним вопрос спама отпадает вообще. Стоит он недорого, можно купить лицензию на меньшее количество ящиков, чем реально используется в системе. Этот вопрос никак не отслеживается и на качество работы не влияет. Но нужно понимать, что это уже нарушение лицензионного соглашения. Но можно всякие хитрости придумать, чтобы и фильтровать и не нарушать.</p>



<h2 class="wp-block-heading">Борьба со спамом средствами postfix</h2>



<p>Сначала хотел сразу все настройки postfix разместить в соответствующем разделе в едином конфиге, но потом передумал и решил все же вынести этот вопрос на отдельное рассмотрение. Возможно, не каждому захочется сразу в эту тему углубляться. Все, что рассказано выше, позволит настроить полноценный почтовый сервер, который будет успешно принимать почту и доставлять ее пользователям. Но в таком виде он будет принимать слишком много спама, но зато не будет проблем с тем, что от кого-то что-то не придет. Как ни крути, но все средства борьбы со спамом так или иначе несут накладные расходы в виде ложных срабатываний с той или иной вероятностью. Если вы решите не заморачиваться и купить&nbsp;Kaspersky Anti-Spam, можете этот раздел не читать. Он сам реализует все те проверки, что мы будем делать. Если же хотите своими силами бороться со спамом средствами postfix, то давайте дальше разбираться.</p>



<p>Я буду использовать штатные возможности postfix, позволяющие отсеять спам по тем или иным параметрам еще до получения письма. Это очень эффективный способ с точки зрения производительности. Благодаря этому, правильно настроенный на отсев спама postfix часто ставят перед exchange, чтобы снизить на него нагрузку. Сразу дам ссылки на официальную документацию с описанием параметров, которые я буду использовать:</p>



<ol><li><a href="http://www.postfix.org/postconf.5.html#smtpd_helo_restrictions" target="_blank" rel="noreferrer noopener">smtpd_helo_restrictions</a></li><li><a href="http://www.postfix.org/postconf.5.html#smtpd_sender_restrictions" target="_blank" rel="noreferrer noopener">smtpd_sender_restrictions</a></li><li><a href="http://www.postfix.org/postconf.5.html#smtpd_recipient_restrictions" target="_blank" rel="noreferrer noopener">smtpd_recipient_restrictions</a></li><li><a href="http://www.postfix.org/postconf.5.html#smtpd_data_restrictions" target="_blank" rel="noreferrer noopener">smtpd_data_restrictions</a></li><li><a href="http://www.postfix.org/postconf.5.html#smtpd_client_restrictions" target="_blank" rel="noreferrer noopener">smtpd_client_restrictions</a></li></ol>



<p>Есть еще парочка —&nbsp;smtpd_etrn_restrictions и smtpd_end_of_data_restrictions, но я ими не пользуюсь.Обращаю внимание на то, что нужно очень аккуратно работать с настройками, о которых пойдет речь. Нужно четко понимать как, зачем и что вы делаете. Неверные настройки могут нарушить нормальное хождение почты. Нужно уметь анализировать лог файл почтового сервера и понимать, что там происходит.</p>



<p>Долго думал, как лучше всего представить информацию по этому разделу. В итоге решил просто написать часть конфига, которая отвечает за&nbsp;restrictions с комментариями. Более подробную информацию по каждой настройке вы можете найти в официальной документации postfix, ссылки я привел выше, либо в гугле. Данные настройки это моя многолетняя калькуляция различных параметров, собранных из черновиков и рабочих серверов. Не везде все было настроено именно в таком виде, так как ситуации бывают разные. Сейчас я постарался все собрать в одном месте и аккуратно описать. Те проверки в борьбе со спамом, что вам не нужны, просто закомментируйте. В конце я еще пройдусь по некоторым из них и поделюсь своим опытом.</p>



<pre class="wp-block-preformatted">#Описание списков исключений
smtpd_restriction_classes = white_client_ip,
                            black_client_ip,
                            block_dsl,
                            white_client,
                            white_helo,
                            black_client,
                            mx_access

# IP адреса, которые нужно пропускать всегда
white_client_ip		= check_client_access hash:/etc/postfix/lists/white_client_ip

# IP адреса, которые нужно блокировать всегда
black_client_ip		= check_client_access hash:/etc/postfix/lists/black_client_ip

# E-mail, которые нужно пропускать всегда
white_client		= check_sender_access hash:/etc/postfix/lists/white_client

# E-mail, которые нужно блокировать всегда
black_client		= check_sender_access hash:/etc/postfix/lists/black_client

# Неправильные значения HELO, которые мы тем не менее принимаем
white_helo = check_sender_access hash:/etc/postfix/lists/white_helo
# Правила для блокировки различных динамических ip.
block_dsl 		= check_client_access regexp:/etc/postfix/lists/block_dsl

# Список приватных сетей, которые не могут быть использованы в качестве IP для MX записей
mx_access		= check_sender_mx_access cidr:/etc/postfix/lists/mx_access

# Проверки на основе данных, переданных в HELO/EHLO hostname
smtpd_helo_restrictions =	permit_mynetworks,
		permit_sasl_authenticated,
		white_client_ip,
                white_helo,
		black_client_ip,
		block_dsl,
		# Отказываем серверам, у которых в HELO несуществующий или не FQDN адрес 
		reject_invalid_helo_hostname,
		reject_non_fqdn_helo_hostname,
		# Запрещаем приём писем от серверов, представляющихся адресом, для которого не существует A или MX записи.
		reject_unknown_helo_hostname

# Проверки клиентского компьютера или другого почтового сервера, который соединяется с сервером postfix для отправки письма
smtpd_client_restrictions = 	permit_mynetworks,
		permit_sasl_authenticated,
		# Отвергает запрос, когда клиент отправляет команды SMTP раньше времени, еще не зная, поддерживает ли Postfix конвейерную обработку команд ESMTP
		reject_unauth_pipelining,
		# Блокируем клиентов с адресами from, домены которых не имеют A/MX записей
		reject_unknown_address,
		reject_unknown_client_hostname

# Проверки исходящей или пересылаемой через нас почты на основе данных MAIL FROM
smtpd_sender_restrictions =	permit_mynetworks,
		permit_sasl_authenticated,
		white_client,
		black_client,
		# Запрет отправки писем, когда адрес MAIL FROM не совпадает с логином пользователя
		reject_authenticated_sender_login_mismatch,
		# Отклоняем письма от несуществующих доменов
		reject_unknown_sender_domain,
		# Отклоняем письма от доменов в не FQDN формате
		reject_non_fqdn_sender,
		# Отклонение писем с несуществующим адресом отправителя
		reject_unlisted_sender,
		reject_unauth_destination,
		# Отклонять сообщения от отправителей, ящики которых не существуют, использовать аккуратно
		#reject_unverified_sender,
		mx_access

# Правила приема почты нашим сервером на основе данных RCPT TO
smtpd_recipient_restrictions =  permit_mynetworks,
		permit_sasl_authenticated,
		# Отклоняет всю почту, что адресована не для наших доменов
		reject_unauth_destination,
		# Отклонение писем с несуществующим адресом получателя
		reject_unlisted_recipient,
		# Отклоняет сообщения на несуществующие домены
		reject_unknown_recipient_domain,
		# Отклоняет сообщения если получатель не в формате FQDN
		reject_non_fqdn_recipient,
		# Отклоняем прием от отправителя с пустым адресом письма, предназначенным нескольким получателям.
		reject_multi_recipient_bounce
</pre>



<p>У меня во всех ограничениях первыми правилами стоят разрешения для mynetworks и авторизовавшихся пользователей. Важно понимать, что это значит и для чего сделано. Ограничения читаются последовательно в порядке их перечисления. Таким образом, мы своих пользователей пускаем мимо ограничений, а для всех остальных выполняются проверки.</p>



<p>Теперь важные комментарии по указанным параметрам. Если бы все почтовые сервера всех системных администраторов были настроены по правилам, то эти комментарии не были бы нужны. Пройдемся по некоторым ограничениям, которые нужно включать осторожно:</p>



<ul><li>reject_invalid_helo_hostname и&nbsp;reject_unknown_helo_hostname — под эти правила иногда попадают почтовые серверы клиентов, которые не очень хорошо настроены. У них бывают неправильные адреса, кривые записи dns, отсутствие обратных зон и т.д. Их не много, но попадаются. Это не страшно, если вы регулярно следите за сервером. Отправитель получит сразу сообщение о том, что его письмо не дошло до вас. Если он как-то сообщит вам о проблеме, вы легко добавите его в белый список и все будет нормально. Если вам не хочется следить за сервером, лучше не указывайте эти ограничения. Но спам они отсекают не плохо. Сюда попадают все завирусованные компьютеры и сервера без нормальных настроек dns (а их чаще всего и нет).</li><li>reject_unverified_sender — специально его закомментировал. Я тестировал этот параметр. В принципе, работает нормально, но есть, как обычно, нюансы. Поясню, что делает этот параметр. Когда вам кто-то шлет письмо, ваш сервер обращается к серверу отправителю и спрашивает его стандатрной командой, есть ли на сервере такой отправитель. Если удаленный сервер отвечает, что есть, то никаких проблем — письмо принимается. Если удаленный сервер не отвечает или говорит, что такого адресата нет — письмо отклоняем. Очевидно, что такие проверки создают дополнительную постоянную нагрузку. Это нужно учитывать. К тому же, у вас почтовый лог постоянно будет забит этими проверками, особенно, если вам приходит много спама. На каждое спамовое письмо будет идти проверка, а сервера отправителя скорее всего либо нет, либо он неправильный, либо не отвечает и т.д. Все это будет постоянно проверяться и фиксироваться. В общем, я не использую.</li></ul>


</br>



<p>На время отладки ограничений, рекомендую пользоваться параметром:</p>



<pre class="wp-block-preformatted">soft_bounce = yes</pre>



<p>Когда он включен, все ответы сервера с кодами ошибок 5XX, заменяются на 4ХХ. То есть постоянная ошибка, которая сразу отклоняет письмо, заменяется на временную, которая предлагает повторить отправку позже. Таким образом, вы увидите работу всех ограничений, но письма не будут отклонены навсегда. Сервер отправителя через некоторое время снова придет к вам с новой попыткой доставки почты. Письмо безвозвратно не отклоняется. Вы можете проанализировать работу фильтра и решить, ставить его на постоянную работу или с ним что-то не так.</p>



<p>Создадим теперь файлы с белыми и черными списками.</p>



<pre class="wp-block-preformatted">cd /etc/postfix/lists &amp;&amp; touch white_client_ip black_client_ip white_client black_client white_helo block_dsl mx_access</pre>



<p>Ниже пример содержания этих файлов. Вы можете менять по своему усмотрению.</p>



<pre class="wp-block-preformatted"># cat white_client_ip
195.28.34.162 OK
141.197.4.160 OK</pre>



<pre class="wp-block-preformatted"># cat&nbsp;black_client_ip
205.201.130.163 REJECT You IP are blacklisted!
198.2.129.162 REJECT&nbsp;You IP are blacklisted!</pre>



<pre class="wp-block-preformatted"># cat&nbsp;white_client
# Принимать всю почту с домена яндекс
yandex.ru OK
# Разрешить конкретный ящик
spammer@mail.ru OK</pre>



<pre class="wp-block-preformatted"># cat black_client
# Блокировать всю почту с домена mail.ru
mail.ru&nbsp;REJECT You domain&nbsp;are blacklisted!
# Блокировать конкретный ящик
spam@rambler.ru&nbsp;REJECT You e-mail&nbsp;are blacklisted!</pre>



<pre class="wp-block-preformatted"># cat white_helo
# Могут попадаться вот такие адреса, которые не пройдут наши проверки
ka-s-ex01.itk.local &nbsp; &nbsp; OK
exchange.elcom.local &nbsp; &nbsp;OK</pre>



<pre class="wp-block-preformatted"># cat&nbsp;block_dsl
/^dsl.*\..*\..*/i                               553 AUTO_DSL spam
/dsl.*\..*\..*/i                                553 AUTO_DSL1 spam
/[ax]dsl.*\..*\..*/i                            553 AUTO_XDSL spam
/client.*\..*\..*/i                             553 AUTO_CLIENT spam
/cable.*\..*\..*/i                              553 AUTO_CABLE spam
/pool.*\..*\..*/i                               553 AUTO_POOL spam
/dial.*\..*\..*/i                               553 AUTO_DIAL spam
/ppp.*\..*\..*/i                                553 AUTO_PPP spam
/dslam.*\..*\..*/i                              553 AUTO_DSLAM spam
/node.*\..*\..*/i                               553 AUTO_NODE spam
/([0-9]*-){3}[0-9]*(\..*){2,}/i                 553 SPAM_ip-add-rr-ess_networks
/([0-9]*\.){4}(.*\.){3,}.*/i                    553 SPAM_ip-add-rr-ess_networks
/.*\.pppool\..*/i                               553 SPAM_POOL
/[0-9]*-[0-9]*-[0-9]*-[0-9]*-tami\.tami\.pl/i   553 SPAM_POOL
/pool-[0-9]*-[0-9]*-[0-9]*-[0-9]*\..*/i         553 SPAM_POOL
/.*-[0-9]*-[0-9]*-[0-9]*-[0-9]*\.gtel.net.mx/i  553 SPAM_POOL
/dhcp.*\..*\..*/i                               553 SPAM_DHCP
</pre>



<pre class="wp-block-preformatted"># cat&nbsp;mx_access
127.0.0.1      DUNNO 
127.0.0.2      550 Domains not registered properly
0.0.0.0/8      REJECT Domain MX in broadcast network 
10.0.0.0/8     REJECT Domain MX in RFC 1918 private network 
127.0.0.0/8    REJECT Domain MX in loopback network 
169.254.0.0/16 REJECT Domain MX in link local network 
172.16.0.0/12  REJECT Domain MX in RFC 1918 private network 
192.0.2.0/24   REJECT Domain MX in TEST-NET network 
192.168.0.0/16 REJECT Domain MX in RFC 1918 private network 
224.0.0.0/4    REJECT Domain MX in class D multicast network 
240.0.0.0/5    REJECT Domain MX in class E reserved network 
248.0.0.0/5    REJECT Domain MX in reserved network</pre>



<p>По сути файлы белых и черных списков не отличаются друг от друга. Можно использовать только один файл и в нем в каждой отдельной строке указывать либо запрет, либо разрешение. Я разделил просто для удобства восприятия и редактирования. Возможно вам будет удобнее с одним файлом.</p>



<p>После редактирования файлов обязательно выполняем команду на перестроение базы данных. Я перестрою сразу все файлы:</p>



<pre class="wp-block-preformatted">cd /etc/postfix/lists &amp;&amp; postmap white_client_ip black_client_ip white_client black_client white_helo&nbsp;block_dsl mx_access</pre>



<p>Еще упомяну о таком популярном явлении в спамерских письмах, как подделка адреса отправителя. Причем не просто подделка на абы кого, а именно на ваше имя домена. Пользователь получает спам письмо и в почтовом клиенте видит, что оно отправлено с вашего домена. Только по заголовкам можно определить реального отправителя. Такой подход позволяет обходить некоторые антиспам системы, которые не фильтруют письма внутреннего домена. Бороться с подменой адреса отправителя в нашем случае очень просто. Для этого в black_client добавим следующую запись:</p>



<pre class="wp-block-preformatted">zeroxzed.ru 554 Stop spam from my name</pre>



<p>Отправка с нашего домена осуществляется только с SASL авторизацией, а она во всех ограничениях стоит первой, поэтому письма реальных пользователей без проблем будут уходить. А всех тех, кто только притворяется нашим доменом, мы будет отфильтровывать этим правилом.</p>



<p>Приведу в завершении описания методов борьбы со спамом простой пример. Добавим в black_client почтовый адрес и отправим с него письмо.</p>



<pre class="wp-block-preformatted"># cat black_client
zeroxzed@gmail.com REJECT Your e-mail was banned!</pre>



<pre class="wp-block-preformatted"># postmap black_client</pre>



<p>Отправляем сообщение и проверяем почтовый лог.</p>



<pre class="wp-block-preformatted"># cat /var/log/maillog</pre>



<pre class="wp-block-preformatted">Mar 20 02:21:34 mail postfix/smtpd[10816]: connect from mail-yw0-f177.google.com[209.85.161.177]
Mar 20 02:21:35 mail postfix/smtpd[10816]: Anonymous TLS connection established from mail-yw0-f177.google.com[209.85.161.177]: TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)
Mar 20 02:21:35 mail postfix/smtpd[10816]: NOQUEUE: reject: RCPT from mail-yw0-f177.google.com[209.85.161.177]: 554 5.7.1 &lt;zeroxzed@gmail.com&gt;: <strong>Sender address rejected: Your e-mail was banned!</strong>; from=&lt;zeroxzed@gmail.com&gt; to=&lt;root@zeroxzed.ru&gt; proto=ESMTP helo=
Mar 20 02:21:35 mail postfix/smtpd[10816]: disconnect from mail-yw0-f177.google.com[209.85.161.177]</pre>



<p>Вот и результат. На этом по борьбе со спамом все.</p>


</br>



<h2 class="wp-block-heading">Заключение</h2>



<p>Проверить настроенный почтовый сервер можно с помощью онлайн сервиса&nbsp;<a href="https://www.mail-tester.com/" target="_blank" rel="noreferrer noopener">https://www.mail-tester.com</a>. Не факт, что получите максимальный бал, но все недочеты будут указаны. Критичное нужно исправить (например, если обратная зона неправильная), некритичное можно пропустить (если dkim, к примеру, не настраивали).</p>



<p>Кажется все написал, что знал по поводу почтового сервера на linux в небольших и средних организациях. У некоторых может возникнуть вопрос, а зачем свой почтовый сервер? Почему бы не воспользоваться средствами корпоративной почты, которую представляют популярные почтовые сервисы бесплатно? Я планирую написать по этому поводу отдельную заметку. У меня тоже есть определенный опыт на этот счет. И если некоторое время назад я считал, что свои почтовые серверы в небольших организациях уже не актуальны, то сейчас я так не думаю, поэтому и появилась эта статья.</p>
<p>Сообщение <a href="https://clip-clap.ru/it/%d1%81%d0%be%d1%84%d1%82-%d0%b8-%d0%be%d1%81/linux/centos/%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%be%d0%b9%d0%ba%d0%b0-postfix-dovecot-mysql-%d0%b1%d0%b0%d0%b7%d0%b0-postfixadmin-roundcube-dkim-%d0%bd%d0%b0-centos-7/">Настройка postfix + dovecot + mysql база + postfixadmin + roundcube + dkim на CentOS 7</a> появились сначала на <a href="https://clip-clap.ru">Clip-Clap</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://clip-clap.ru/it/%d1%81%d0%be%d1%84%d1%82-%d0%b8-%d0%be%d1%81/linux/centos/%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%be%d0%b9%d0%ba%d0%b0-postfix-dovecot-mysql-%d0%b1%d0%b0%d0%b7%d0%b0-postfixadmin-roundcube-dkim-%d0%bd%d0%b0-centos-7/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Кэширование страницы с использованием Disk: Enhanced 
Минифицировано с помощью Disk
Кэширование БД с использованием Disk (Request-wide (широкий запрос) modification query)

Served from: clip-clap.ru @ 2026-07-04 15:18:46 by W3 Total Cache
-->