ТаблицейБлоками.

Способы хранения веб-страниц

Последняя модификация: 10.08.2014 г

Страница загружена с адреса: http://webdesign.site3k.ru/consulting/infosave.html

Моя студия веб-дизайна

Способы хранения веб-страниц: документы FrontPage или базы данных

  1. Метод хранения документов FrontPage или DreamWeaver
  2. Метод фреймов
  3. Метод SSI include
  4. Движок для статического HTML
  5. Генерация страниц из SQL-баз
  6. Подведение итогов

Метод хранения документов FrontPage или DreamWeaver

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

Преимущества метода полноценных HTML-страниц

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

Недостатки полноценных HTML-страниц

Главным недостатком является то, что для внесения изменения в дизайн или меню (например, для смены контактного телефона, внизу страницы), приходится открывать и редактировать все документы, в которых должны появиться изменения. То есть, количество труда, требуемого для изменений прямо пропорционально количеству изменяемых страниц. Многие люди даже не представляют, что это является недостатком. С их точки зрения труд всегда прямо пропорционален количеству. Например, на перекопку 2-х огородов уходит времени в 2 раза больше, чем на перекопку одного. И никак не иначе.

Другим недостатком является необходимость хранения большого количества кода на каждой странице.

Как пользоваться FrontPage или DreamWeaver

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

Метод фреймов

Метод фреймов был задуман для уменьшения количества хранимого кода и отделения постоянного содержимого (оформление, меню, контактная информация) от содержимого страниц, которое, у каждой страницы свое. В данном методе рабочая область документа разделяется на части, в каждую из которых загружается свой документ. В результате чего, если у документа 2 части – меню слева и содержимое справа, для его отображения используется 3 документа: главный документ фрейма, в котором расписаны размеры, положение отдельных кадров и адреса тех документов, которые в этих кадрах должны отображаться, и сами эти кадры. Этот документ не виден посетителю, хотя браузер и показывает его адрес в адресной строке. Посетитель видит те документы, которые отображаются в кадрах, описанных в этом документе. При щелчке по ссылке в меню, посетитель не перегружает весь документ с описанием фреймов: он загружает только новую содержательную часть, оставаясь в прежнем описании фреймов с прежним меню. В результате, можно не только ускорить загрузку документов (поскольку меню не перегружается каждый раз), но и вынести статические данные (меню, контактную информацию и т. д.) в статическую часть фреймов. Статическая часть фреймов при этом может быть представлена единственным документом на весь сайт и, при необходимости внести изменения в меню, дизайн, или контактную информацию, нужно будет изменить ТОЛЬКО ЕГО.

Преимущества метода фреймов

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

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

Недостатки метода фреймов

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

<html>

</html>

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

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

Как пользоваться фреймами

О том, как использовать фреймы, сказано в документации по языку HTML. Некоторые особенности использования освещены в статьях «Ссылки и окна. Внешний вид и внутреннее содержание» и «Если вам не нравятся фреймы, значит, вы не умеете их готовить». Метод считается устаревшим, хотя до сих пор используется в справочной системе Windows, в утилите phpMyAdmin, в административной части форума phpBB и многих других программах и веб-приложениях.

Метод SSI include

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

Преимущества метода SSI include

Самым большим преимуществом является возможность одним выстрелом подкосить стадо зайцев любой величины.

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

Недостатки метода SSI include

Единственным недостатком является невозможность увидеть, как будет выглядеть страница, пока она не будет смонтирована сервером. И если, делая дизайн страницы, можно вначале сделать ее целиком, а потом разрезать на части, то, вносить изменения в уже существующие части сложнее. Так как начало элемента может находиться в одном файле, а конец в другом (середина может быть в третьем, или даже десятке промежуточных файлов), и вероятность ошибки в коде увеличивается.

Кроме того, можно считать недостатком и то, что открытие, рассчитанного на SSI файла в визивизинг-редакторе типа FrontPage или DreamWeaver чревато серьезными проблемами, так как эти редакторы не имеют ни малейшего представления о программировании, разделении кода и прочих премудростях и самовольно допишут в документ то, чего, по их мнению, в нем не хватает. В результате, вычищение визивизинг-мусора может потребовать больше времени, чем написание этих файлов с чистого листа в блокноте. Но для профессионалов это не является недостатком, так как они и так постоянно работают в блокноте и знают язык HTML наизусть.

Как пользоваться SSI include

Метод SSI include, вместе с остальными функциями SSI описан на странице SSI – Server Side Includes, однако, будет уместным привести здесь простой пример применения. Для примера возьмем простой сайт, над которым я сейчас работаю (по завершении работы сайт может измениться, но это не мешает использовать сырой вариант в качестве примера). В данном случае, страницы сайта фактически состоят из 3−х частей: голова, туловище и хвост (шапка, контент и подвал сайта). Помещаем часть кода в шапку:

Файл head.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html lang="ru">
<head>
<meta http-equiv=Content-Type content="text/html; charset=windows-1251">
<title><!--#echo var="titl"--></title>
<meta name="Description" content="<!--#echo var="descript"-->">
<meta name="Keywords" content="<!--#echo var="keyw"-->">
<meta name="Author" content="Красносельский К. К.">
<link rel="stylesheet" href="/include/style.css" type="text/css">
</head>
<body>

<Table border=0 CELLPADDING="0" CELLSPACING="0" class=main>
<col width=238px><col>
<TBODY>
<tr><td colspan=2 class=head><div class=head>

<h1 class=slogan>Полный спектр оборудования<br><span>для контроля помещений</span></h1>
<table border=0 CELLPADDING="0" CELLSPACING="0" class=menu>
<col width=20px><col>
<TBODY>
<tr><td><!--#if expr="$SCRIPT_NAME=/^\/index.html/" -->&#8226;<!--#endif --><td><a href="http://<!--#echo var="SERVER_NAME"-->/">главная</a></tr>
<tr><td><!--#if expr="$SCRIPT_NAME=/^\/product.html/" -->&#8226;<!--#endif --><td><a href="/product.html">продукты</a></tr>
<tr><td><!--#if expr="$SCRIPT_NAME=/^\/uslugi.html/" -->&#8226;<!--#endif --><td><a href="/uslugi.html">услуги</a></tr>
<tr><td><!--#if expr="$SCRIPT_NAME=/^\/partners.html/" -->&#8226;<!--#endif --><td><a href="/partners.html">партнерам</a></tr>
<tr><td><!--#if expr="$SCRIPT_NAME=/^\/contacts.html/" -->&#8226;<!--#endif --><td><a href="/contacts.html">контакты</a></tr>
</TBODY>
</table>
</div></tr>
<tr><td class=top-left><td class=td-topcontent><img src="/include/top-ugol.jpg" alt=""></tr>
<tr><td class=left>

<div class=left>
<div class=left-top></div>
<div class=block>
<h2>До 1 февраля</h2>
<h3>GSM сигнализация  STD&#8722;001</h3>
<p style="text-align:center"><img src="/include/signal_pict_wolf_camera.gif" alt="">
<P>По суперцене: 5000 руб.
<P>* Камера – доп. опция

</div>
<div class=left-bottom></div>
</div>

<td class=td-content>

Что непривычного в этом файле для тех, кто не привык использовать SSI?

Первое, что, наверняка бросится в глаза, это странные комментарии типа <!--#echo var="titl"-->. Те, кто читал SSI – Server Side Includes знает, что это не комментарии, а SSI-инструкции. Данная инструкция говорит серверу вывести в эту часть страницы содержимое переменной titl, которая, очевидно, должна быть объявлена где-то раньше.

Второе, что может показаться непривычным – это то, что ссылки, значения href, url и src часто начинаются с косой черты. Например: <link rel="stylesheet" href="/include/style.css" type="text/css">

Косая черта указывает на корень сайта и, в данном случае, href указывает на файл style.css, находящийся в папке include, находящейся в корне сайта (например, http://имя_сайта/include/style.css). Для тех, кто привык делать страницы целиком в визуальных редакторах привычней писать относительные пути, вроде ../include или include, но, это возможно, когда страница, в которой эти пути указываются, имеет определенный адрес. Если же данная шапка будет включаться в различные файлы, находящиеся в различных каталогах, то, куда, например, укажет относительный путь ../include/style.css для следующих файлов:

http://имя_сайта/dir1/page1.html
http://имя_сайта/dir1/poddir/page1.html
http://имя_сайта/page1.html

В первом случае, файл style.css, как и положено, будет искаться в каталоге http://имя_сайта/include. Во втором он будет искаться в НЕсуществующем каталоге http://имя_сайта/dir1/include, а в третьем, вообще, в каталоге, который, если и существует, недоступен из интернета.

Указание абсолютного пути от корня сайта позволяет избежать казусов, возникающих с относительными путями. Можно было бы указывать путь как http://имя_сайта/include/style.css, но в этом случае, при смене имени домена потребовалось бы менять имя_сайта. Отказ от явного указания имени сайта позволяет этого избежать и сделать код более гибким. Впрочем, иногда имя сайта стоит указать. Например, для перехода на главную (если страница сохранена локально, лучше, чтобы там сохранился такой адрес главной страницы сайта: «http://имя_сайта/», а не такой «/»). Для этого, можно использовать различные переменные сервера. Например, с использованием переменной SERVER_NAME, можно указать адрес сайта как http://<!--#echo var="SERVER_NAME"-->/. Так можно указать полный путь в интернете ко всем страницам, но я указываю только на главную, оставляя в других местах указание пути от корня сайта, чтобы сократить код.

Далее по коду обнаруживаются несколько странные конструкции <!--#if expr="$SCRIPT_NAME=/^\/index.html/" -->&#8226;<!--#endif -->. Это условный оператор, описанный в SSI – Server Side Includes. Посмотрим на него внимательно. Он анализирует значение переменной SCRIPT_NAME, содержащей полный путь к странице от корня сайта. В данном случае, меня интересовал именно файл index.html, находящийся в корне сайта (в других папках сайта тоже может находиться файл с таким именем). Именно поэтому, для анализа взята не переменная DOCUMENT_NAME, содержащая название файла, а SCRIPT_NAME, содержащая еще и путь. Но путь к файлу начинается с символа «/», имеющего для SSI специальное значение. Поэтому, чтобы отключить это значение, приходится применить регулярное выражение (взяв имя и путь файла в символы «/»). В этом выражении, знаком «^» показать что, значение должно начинаться именно так, как написано здесь (то есть, не должно содержать никаких символов до этого текста, а не просто содержать этот текст где-то, внутри); затем, применив символ «\», показать, что следующий за ним символ должен пониматься как простой символ, а не конец выражения (как он обычно понимается); потом уже написать /index.html и закончить выражение символом «/». Таким образом, серверу будет указано, что нужно ориентироваться не на какой-нибудь index.html, а именно на index.html в корне сайта (если нужно указать, что сравнивать следует именно с «index.html», а не чем-то другим, вроде «index.htmlчто-то_еще», перед закрытием регулярного выражения нужно поставить знак доллара – я этого не делаю, так как в моей практике «index.htmlчто-то_еще» пока не бывало).

С конструкцией разобрались. Зачем же она была нужна? Зачем мне нужно было усложнять код, вписав в него <!--#if expr="$SCRIPT_NAME=/^\/index.html/" -->&#8226;<!--#endif -->?

Все очень просто: так я хочу определять текущую страницу и, если выражение дало истинный ответ, сервер вписывает в страницу &#8226; (отображается, как кружек). Иначе никакого &#8226; не пишется и среди всего меню этим знаком помечена только текущая страница. По аналогии можно менять стиль ссылки на текущую страницу или заменять ее простым текстом.

И так, шапка готова. Делаем хвост.

Файл footer.html

</tr>

<tr><td><td class=td-bottomcontent><img src="/include/bottom-ugol.jpg" alt=""></tr>
<tr><td colspan=2 class=bottom>

<p class=copy>
<a class=valid href="javascript:window.location='http://validator.w3.org/check?uri=http://<!--#echo var="HTTP_HOST" --><!--#echo var="SCRIPT_NAME" -->'" target=_blank><img src="http://www.w3.org/Icons/valid-html401.png" alt="Valid HTML 4.01 Transitional" height="31" width="88"></a>

</tr>
</TBODY>
</table>

</body>
</html>

В этом файле все просто. Сомнения могут возникнуть только по поводу http://validator.w3.org/check?uri=http://<!--#echo var="HTTP_HOST" --><!--#echo var="SCRIPT_NAME" -->. Что это такое? Это ссылка для проверки правильности HTML-кода. В uri этой ссылки указывается адрес страницы, которую следует проверить. Поскольку хвост включается в разные страницы, а писать имена всех страниц неразумно, там использована конструкция http://<!--#echo var="HTTP_HOST" --><!--#echo var="SCRIPT_NAME" --> , которая подставляет адрес текущей страницы (HTTP_HOST имеет значение, аналогичное SERVER_NAME). В результате, какую бы страницу не открыли, там будет прописан адрес именно той страницы, которую открыли.

И так, у нас есть голова и хвост, что остается телу? А вот:

Файл index.html

<!--#set var="titl" value="Полный спектр оборудования для контроля помещений. Главная" -->
<!--#set var="keyw" value="Полный спектр оборудование контроль помещений" -->
<!--#set var="descript" value="Полный спектр оборудования для контроля помещений" -->

<!--#include virtual="/include/head.html" -->

<h1>Ваш дом под <span style="font-variant: small-caps">вашим</span> контролем</h1>

<p>Современный дом – это не просто красивое и комфортное жилье с отоплением и горячей водой, но и сложный организм, состоящий из множества компонентов, контроль и управление, которыми требуют постоянного внимания хозяина. К счастью, новейшие технологии позволяют контролировать и управлять функциями дома независимо от местоположения его владельца.
<p><b>Компания «Дом XXI века»</b> предлагает полный спектр оборудования от удаленного контроля над состоянием входной двери до создания сложных систем, управляющих температурой, электричеством, мультимедиа и водоснабжением Вашей квартиры или загородного дома.
<p><b>Компания «Дом XXI века»</b> создает готовые решения для дистанционного управления и контроля для:
<ol>
<li>Городских квартир (фото красивой квартиры)
<li>Гаражей (гараж с машиной)
<li>Частных домов и приусадебных участков (домик)
<li>Магазинов/киосков (магазин)
<li>Индивидуальных проектов «Умный дом» (фото плана дома)
</ol>

<!--#include virtual="/include/footer.html" -->

Что первое бросается в глаза? Конечно же, то, что переменные, требуемые в голове, наконец-то определены:

<!--#set var="titl" value="Полный спектр оборудования для контроля помещений. Главная" -->

и т. д. В результате, у каждой страницы, несмотря на общую шапку, будут собственные титул, ключевые и описание. Определив их, можно включить шапку, инструкцией <!--#include virtual="/include/head.html" -->. А по окончании содержания страницы, включить хвост, инструкцией <!--#include virtual="/include/footer.html" -->.

Результат использования SSI include

Все ОЧЕНЬ просто. Теперь оформление и меню сайта находятся в 2-х файлах и для изменения меню или дизайна не требуется переделывать весь сайт.

Можно ли сделать так, чтобы голова и хвост находились в 1-ом файле?

Да проще пареной репы: Называем файл all.html, пишем в нем первой строкой:

<!--#if expr="$sector=top" -->
Затем копируем сюда все содержание шапки, после чего пишем:
<!--#elif expr="$sector=bottom" -->
Сюда копируем все содержание хвоста, затем пишем:
<!--#endif -->

Файл all.html завершен. Теперь нужно внести некоторые изменения в страницы сайта. А именно, вместо

<!--#include virtual="/include/head.html" -->

Вписать

<!--#set var="sector" value="top" -->
<!--#include virtual="/include/all.html" -->

а вместо

<!--#include virtual="/include/footer.html" -->

вписать

<!--#set var="sector" value="bottom" -->
<!--#include virtual="/include/all.html" -->

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

Настройка сервера

На некоторых хостингах обработка SSI-директив не включена по умолчанию и ее придется включить вручную в панели управления сайтом, назначив обработчик файлам с расширением html, или прописав инструкцию

AddHandler server-parsed .html

в файл .htaccess в корне вашего сайта.

Движок для статического HTML

Аналогию метода SSI include можно сделать на любом серверном языке. На многих сайтах вместо SSI применяется Perl или PHP. Метод SSI в интерпретации PHP выглядит так (файл index.html, который, скорее всего, в данном варианте будет называться index.php):

<?php
$titl="Тут титул страницы";
$keyw="Тут перечень ключевых слов";
$descript="Тут описание страницы";
include($_SERVER["DOCUMENT_ROOT"]."/include/head.php") ;

Тут содержимое страницы

include($_SERVER["DOCUMENT_ROOT"]."/include/footer.php") ;
?>

Замена SSI на PHP имеет смысл в том случае, если многие страницы содержат элементы (скрипты) которые невозможно реализовать с помощью SSI. Можно, конечно, выделить эти элементы в отдельные PHP-файлы и инклюдить их в SSI, но если таких включений много, то лучше сразу перейти на PHP и позволить себе писать PHP-код прямо в страницах.

Назовем такой метод методом PHP-include. Им пользуются многие сайты. Но, имея такой мощный язык как PHP, ограничиваться теми же возможностями, что давал SSI…

Не хочу никого обидеть, но, по-моему, это означает быть конкретным тормозом.

Если уж использовать PHP-страницы, то нужно делать движок.

На счет движка, требуется внести ясность: многие люди считают что движок и СУКа (система управления контентом) – это одно и то же. На самом деле, это не так. Любая СУКа имеет свои скрипты (движки), но движки могут быть не только у СУК и, более того, основная часть движков принадлежит вовсе не СУКам. Движок – это скрипт управляющий выводом страниц в браузер посетителя. Кроме движка СУКи имеют еще, по крайней мере, админку, в которой владелец сайта может менять и дополнять содержимое сайта. Такая спайка есть у форумов и гостевых книг, которые никто не станет называть СУКами. СУКа – это нечто большее, чем форум или гостевая. СУКа – это единая всеобъемлющая система управления ВСЕГО ввода и вывода сайта. А движок является ее частью, или частью форума. Или применяется вообще без какой-либо админки. Например, на сайте aedesign.com.ua движок создает ссылки «следующая», «предыдущая», формирует раздел работ: http://aedesign.com.ua/raboti/, разбивая его на страницы, создает страницы с адресами вроде http://aedesign.com.ua/win.php?/raboti/10/p/1.jpg, и ява-скрипты к ним. И никакой админки. То есть, движок – это движок. Его можно поставить на колеса, добавить руль и получить машину, а можно прикрепить к нему ленту и получить конвейер, или кабинку на тросе и получить лифт. СУКа – это только одно из применений движка. Но в данном случае значения не имеет, будет ли движок дополнен админкой, чтобы получить СУКу, или же применится самостоятельно. В данном случае, вопрос в том, как его делать и применять. Более того, в данном случае речь идет о движке для статического HTML, не использующем базы данных и выдающем страницы с человеку понятными урлами (ЧПУ).

О движке для статического HTML я не слышал ничего, пока не придумал его сам в 2006 году. До этого времени я стабильно применял SSI, включая php-скрипты, там, где они нужны, инструкциями типа

<!--#include virtual="/include/script.php" -->

Но постоянное усложнение задач, вынуждало меня применять вызов PHP все чаще и чаще, пока я не задумался о целесообразности полного перехода на PHP. Но я не хотел, чтобы страницы сайта имели расширение PHP. Можно было вставить в .htaccess строку

AddType application/x-httpd-php .html

и, таким образом, заставить сервер выполнять файлы с расширением html, как PHP-скрипты, а в них использовать метод PHP-include, но эта идея напоминала мне костыли. Если уж использовать PHP, то нужен движок, а не SSI – вид сбоку. Однако классические движки меня совсем не устраивали. Они опираются на использование баз данных – громоздкий и медленный метод, применение которого не оправдывается в 90% случаев. Для большинства сайтов нужны движки не с базами данных, а движки со статическим HTML. Когда я сказал об этом некоторым веб-мастерам, оказалось, что о таком звере они еще и не слышали. «Движок и статический HTML? Это что-то несовместимое» – заявили они в один голос и, когда узнали, что я это сделал, просили рассказать об этом поподробней.

С тех пор прошло полтора года. Я, наконец, нашел время рассказать об этом.

 

Метод движка для статического HTML появился как альтернатива методу SSI include. Суть метода заключается в том что, скрипт берет данные (в данном случае из html-файлов) и внедряет их в дизайн сайта, хранящийся в отдельном файле, снабжая меню и всей необходимой динамикой. В результате, для внесения изменений в дизайн или меню достаточно изменить один, отвечающий за это файл, упрощается использование php-скриптов и создание админки для превращения движка в СУКу.

Преимущества метода движка для статического HTML

Самым большим преимуществом, как и при применении метода SSI include, является возможность одним движением руки вносить изменения по всему сайту.

Другим преимуществом, как и при применении метода SSI include, является сокращение объема хранимого кода: вместо того, чтобы повторяться на каждой странице, общий код приведен только 1 раз в 1 файле, на основе которого движок создает страницы сайта.

По отношению к методу SSI include данный метод упрощает использование серверных скриптов и создание или редактирование страниц. Если в SSI-версии в страницы требовалось вносить хотя бы некоторые операторы программирования, например:

<!--#include virtual="/include/head.html" -->

то при использовании движка страницам не требуется даже этого.

Недостатки метода движка для статического HTML

С большой натяжкой недостатком метода можно считать то, что открытие, рассчитанного на него файла в визивизинг-редакторе типа FrontPage или DreamWeaver чревато серьезными проблемами, так как эти редакторы самовольно допишут в документ то, чего, по их мнению, в нем не хватает, и усложнят жизнь веб-мастера вычищением визивизинг-мусора. Но профессионал не станет использовать костыли для редактирования таких файлов, а откроет их в блокноте. Дилетантам для этих же целей легко сделать админку, оснастив движок органом, превращающим его в СУКу.

Как пользоваться движком для статического HTML

Для использования движка, нужно написать движок, затем одним из указанных ниже способов, инструкциями файла .htaccess передать обработку html-файлов этому движку.

Способ 1 – назначение обработчика.

action template "/index.php"
addHandler template .html .htm .shtm .shtml

Способ 2 – использование модуля mod_rewrite

RewriteEngine on
RewriteRule \.html /index.php [NS,L]
RewriteRule ^.*/$ /index.php [NS,L]

Различия способов

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

Второй способ хорош тем что, в отличие от первого, позволяет нормально обрабатывать НЕсуществующие страницы. В первом способе несуществующие страницы так же обрабатываются, однако, перед выдачей страницы, сервер сообщает что, страницы нет (ошибка 404). Во втором способе информация об ошибке не выдается.

Для понимания, зачем может потребоваться обработка несуществующих страниц, приведу пример:

Предположим, сообщения в гостевой книге разбиты на страницы. Обычно, выдача страниц производится по таким адресам:

http://mysite.ru/guest/?page=1

http://mysite.ru/guest/?page=2

и т. д.

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

http://mysite.ru/guest/1.html

http://mysite.ru/guest/2.html

Таким преобразованием может заниматься модуль mod_rewrite или сам скрипт гостевой (движок). Получая запрос http://mysite.ru/guest/1.html, скрипт понимает, что его нужно отработать как http://mysite.ru/guest/?page=1 и выдает соответствующую страницу. Однако, если движок подключен первым способом, выдаче страницы будет сопутствовать код 404 (файл не найден). При использовании второго способа, странице будет сопутствовать код 200 (все в порядке).

Компоненты сайта на движке для статического HTML

Главным компонентом тут, естественно, является движок. Но, чтобы понять устройство движка, нужно вначале увидеть, с чем ему предстоит работать.

Шаблон сайта, файл all.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html lang="ru">
<head>
<meta http-equiv=Content-Type content="text/html; charset=windows-1251">
<!--METADATA-->
<meta name="Author" content="Красносельский К. К.">
<link rel="stylesheet" href="/include/style.css" type="text/css">
</head>
<body>
<div class=topimg>
<P class=whit>Наши лучшие идеи для Вас!
<img src="/include/topright.gif" alt="">
<P class=deviz>Оборудование для ресторанов,<br>кафе, баров, столовых, буфетов и...
<P class=hmenu>
<a href="/about.html">О компании</a>
<a href="/dilers.html">Дилерам</a>
<a href="/catalog/raspr.html">Распродажа</a>
<a href="/vakansy/">Вакансии</a>
<a href="/contacts.html">Контакты</a>
</div>
<table border=0 CELLPADDING="0" CELLSPACING="0" class=main><tr>
<td style="width:209px" class=vmenu rowspan=2>
<div class=vmenu>
<p class=mhead>Меню
<!--MENU-->
</div>

<img class=dogovor src="/include/dogovor.jpg" alt="">

<td class=content rowspan=2>

<!--CONTENT-->

<table style="width:100%" border=0 CELLPADDING="3" CELLSPACING="0"><tr>
<td style="text-align:center"><img src="/include/bottom1.jpg" alt="Спецпредложение по цене" width=240 style="border:2px solid #943F3F">
<td style="text-align:center"><img src="/include/bottom2.jpg" alt="Спецпредложение по условию" width=240 style="border:2px solid #943F3F">
</table>

<td class=right>

<img src="/include/right1.jpg" alt="Хит продаж тепловое оборудование" width=190 style="border:1px solid black; margin:15px 15px 0 0">
<img src="/include/right2.jpg" alt="Хит продаж холодильное оборудование" width=190 style="border:1px solid black; margin:15px 15px 0 0">
<img src="/include/right3.jpg" alt="Хит продаж механическое оборудование" width=190 style="border:1px solid black; margin:15px 15px 0 0">
<img src="/include/right4.jpg" alt="Хит продаж интерьер ресторана" width=190 style="border:1px solid black; margin:15px 15px 15px 0">

</tr>
<tr><td class=right2>&nbsp;</tr>
</table>

<div class=futer>
<P><img src="/include/tel.gif" alt="">Телефоны: <span>(495) 159-20-40</span><img src="/include/home.gif" alt="">Адрес: <span>г.Москва, ул. Космонавта Волкова д.14</span>
</div>
<a href="javascript:window.location='http://validator.w3.org/check?uri=http://<?=$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'];?>'" target=_blank><img src="http://www.w3.org/Icons/valid-html401.png" alt="Valid HTML 4.01 Transitional" height="31" width="88"></a>

</body>
</html>

Обычный html-документ. Если бы не абсолютные адреса от корня сайта, вроде этих:

/include/style.css

(их можно временно заменить относительными), он нормально отображался бы без всяких движков, открытый просто с диска. Такой файл легко корректировать, внося изменения в дизайн или общие элементы содержания. Единственный НЕ html-фрагмент – это подстрока

<?=$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'];?>

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

Теперь возникает задача вставить в данный шаблон реальное содержимое. Места вставки определены комментариями:

<!--METADATA-->

<!--MENU-->

<!--CONTENT-->

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

Файл содержимого index.html

<title>Заголовок страницы</title>
<meta name="Description" content="Описание страницы">
<meta name="Keywords" content="Ключевые слова">
<!--End Meta-->

<table style="width:100%" border=0 CELLPADDING="3" CELLSPACING="0"><tr>
<td style="text-align:center"><img src="/include/volna.jpg" alt="" width=160 style="border:2px solid #943F3F">
<td style="text-align:center"><img src="/include/europe.jpg" alt="" width=160 style="border:2px solid #943F3F">
<td style="text-align:center"><img src="/include/roal.jpg" alt="" width=160 style="border:2px solid #943F3F">
</table>

<h1>Добро пожаловать на сайт компании «Авангард Люкс»</h1>
<div class=news>
<p>Наша фирма специализируется на поставках профессионального оборудования, мебели, барных стоек, посуды, текстиля, аксессуаров и т.д. для ресторанов, кафе, баров, отелей и развлекательных комплексов.
<p>Наш ассортимент постоянно обновляется, мы стараемся все новые позиции сразу размещать на сайте в каталоге. Не всегда это получается сделать оперативно, поэтому если Вы не нашли искомого в каталоге, обращайтесь к менеджерам – по электронной почте, по ICQ, по телефону. Менеджеры расскажут вс¨ подробнейшим образом, порекомендуют варианты и если в данный момент требуемого не окажется на складе, специально для Вас закажут у производителя.
<p>Одними из приоритетных направлений деятельности нашей компании являются консалтинговые услуги. Мы готовы помочь Вам на любой стадии становления Вашего ресторана – начиная с планировки и проектирования и заканчивая дизайном и оснащением.
<p>Мы поможем Вам и в случае, если Ваше заведение уже функционирует, но Вы хотите что-то улучшить, добавить, исправить в Вашем ресторане. Провед¨м маркетинговые исследования, составим сво¨ заключение для Вас о целесообразности того или иного действия над заведением.
<p>Мы поможем спроектировать и организовать ресторан и службу питания при отеле, при развлекательном комплексе, в кинотеатре и т.д.
<p>Искренне надеемся, что сотрудничество с нами в дальнейшем перераст¨т в долгосрочные партн¨рские отношения.

<p>Наши лучшие идеи для Вас.
</div>

Как и шаблон, данный файл так же нормально открывается в браузере и, хоть и без всякого оформления, но, тем не менее, нормально отображается. Однако, при запросе этого файла, движок находит метку «<!--End Meta-->», разделяет по ней страницу на метатеги и основное содержание, после чего, вставляет метатеги вместо метки «<!--METADATA-->» шаблона, а содержимое, вместо метки «<!--CONTENT-->».

Таким образом, у каждой страницы могут быть свои метатеги тип и количество которых ничем не ограничен, а данное удовольствие доступно далеко не в каждой СУКе.

Остается вставить меню. В одном случае в качестве меню может быть простой файл, с перечнем, типа:

<P><a href="/">Главная</a>
<P><a href="/catalog/">Продукты</a>
<P><a href="/partners.html">Партнерам</a>
<P><a href="/catalog/?pod=1">Купить</a>
<P><A HREF="/contacts.html">Контакты</a>

и включаетый в остальную страницу чем-то вроде

readfile ('http://cjcity.ru/new_statyi.inc');

В другом случае меню может быть просто частью шаблона (файла all.html в данном примере). В третьем случае, чем-то более хитрым. Например, меню может быть базой, обрабатываемой специальным скриптом.

Образец меню-базы для скрипта.

/|/|index.html|Главная
/|/catalog/|index.html|Каталог
/|/|consalting.html|Консалтинг
/|/|proect.html|Проектирование ресторанов
/|/|services.html|Сервисное обслуживание
/|/|uslovia.html|Условия доставки
/|/bouling/|index.html|Боулинг
/bouling/|/bouling/|montag.html|Монтаж и установка боулинга
/bouling/|/bouling/|sroki.html|Сроки
/bouling/|/bouling/|okupaemost.html|Окупаемость
/bouling/|/bouling/|serves.html|Сервисное обслуживание
/bouling/|/bouling/|accesuars.html|Аксессуары для боулинга
/|/as/|index.html|Хочу открыть ресторан
/|/questions/|index.html|Вопрос - Ответ
/|/article/|index.html|Статьи
/|/news/|index.html|Новости

В данном случае каждая строка является записью с полями, разделенными вертикальной чертой. Первое поле определяет уровень, на котором должен отображаться данный пункт меню. Второе поле указывает на каталог, к которому ведет ссылка меню. 3 поле указывает имя файла, а 4 – название ссылки. Порядок пунктов определяется порядком записей.

Скрипт-обработчик меню

<?php // сначала добьемся однозначности имени запрашиваемого файла
if (@array_key_exists('extension', $path)){// если в массиве есть ключ расширений
	@$localpath['dirname']=str_replace('\\','/',$path['dirname']).'/';// замена слешей для совместимости с локальным сервером
	@$localpath['basename']=str_replace('\\','/',$path['basename']);
}else{// иначе обращение к каталогу и basename содержит имя каталога
	@$localpath['dirname']=str_replace('\\','/',$path['dirname'].'/'.$path['basename'].'/');
	@$localpath['basename']='index.html';
}
// если нужно считать что file.php?a=1 и file.php?a=2 - это то же самое, нужно отбросить параметры
if(strpos($localpath['dirname'], "?") !== false){// ecли $localpath['dirname'] содержит параметры
	$localpath['dirname']=substr($localpath['dirname'],0,strpos($localpath['dirname'], "?"));
}
while (ereg("//", $localpath['dirname'])) {
	$localpath['dirname']=str_replace('//','/',$localpath['dirname']);// если где-то оказалось 2 слеша подряд, заменить на 1
}

$menuarray=file($_SERVER['DOCUMENT_ROOT']."/include/menu.txt"); // считаем меню

for($i=0; $i < count($menuarray); $i++) {
	// перебросим строку в переменные
	@list($uroven, $katalog, $namefile, $nazvanie)=explode("|",trim($menuarray[$i]));
	if ($namefile=='index.html'){// заменим index.html на пустое имя
		$newnamefile='';
	}else{
		$newnamefile=$namefile;
	}
	if (eregi('^'.$uroven.'.*$',$localpath['dirname']) !==false){ // если начало пути запрашиваемого файла
		// совпадает с минимальным уровнем отображения для пункта меню, пункт будет отображаться
		$myarray=explode("/",$uroven);
		$counturoven=count($myarray);// счетчик уровней вложенности
		if (@$oldcounturoven){// если старое значение счетчика сохранено
			if ($oldcounturoven < $counturoven){// если уровень изменился в большую сторону
				echo "<ul>\r\n";
			}else if ($oldcounturoven > $counturoven){ // если в меньшую (если не изменился, ничего не делаем)
				echo "</ul>\r\n";
			}
		}
		$oldcounturoven=$counturoven; // теперь сохраняем старое значение
		if ($counturoven > 2){// если уровень превышает второй, делаем список
			$nametag='<li>';
		}else{// если не превышает, делаем абзац
			$nametag='<P>';
		}
		if ($localpath['dirname'].$localpath['basename']==$katalog.$namefile){// если путь и имя файла в пункте меню совпадают с запрашиваемым,
			// отображаем как обычный текст. При этом в стилях определено что, обычный
			// текст жирный, а ссылки нет и, в результате, пункт текущей страницы отображается без ссылки и выделен жирным
			echo "$nametag$nazvanie\r\n";
		}else{ // иначе, как ссылку
			echo "$nametag<a href='$katalog$newnamefile'>$nazvanie</a>\r\n";
		}
	}
}
?>

Сделав основные модули, над которыми придется трудиться движку, можно взяться за сам движок.

Движок для статического HTML

<?php
$indexfile="/index.html";// название индексного файла
$sitemail='manager@'.$_SERVER["SERVER_NAME"]; // маил сайта
require($_SERVER["DOCUMENT_ROOT"].'/include/check.php') ;
$path=pathinfo($_SERVER["REQUEST_URI"]);// разбить запрошеный УРЛ для анализа (потребуется в меню)
$fname=$_SERVER["DOCUMENT_ROOT"].$_SERVER["REQUEST_URI"];// Путь и имя с расширением и параметрами
$fname=parse_url($fname);
$fname=$fname['path'];
@$template=file_get_contents($_SERVER['DOCUMENT_ROOT']."/include/index.html");

if($fname==''){ // Запуск шаблона напрямую
	$tempstring=file_get_contents($indexfile);
	$temparray=explode('<!--End Meta-->',$tempstring,2);
	while(count($temparray)<2){
		array_unshift($temparray,'');
	}
	$metadata=$temparray[0];
	$content=$temparray[1];
}else if (is_dir($fname) && eregi('include',$fname)===false && is_file($fname.$indexfile)){ // это каталог и в нем есть индексный файл и это не служебный каталог include
	$tempstring=file_get_contents ($fname.$indexfile);
	$temparray=explode('<!--End Meta-->',$tempstring,2);
	while(count($temparray)<2){
		array_unshift($temparray,'');
	}
	$metadata=$temparray[0];
	$content=$temparray[1];
}else if (is_dir($fname)){ // это каталог (без индексного)
	$tempstring=file_get_contents ('errors/error_403.html');
	$temparray=explode('<!--End Meta-->',$tempstring,2);
	while(count($temparray)<2){
		array_unshift($temparray,'');
	}
	$metadata=$temparray[0];
	$content=$temparray[1];
}else if (is_file($fname) && !stristr($fname,'/.') && eregi('include',$fname)===false){	// Файл есть и его имя не начинается с точки
	// (системные файлы .htaccess, .htpassword и т. д.) и это не служебный файл из папки include
	$tempstring=file_get_contents($fname);
	$temparray=explode('<!--End Meta-->',$tempstring,2);
	while(count($temparray)<2){
		array_unshift($temparray,'');
	}
	$metadata=$temparray[0];
	$content=$temparray[1];
}else{
	$tempstring=file_get_contents ('errors/error_404.html');
	$temparray=explode('<!--End Meta-->',$tempstring,2);
	while(count($temparray)<2){
		array_unshift($temparray,'');
	}
	$metadata=$temparray[0];
	$content=$temparray[1];
}

@$menu=file_get_contents($_SERVER['DOCUMENT_ROOT']."/include/menu.php");

$template=str_replace("<!--METADATA-->",$metadata,$template);
$template=str_replace("<!--MENU-->",$menu,$template);
$template=str_replace("<!--CONTENT-->",$content,$template);
eval('?>'.$template.'<?');
//echo $template;

?>

Два момента в этом движке требуют пояснений:

require($_SERVER["DOCUMENT_ROOT"].'/include/check.php') ;

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

Строка

eval('?>'.$template.'<?');

и идущая следом за ней закомментированная строка

//echo $template;

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

Этот фрагмент написан спустя полтора года после написания основной статьи. Хочу пояснить в нем некоторые вещи, которые не всем были очевидны из основного текста.

Практически для любой СУКи, построенной на базе данных, при большой посещаемости возникает проблема кеширования данных на диск (и некоторые ранее популярные СУКи на базах вымерли, как динозавры, из-за своей медлительности, например, phpNuke). Эта проблема возникает из-за того, что данные из файлов на диске считываются гораздо быстрее и с меньшей нагрузкой, чем данные из базы. Именно для снижения нагрузки на сервер и увеличения скорости, СУКа начинает использовать простые текстовые файлы с диска. Фактически, это означает, что она, хоть и построена на базе данных, начинает работать как СУКа на текстовых файлах. Возникает парадокс: а зачем собственно, писать данные в базу, чтоб потом вытаскивать их на диск и работать с текстовыми файлами с диска? Не проще ли сразу писать их на диск?

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

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

Генерация страниц из SQL-баз

Все вышеперечисленные способы хорошо подходят для хранения простых страниц, содержащих статьи. Метод движка для статического HTML является самым совершенным из них. Однако, если вместо статей на сайте должны храниться структурированные данные, все эти способы никуда не годятся. Примером структурированных данных может служить прайс. Допустим, на вашем сайте есть каталог бытовой техники со следующей структурой:

КатегорияПроизводительАртикльЦена
ТелевизорыSamsung8765150$
ТелевизорыSamsung2565140$
ТелевизорыSony346160$
ТелевизорыSony78170$
ТелевизорыSony0456130$
ТелевизорыPhilips78387150$
ТелевизорыPhilips9368145$

И так далее. Эти данные, конечно, можно представить в виде обычной таблички:

<table>
<tr><th>Категория<th>Производитель<th>Артикль<th>Цена
<tr><td>Телевизоры<td>Samsung<td>8765<td>150$
<tr><td>Телевизоры<td>Samsung<td>2565<td>140$
<tr><td>Телевизоры<td>Sony<td>346<td>160$
<tr><td>Телевизоры<td>Sony<td>78<td>170$
<tr><td>Телевизоры<td>Sony<td>0456<td>130$
<tr><td>Телевизоры<td>Philips<td>78387<td>150$
<tr><td>Телевизоры<td>Philips<td>9368<td>145$
</table>

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

Гораздо удобней будет работать с данными, если хранить их в специальной базе. Часто для этого подойдут обычные текстовые базы. Например, ту же таблицу можно хранить в отдельном текстовом файле в формате:

<tr><td>Телевизоры<td>Samsung<td>8765<td>150$
<tr><td>Телевизоры<td>Samsung<td>2565<td>140$
<tr><td>Телевизоры<td>Sony<td>346<td>160$
<tr><td>Телевизоры<td>Sony<td>78<td>170$
<tr><td>Телевизоры<td>Sony<td>0456<td>130$
<tr><td>Телевизоры<td>Philips<td>78387<td>150$
<tr><td>Телевизоры<td>Philips<td>9368<td>145$

Скрипт считывает весь файл в переменную, преобразовывает ее в массив, используя в качестве разделителя «<tr><td>», затем проходит по всем элементам массива, дробя данные на отдельные элементы, используя в качестве разделителя «<td>». И дальше анализирует полученные переменные (обычно деление на строки идет не по «<tr><td>», а по началу новой строки, а в качестве разделителя элементов строки используется не «<td>», а вертикальная черта «|»).

Этот вариант очень хорош для небольших баз данных. Но что, если у вас сайт рецептов приготовления пищи, на котором планируется разместить рецепты разных народов мира? Это же тысячи записей, при чем, не таких коротких строк, как прайс, а довольно больших, каждая из которых может содержать десятки тысяч символов описания того или иного рецепта. В простейшем случае, получается 1000 записей умноженная на 10 000 символов = база размером в 10 мегабайт. Довольно громоздко. Если каждому посетителю делать выборку из такой базы, нагрузка на сервер получается значительная. Такой объем данных лучше хранить в отдельных файлах. Может даже, как статические страницы. Но, это хорошо, если сайт имеет структуру вроде такой:

Русская кухня
	1 блюда
		щи
		окрошка
		…
	2 блюда
		картофельное пюре
		овощное рагу
…
	3 блюда
		Квас
		Пиво
		…
Украинская кухня
	1 блюда
		борщ
		суп с лапшой
		…
	2 блюда
…
и т. д.

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

Разделив данные на отдельные файлы и каталоги, реализовать данную задачу без критических нагрузок на сервер невозможно. Тут требуется единая база для быстрой обработки. Но текстовая база на 10 мегабайт в таком случае тоже не лучшее решение. Ведь, что для выборки всех первых блюд, независимо от типа кухни, что для выборки всех рецептов плова, что для получения одной единственной записи, необходимо считать и проанализировать всю базу. Снизить нагрузку можно только используя базы с ключевыми полями. Например, SQL. Именно в этом случае использование SQL-баз оказывается эффективней хранения данных в отдельных страницах или хранение их в текстовых базах. Тут затраты ресурсов на SQL-сервер оправдываются экономией ресурсов при обработке данных.

Подведение итогов

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

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

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

Для сайтов и разделов с многозначной выборкой (первые блюда всех национальных кухонь, а не только какой-то одной) наиболее эффективным с точки зрения удобства редактирования, требуемого места и производительности является использование SQL-баз. Лишь для небольших баз, про которые заранее известно, что они всегда будут вмещаться в пределы мегабайта вместо SQL стоит использовать текстовый формат.

Очень часто имеет смысл совмещать базы данных и файлы. Например, на сайте as-event.ru стоит текстовая СУКа, но данные о клиентах, проектах и т. д. выбираются из SQL-базы.

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

 

< Предыдущая (AJAX) Способы хранения страниц Следующая (Как заработать на сайте)>

 

Комментарии к странице (всего 2)

 

 

 


На главную страницу сайта