0% нашли этот документ полезным (0 голосов)
21 просмотров

JavaScript и PHP

Загружено:

ohnnyj961
Авторское право
© © All Rights Reserved
Мы серьезно относимся к защите прав на контент. Если вы подозреваете, что это ваш контент, заявите об этом здесь.
Доступные форматы
Скачать в формате PDF, TXT или читать онлайн в Scribd
0% нашли этот документ полезным (0 голосов)
21 просмотров

JavaScript и PHP

Загружено:

ohnnyj961
Авторское право
© © All Rights Reserved
Мы серьезно относимся к защите прав на контент. Если вы подозреваете, что это ваш контент, заявите об этом здесь.
Доступные форматы
Скачать в формате PDF, TXT или читать онлайн в Scribd
Вы находитесь на странице: 1/ 191

В. В.

ЯНЦЕВ

,Больше книг в https://t.me/portalToIT

JAVASCRIPT И PHP.
CONTENT MANAGEMENT
SYSTEM
Учебное пособие

•САНКТПЕТЕРБУРГ•МОСКВА•КРАСНОДАР•
2022
УДК 004
ББК 32.973.4я73

Я 65 Янцев В. В. JavaScript и PHP. Content management system : учеб


ное пособие для вузов / В. В. Янцев. — СанктПетербург : Лань,
2022. — 192 с. : ил. — Текст : непосредственный.

ISBN 9785507448463

Современные вебресурсы невозможно представить без систем управле


ния, которые принято называть Content management system или сокращенно
CMS. Многие разработчики используют не готовые CMS, например Word
Press, Joomla, Drupal, 1СБитрикс, а пишут собственные. Для опытного
программиста разработка подобной системы видится вполне реальной. Для
начинающих это серьезный труд. Но если есть желание, то все «подводные
камни» удастся преодолеть. А поможет в этом данная книга. В ней рассказы
вается о механизмах разработки очень простой, но вполне работоспособной
CMS, которую можно использовать для создания и управления небольшими
сайтами. По сути, это рассказ о первых шагах, с которых начинается
освоение такой интересной стези, как написание CMS. Естественно, что,
изучив предложенное автором программное обеспечение, вы можете пойти
дальше и существенно нарастить его функционал или, используя полу
ченные навыки, разработать собственный вариант системы управления.
Рекомендовано в качестве дополнительной литературы для студентов
вузов, обучающихся по направлению «Информатика и вычислительная
техника».
Освоить материал книги вам поможет zipархив с полным набором
файлов рассмотренной CMS. Вы сможете запустить систему на своем
компьютере. Для этого книга содержит подробные инструкции по созданию
локального хостинга на ПК. Дополнительные материалы доступны в
электронной библиотечной системе «Лань» по ссылке или QRкоду, указан
ным ниже.

УДК 004
ББК 32.973.4я73

Обложка
П. И. ПОЛЯКОВА

© Издательство «Лань», 2022


© В. В. Янцев, 2022
© Издательство «Лань»,
художественное оформление, 2022
https://t.me/portalToIT

1. ВВЕДЕНИЕ
Современные веб-ресурсы невозможно представить без систем управления,
которые принято называть Content management system (CMS).
Создание профессиональной системы управления — сложная и трудоем-
кая задача. И тем не менее многие разработчики используют не готовые CMS, а
пишут собственные. Иногда популярные системы, такие как WordPress, Joomla,
Drupal, 1С-Битрикс и др., оказываются слишком громоздкими для тех или иных
проектов. Поэтому рациональнее написать собственную, более простую, «зато-
ченную» под конкретную задачу. Это как с покосом травы на даче. Конечно,
можно запустить на участок комбайн, но гораздо проще справиться обычной
бензокосилкой.
Автор начал заниматься программированием в 2003 г. С 2004 г. за до-
вольно короткий промежуток времени успел поработать в нескольких студиях
веб-дизайна. И вот на что обратил внимание. В те годы почти каждая такая сту-
дия, большая или маленькая, старалась написать собственные «движки» для
сайтов. Получались они очень разными — от весьма простеньких и неказистых
до серьезных профессиональных разработок.
Не остался в стороне от этой моды и автор. Только писал такие системы не
во взаимодействии с группой программистов и дизайнеров, а полностью само-
стоятельно. Назывались они непритязательно — СОУС (Система оперативного
управления сайтом), ИКС (Интерактивный конструктор сайтов), КПСС (Кон-
структор простых социальных сетей). Естественно, автор постоянно их совер-
шенствовал, наращивал функциональные возможности и т. д. До 2020 г. автор и
многие его клиенты вполне успешно пользовались данными разработками.
Однако наступил момент, когда автор уже не мог в одиночку конкуриро-
вать с такими проектами, как WordPress, Joomla, Drupal, 1С-Битрикс и др.
В итоге все мои системы постепенно были отправлены в архив, после че-
го автор занялся другими разработками. Но по-прежнему интересовался тем,
что происходит в индустрии создания «движков». А самое главное, продолжал
заниматься написанием различных компонентов, которые являлись составными
частями CMS.
У читателей, наверное, уже появился вопрос: к чему этот исторический экс-
курс? Он демонстрирует, что на сегодняшний день у автора накопился немалый
опыт создания самых разных CMS и их отдельный частей для самых разных веб-
проектов. Этим опытом автор и хочет поделиться на страницах данной книги.

1.1. О чем эта книга


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

3
Есть ряд книг, в которых излагаются не только основы языка, но и даются
примеры сценариев. Они могут объяснить начинающему разработчику, каким
образом создаются реальные проекты.
Но изданий, рассказывающих о такой популярной среди программистов
теме, как создание CMS, практически нет. Частично автор попытался осветить
этот вопрос в своем учебнике «JavaScript. Визуальные редакторы». Но там рас-
сказ велся лишь об одной из составных частей CMS — редакторах WYSIWYG.
Поэтому автор решил написать новую книгу, целиком посвященную теме
создания полностью законченной системы управления, которая позволяла бы
конструировать сайты с нуля. Специально для данной книги было написано до-
вольно простое ПО, которое и будет рассмотрено в дальнейшем.
Но это будут не просто теоретические рассуждения. Если в книге пред-
ставлены лишь фрагменты кода, то, скачав по ссылке https://editjs.ru/CMS.zip
zip-архив, вы получите в свое распоряжение весь проект, включая сам движок и
демонстрационный сайт, выполненный в двух вариантах. Вы можете проверить
в действии и сайт, и систему управления. А также читать книгу и одновременно
изучать «живой» код, записанный в тех или иных файлах.

1.2. Особенности проекта


Автор ставил перед собой задачу сделать проект максимально простым,
чтобы разобраться в нем и повторить его мог даже начинающий программист. По-
этому было принято решение отказаться от применения базы данных и использо-
вать вместо нее текстовые файлы. Тем более что проект не содержит ничего тако-
го, что необходимо было скрывать от посторонних лиц. Вся хранящаяся в файлах
информация выводится на страницы сайта в открытом виде. Применение БД сде-
лает проект более сложным, более объемным, а его описание неоправданно уве-
личит объем книги. Больше код — соответственно, больше «вес» архива проек-
та, а его автор тоже хотел сделать максимально «легким». Освоив материал, чита-
тель самостоятельно сможет заменить текстовые файлы таблицами базы данных.
Итак, проект очень простой. Но в нем есть один компонент, сделанный на
профессиональном уровне, — визуальный редактор страниц. Достаточно ска-
зать, что в JavaScript-файле, обеспечивающем все функции редактора, почти
700 строк кода. Но пусть вас это не пугает. В книге приведены подробные, ис-
черпывающие пояснения ко всем компонентам проекта, включая этот огром-
ный файл.
Редактор не только профессиональный, но и очень удобный. Большин-
ство систем WYSIWYG предоставляет разработчику окно редактирования, раз-
меры которого могут совершенно не совпадать с истинным размером соответ-
ствующей части страницы. В результате, то, что хорошо смотрится в
WYSIWYG, на реальном сайте может иметь совсем иной вид. И приходится
разработчику по новой править страницу. Наш редактор избавлен от такого не-
достатка. В нем предусмотрен механизм, благодаря которому вы сразу видите,
как будет выглядеть страница с внесенными изменениями еще до того, как вы
записали их в файл. Более того, вы вносите изменения не в каком-то абстракт-

4
ном окне, а прямо в самой странице. Благодаря этому требуемый результат до-
стигается с первой попытки.
Еще один важный момент. Как ясно из названия книги, главные действу-
ющие лица в ней — языки программирования JavaScript и PHP. Первое место
по объему кода занимает JavaScript, соответственно PHP — второе, отставая не
на много. Поэтому было бы неплохо, чтобы читатель знал основы этих языков
хотя бы на уровне начинающего.
HTML-разметке и таблицам стилей CSS мы тоже уделим внимание, так
как их доля в коде проекта заметна. Поэтому быть знакомыми с HTML и CSS
читателю также необходимо.
На что еще хотел бы обратить внимание читателя: во избежание терми-
нологических споров по языку JavaScript автор ориентировался на определения,
данные на страницах сайта https://developer.mozilla.org/ru/.

1.3. Оформление сценариев


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

function coor(ev)
{
let v=ev.pageY;
let sv=document.getElementById("bpan").offsetTop;
dv=v-sv;
addEventListener("mousemove", neco);
}

Если фрагменты сценария внедрены непосредственно в текст, то в этом


случае части кода выделены полужирным шрифтом, например, так:
— если условие ans = 1 ложно.
Обратите внимание: в некоторых блоках программ сделан перенос
части кода на вторую и даже на третью или четвертую строку (из-за недо-
статка ширины страницы в книге). В реальном сценарии код записывает-
ся одной строкой. Запомните правило: все переносы строк кода существу-
ют только в их типографском воспроизведении. Если вы в дальнейшем
столкнетесь с подобной ситуацией, учитывайте данный аспект.
Еще один момент. Обычно таблицы стилей какой-либо страницы принято
записывать следующим образом (пример):
#divfl {
position: absolute;
left: 0;
display: flex;
flex-wrap: wrap;
z-index: 2;
width: 100%;
top: 250px;
}

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

#divfl {position: absolute; left: 0; display: flex;


flex-wrap: wrap; z-index: 2;
width: 100%; top: 250px;}

Как видите, экономия налицо: в этом случае вместо девяти строк на за-
пись потрачено только три.
И последнее. В описании программ нередко имеет смысл не приводить
весь код, который относится к разбираемому фрагменту, а показывать только ту
его часть, что важна в контексте данных объяснений. Поэтому в таких случаях
автор вводит сокращение в виде трех точек:

...

Например, сокращенный вариант для предыдущего примера с настройка-


ми стилей может выглядеть так:

#divfl {position: absolute; left: 0; ...}

Тем самым мы показываем, что на данном этапе описания нам важно об-
ратить внимание исключительно на позиционирование элемента.

6
2. ПРЕЖДЕ ЧЕМ НАЧАТЬ
До того как мы приступим к написанию CMS, автор посчитал необходи-
мым дать некоторую предварительную информацию:
— подробнее коснуться того, что такое CMS;
— рассказать, какие технологии и методы необходимо будет применить
в нашем проекте, в первую очередь в визуальном редакторе;
— пояснить смысл терминов, которые нам придется использовать неод-
нократно.
Поэтому данный материал я решил выделить пусть и в небольшую, но
самостоятельную главу.

2.1. CMS
Представим такую ситуацию: вы разработали сайт, состоящий из несколь-
ких HTML-страниц, и разместили его в Сети. Пройдет немного времени — и вы
обнаружите, что информацию на одной или нескольких страницах необходимо
отредактировать, а может, и кардинально изменить. Даже самые стабильные ин-
тернет-ресурсы нуждаются в периодическом обновлении. А что говорить про
сайты, которые рассказывают о быстро меняющихся вещах, например о строи-
тельстве жилого микрорайона или о новинках в мире компьютеров. В таких слу-
чаях обновление информации происходит чуть ли не каждый день.
И вот вы стоите перед задачей ежедневно редактировать одну или не-
сколько страниц и ставить их вместо устаревших версий. Выглядит это при-
мерно так: открыли на своем компьютере папку с файлами сайта, добавили но-
вые рисунки, схемы или фотографии, отредактировали страницу, подключи-
лись к хостингу, закачали новые изображения, заменили страницу, запустили
браузер, проверили полученный результат. Если что-то не понравилось, вновь
правите страницу и закачиваете ее новую версию на хостинг. Уже чувствуется,
что это довольно хлопотное занятие. Между тем, если бы ваш сайт был снаб-
жен CMS, все оказалось бы гораздо проще: запускаете в браузере систему
управления и выполняете все манипуляции через нее. Не надо подключаться к
хостингу, не надо переносить страницу на свой компьютер, не надо закачивать
отредактированную страницу через файловый менеджер. Еще раз повторим: все
делается через браузер и без предварительной обработки данных на своем ком-
пьютере.
Обычно CMS — это симбиоз двух продуктов: конструктора сайта и си-
стемы управления сайтом. В Сети рекламируется много различных CMS,
например уже упоминавшиеся во введении WordPress, Joomla, Drupal, 1С-
Битрикс и др.
Аббревиатура CMS происходит от английского Content management
system, что переводится как система управления содержимым. Многие совре-
менные сайты (если не большинство) управляются такими системами. CMS поз-
7
воляют администратору вносить любые изменения на страницы сайта в онлайн-
режиме. Для этого системы управления снабжают WYSIWYG-редакторами.

2.2. WYSIWYG
Сайты пишутся на языках программирования HTML и JavaScript. Поэто-
му редактирование веб-страницы — это по сути процесс изменения ее кода.
Для профессионального разработчика — это привычное занятие. Но ведь боль-
шинство сайтов программисты создают не для себя, а для клиентов. Много ли
среди них специалистов, досконально знающих HTML и JavaScript? Ответ —
единицы. Значит, для большинства клиентов необходим механизм, который бы
позволял создавать на сайте новые страницы и редактировать существующие
без специальных знаний.
Поэтому креативные программисты придумали не простой редактор,
а WYSIWYG (или, как еще говорят, визуальный редактор). Он отображает ре-
дактируемые материалы практически в том же самом виде, как и на странице
сайта. То есть нажали кнопку «BOLD=» — фрагмент текста действительно вы-
делился жирным. Нажали кнопку «IMG» — и появилась настоящая фотогра-
фия, а не ее HTML-код.
Термин WYSIWYG — это аббревиатура, произошедшая от английского
выражения What You See Is What You Get, что в переводе означает «что вы ви-
дите, то вы и получаете». В нашем случае смысл этого выражения таков: что вы
видите в редакторе, то и будет на странице сайта. Очень удобно для создания и
редактирования контента!

2.3. Атрибут contentEditable


Данный атрибут, если он указан в элементе, сообщает браузеру, что этот
элемент — редактируемый.
В чем польза contentEditable? Допустим, есть слой div, в который мы за-
гружаем предназначенный для редактирования фрагмент веб-страницы. Если у
div установлен данный атрибут, мы можем вставлять в слой курсор, выделять,
удалять, добавлять текст, в том числе вводя его с клавиатуры. То есть у нас уже
получился простейший редактор. А если к нему добавить кнопки для импорта в
div HTML-кода рисунков, таблиц, линий и т. д. то у нас выйдет уже полноцен-
ный редактор! Естественно, что написанное с нуля или исправленное содержи-
мое слоя можно будет передать специальной программе для записи в файл или
базу данных.
Некоторые программисты указывают атрибут contentEditable без значе-
ния. Это не совсем корректно, хотя и будет работать. Как утверждает
https://developer.mozilla.org/ru/, правильно писать:
contentEditable="true"

Атрибут можно указать непосредственно в теге, например так:


<div id="edit" contenteditable="true">

8
или присвоить его в JavaScript-сценарии:

document.getElementById("edit").
setAttribute("contenteditable", true);

Обратите внимание! Приведенный выше фрагмент — это пример перено-


са строки, сделанный в книге из-за недостатка ширины страницы. В реальном
файле данный код записывается одной строкой! Автор уже предупреждал о та-
ких переносах во введении. Думаю, читателям все ясно и в дальнейшем не
нужно повторять это правило.
Добавлю, что операция присвоения элементу атрибута в JavaScript-
сценарии должна производиться после загрузки страницы:
addEventListener("load", function()
{
document.getElementById("edit").
setAttribute("contenteditable", true);
});

2.4. Свойство designMode


Свойство designMode имеет много общего с contentEditable. Отличие в
том, что designMode устанавливается не для отдельного элемента, а для всего
документа в целом. Когда свойство включено (document.designMode = «on»),
можно редактировать любую HTML-страницу полностью. Эта особенность
натолкнула программистов на идею создания визуальных редакторов, исполь-
зуя загрузку целой страницы или ее отдельного фрагмента в iframe.
Процедура включения режима редактирования может выглядеть, напри-
мер, так. Пусть на странице редактора есть фрейм, в который загружается дру-
гая HTML-страница, предназначенная для обработки. Тогда в JavaScript-коде
редактора необходимо записать всего одну строку:

frames[0].document.designMode="on";

Естественно, что данная операция, как и в предыдущем случае, должна


производиться после загрузки страницы:

addEventListener("load", function()
{
frames[0].document.designMode="on";
});

Хотя в нашем проекте все манипуляции с контентом производятся непо-


средственно на редактируемой странице, делается это без использования
designMode. Мы познакомились с данным свойством в порядке расширения
кругозора. В описанной в книге системе принцип действия следующий: выби-
раете редактируемую область (их на каждой странице 3), нажимаете кнопку и
для соответствующего раздела устанавливается атрибут contentEditable.

9
2.5. Метод getSelection
Теперь мы переходим к методам, которые предоставляет в наше распоря-
жение JavaScript.
Первым упомянем getSelection. Тут все очень просто: метод возвращает
объект Selection, который содержит выделенный пользователем текст. Разра-
ботчик имеет возможность на программном уровне манипулировать выделен-
ным фрагментом: форматировать его, копировать, удалять, изменять.
Как выглядит применение метода на практике? Например, у вас есть ре-
дактируемая область контента. Вы выделили в этой области одно или несколь-
ко слов и нажали кнопку BOLD. К тексту, содержащемуся в объекте Selection,
будет добавлена пара тегов b — открывающий и закрывающий. После этого те-
кущее выделение будет преобразовано к полужирному начертанию.
Аналогично происходит, если вы просто вставили курсор в какое-либо
место редактируемой области. Объект Selection в этом случае хранит точку
вставки курсора. В эту точку можно поместить рисунок, таблицу, линию,
символ и т. д.

2.6. Методы createElement и surroundContents


Первый метод позволяет создать новый элемент документа, а второй —
добавить его к выделению, полученному методом getSelection. Например, у нас
в редактируемой области есть слово «Важно», которое мы хотим выделить по-
лужирным шрифтом. Для этого можно написать вот такой сценарий из 4 строк:

let sel=document.getSelection();
let rng=sel.getRangeAt(0);
let elm=document.createElement("b");
rng.surroundContents(elm);

Разберем, что происходит в каждой строке кода.


1. Создаем объект, содержащий выделенный фрагмент текста.
2. Используя метод getRangeAt, создаем объект Range с диапазоном,
равным выделению. Индекс 0 указывает, что выбранный диапазон у нас первый
и единственный.
3. В теле документа методом createElement создаем открывающий и за-
крывающий теги элемента b (тег для полужирного начертания шрифта).
4. Методом surroundContents добавляем эти теги перед началом выделе-
ния и после конца выделения.
Теперь редактируемое слово выглядит так: Важно.
Этот способ позволяет добавлять теги не только к выделенному тексту,
но и вставлять элементы в положение курсора в редактируемой области:
например рисунки, таблицы, линии и т. д.

10
3. СРЕДА РАЗРАБОТКИ
Для написания и тестирования примеров из книги нам в обязательном по-
рядке необходимо создать на своем компьютере локальный хостинг.
Вообще, хостинг — это, проще говоря, компьютер, предназначенный для
размещения сайтов и обеспечивающий к ним доступ из Интернета. Локальный
хостинг — набор программ на вашем персональном компьютере, которые поз-
воляют имитировать реальный хостинг и проводить тестирование ваших сайтов
(включая HTML-страницы и серверные скрипты) перед их размещением в Се-
ти. Кстати, обратите внимание: локальный хостинг работает без подклю-
чения к Интернету!
Для чего нужен хостинг в нашем случае? Дело в том, что разбираемая
CMS написана не на чистом HTML, а на «смеси» HTML и PHP. То есть каждая
страница, которую видит посетитель или администратор системы управления,
представляет собой серверную PHP-программу, формирующую данную стра-
ницу вслед за запросом. А значит, открыть такую программу просто двойным
щелчком по иконке нельзя. Необходимо расположить скрипт на хостинге и за-
пускать, вводя соответствующий запрос в адресном поле браузера. Только в
этом случае вы увидите запрошенную страницу.
Чтобы получить результаты, описанные в данной книге, вам понадобится
установить на компьютер распространяемый пакет компонентов Visual C++ от
Microsoft, сервер Apache и интерпретатор PHP. Описание технологии их уста-
новки приведено в следующих разделах.
Полноценную среду разработки невозможно представить без настоящего
текстового редактора, специально предназначенного для создания программ.
Конечно, весь код можно писать в обычном «Блокноте». Но гораздо лучше и ра-
циональнее пользоваться специализированными редакторами. Такие приложения
намного удобнее: они подсвечивают код, предлагают синтаксические подсказки,
позволяют менять кодировку документов, сохраняют файлы в разных форматах.
В нашем случае необходим редактор, который позволит:
— выполнять качественную разметку;
— создавать файлы с таблицами стилей;
— писать сценарии на JavaScript;
— писать серверные приложения на PHP.
Этим принципам удовлетворяют многие редакторы. Но лучше всего, на
наш взгляд, Notepad++. Где его взять и как установить, рассказывается в пятом
разделе этой главы.
Итак, вводная часть завершена — приступим к созданию локального хо-
стинга. А начнем мы с выяснения разрядности вашей ОС.
Обратите внимание: все процедуры описаны для компьютера с опе-
рационной системой Windows 10.

11
3.1. Выясняем разрядность ОС
Перед тем как мы создадим на компьютере среду разработки, необходимо
выяснить разрядность вашей операционной системы, чтобы знать, какие файлы
скачивать для локального хостинга.
Включите компьютер, дождитесь полной загрузки операционной систе-
мы. Щелкните на кнопке «Пуск», а затем на кнопке «Параметры» (рис. 3.1.1).
В открывшемся окне выберите пункт меню «Система». Откроется следующая
вкладка, в которой необходимо кликнуть на строке «О системе» (или «О про-
грамме»). После этого на вкладке «Характеристики устройства» посмотрите
строку «Тип системы» (рис. 3.1.2).

Рис. 3.1.1
Кнопка «Параметры» в меню

Рис. 3.1.2
Разрядность или тип операционной системы

12
Типов ОС Windows существует два — 32-разрядная и 64-разрядная. За-
помните разрядность вашей. Это число понадобится трижды: при скачивании
пакета Visual C++, а также zip-архивов сервера Apache и интерпретатора PHP.

3.2. Установка пакета Visual C++


Откройте браузер и зайдите на страницу https://www.apachelounge.com/-
download/. Это сайт, с которого мы будем скачивать архив с сервером Apache.
Но сначала надо установить на ваш компьютер компоненты Visual C++, без ко-
торых сервер не заработает.
Для этого найдите на указанной странице текст «Be sure you installed
latest 14.29.30037.0 Visual C++ Redistributable for Visual Studio 2015–2019:
vc_redist_x64 or vc_redist_x86 see Redistributable» (на момент подготовки кни-
ги данные строки были последними в описательной части страницы; со време-
нем текст может несколько измениться). Для нас важны две ссылки, располо-
женные в тексте: vc_redist_x64 и vc_redist_x86. Если у вас 64-разрядная ОС,
нажмите ссылку vc_redist_x64. Если 32-разрядная, необходимо щелкнуть на
ссылке vc_redist_x86 (рис. 3.2.1).

Рис. 3.2.1
Ссылки для скачивания пакетов Visual C++
Запустите скачанный файл (рис. 3.2.2). Примите условия лицензионного со-
глашения и нажмите кнопку «Установить». Дождитесь завершения процесса уста-
новки (рис. 3.2.3). Нажмите кнопку «Закрыть» (рис. 3.2.4).

13
Рис. 3.2.2
Начало установки пакета

Рис. 3.2.3
Процесс установки

Рис. 3.2.4
Завершение установки
14
3.3. Установка сервера Apache 2.4
Теперь скачайте на «Рабочий стол» компьютера с сайта
https://www.apache-lounge.com/download/ zip-архив сервера, соответствующий
разрядности вашей ОС (рис. 3.3.1). Для 64-разрядной системы — архив с по-
меткой Win64 (например, httpd-2.4.48-win64-VS16.zip), для 32-разрядной — с
пометкой Win32 (например, httpd-2.4.48-win32-VS16.zip). Распакуйте архив.
Скопируйте папку Apache24 (только ее) непосредственно на диск C, чтобы ее
адрес был C:\Apache24 (рис. 3.3.2).

Рис. 3.3.1
Сайт с zip-архивом сервера
Откройте приложение «Командная строка» от имени администратора. Для
этого нажмите кнопку «Пуск», в меню выберите «Служебные — Windows», найди-
те пункт «Командная строка» и щелкните на нем правой (!) кнопкой мыши. В вы-
падающем списке выберите «Дополнительно», а затем «Запуск от имени админи-
стратора» (рис. 3.3.3). Откроется окно программы.

Рис. 3.3.2
Копируем папку Apache24 на диск C

15
Рис. 3.3.3
Запуск командной строки
Теперь надо инсталлировать, а затем запустить сервер. Для этого в окне
командной строки сразу после:
C:\WINDOWS\System32>

наберите
C:\Apache24\bin\httpd.exe -k install

Должно получиться
C:\WINDOWS\System32>C:\Apache24\bin\httpd.exe -k install

Нажмите «Enter». Когда процесс инсталляции закончится (рис. 3.3.4), в


окне программы вновь появится строка

C:\WINDOWS\System32>

Закройте окно командной строки и перезагрузите компьютер.


ВНИМАНИЕ! Может открыться окно брандмауэра с запросом на
разрешение работы Apache. Разрешите доступ во всех сетях.

16
Рис. 3.3.4
Инсталляция сервера из приложения «Командная строка»
Нам надо убедиться, что сервер заработал. Для этого откройте ваш брау-
зер и введите в строке адреса http://localhost/. Если появилось сообщение «It
works!», то все в порядке — сервер функционирует как положено (рис. 3.3.5).

Рис. 3.3.5
Сервер подтверждает, что он работает
Теперь обратите внимание на один важный момент. Метод, которым мы
установили сервер, позволяет запускать его автоматически при включении
компьютера и завершать его работу при выключении компьютера. То есть
Apache продолжает функционировать, даже если в данное время он вам не ну-
жен. Эта информация вызывает у вас беспокойство по поводу теоретической
уязвимости ПК? Тогда переведите сервер в режим ручного запуска. Для этого
выполните следующие действия:
— введите в строке поиска слово «службы» (рис. 3.3.6) и нажмите на со-
ответствующей иконке, которая появится в списке результатов поиска;
— в открывшемся окне найдите строку «Apache2.4» и щелкните на ней пра-
вой (!) кнопкой мыши — появится меню управления службой (рис. 3.3.7);
— кликните на строке «Свойства» и в окне на вкладке «Общие» найдите
список «Тип запуска»;
— выберите пункт «Вручную», затем последовательно нажмите кнопки
«Применить» и «ОК» (рис. 3.3.8);
— теперь остановите службу, нажав соответствующую ссылку в левой
стороне окна (рис. 3.3.9);
— дождитесь окончания процесса (рис. 3.3.10);
— закройте вкладку «Службы».

17
Рис. 3.3.6
Находим приложение «Службы»

Рис. 3.3.7
Открываем меню управления службой

18
Рис. 3.3.8
Переводим службу в режим ручного запуска

Рис. 3.3.9
Нажимаем ссылку «Остановить службу»

19
Рис. 3.3.10
Идет процесс остановки Apache
Теперь Apache остановлен и переведен в режим ручного запуска. При необ-
ходимости воспользоваться сервером снова зайдите в раздел «Службы», выделите
строку «Apache2.4» и кликните на ссылке «Запустить службу» (рис. 3.3.11). Подо-
ждите окончания процесса (рис. 3.3.12).

.
Рис. 3.3.11
Для запуска Apache нажмите ссылку «Запустить службу»

20
Рис. 3.3.12
Идет процесс запуска сервера
Продолжаете беспокоиться о безопасности? Можете на время работы с
сервером отключать свой ПК от Интернета. Напомню — локальный хостинг
работает без Сети.

3.4. Установка PHP 8


Скачайте на рабочий стол компьютера с сайта
https://windows.php.net/download/ zip-архив PHP 8, соответствующий разряд-
ности вашей ОС. Для 64-разрядной системы — архив с пометкой x64, для 32-
разрядной — с пометкой x86. Обратите внимание: так как мы будем устанавли-
вать PHP в качестве модуля сервера Apache, скачивать надо дистрибутив
Thread Safe (рис. 3.4.1).
Распакуйте архив. Переименуйте распакованную папку на php и переме-
стите ее непосредственно на диск C, чтобы ее адрес был C:\php (рис. 3.4.2).
В папке C:\Apache24\conf с помощью текстового редактора «Блокнот»
откройте файл httpd.conf (рис. 3.4.3), найдите в нем строку

DirectoryIndex index.html

и добавьте к ней

index.php
21
Рис. 3.4.1
Сайт с zip-архивом PHP

Рис. 3.4.2
Перемещаем папку php на диск C

22
Рис. 3.4.3
Файл httpd.conf
Должно получиться

DirectoryIndex index.html index.php

Теперь в самый конец файла httpd.conf добавьте 2 строки:

AddHandler application/x-httpd-php .php


LoadModule php_module "C:/php/php8apache2_4.dll"

Сохраните изменения, закройте файл и перезагрузите компьютер.


Убедимся, что PHP заработал. Для этого в папке C:\Apache24\htdocs со-
здайте текстовый файл и запишите в него следующий код:

<?php
echo "Good !";

Сохраните этот файл под именем, например, test.php. Откройте ваш


браузер и введите в строке адреса http://localhost/test.php. Если появилось со-
общение «Good!», то все в порядке — PHP работает (рис. 3.4.4).

Рис. 3.4.4
Проверяем работу PHP

23
3.5. Установка редактора Notepad++ 8
Скачать Notepad++ можно по адресу https://notepad-plus-
plus.org/downloads/ (рис. 3.5.1).
Устанавливается редактор следующим образом. Запустите скачанный файл.
В первую очередь появится окно выбора языка программы. Оставляем русский
(рис. 3.5.2) и нажимаем кнопку «ОК».

Рис. 3.5.1
Сайт с файлами редактора Notepad++

Рис. 3.5.2
Выбираем язык ПО
В следующем окне нажимаем кнопку «Далее» (рис. 3.5.3). Затем нажати-
ем кнопки «Принимаю» соглашаемся с условиями лицензии (рис. 3.5.4).

Рис. 3.5.3
Нажимаем кнопку «Далее»

24
Рис. 3.5.4
Соглашаемся с условиями лицензии
Теперь нам необходимо выбрать папку для установки программы.
По умолчанию предлагается C:\Program Files\Notepad++. Думаем, такой вари-
ант установки походит всем, поэтому данную папку оставляем без измене-
ний (рис. 3.5.5).

Рис. 3.5.5
Выбираем папку для установки программы

25
Дальше будет окно с выбором компонентов, которые можно установить
вместе с программой. Этот раздел предназначен в первую очередь для опыт-
ных программистов, каковыми мы пока не являемся. Поэтому здесь ничего не
меняем, жмем кнопку «Далее» (рис. 3.5.6).

Рис. 3.5.6
Здесь ничего не меняем, жмем кнопку «Далее»
На последнем этапе перед началом непосредственной установки на вкладке
появится пункт «Create Shortcut on Desktop» (рис. 3.5.7). То есть нам предлагают со-
здать ярлык программы на рабочем столе. Делать это или нет — зависит от вашего
желания. Замечу, что после установки редактора ссылка на него добавится в кон-
текстное меню. Достаточно будет навести указатель мыши на файл, щелкнуть пра-
вой клавишей, и в списке возможных действий вы увидите строку «Edit with
Notepad++». Кликните по ней — и файл будет открыт в редакторе.

Рис. 3.5.7
Нажимаем кнопку «Установить»

26
Разобравшись с вопросом, необходим ли ярлык на рабочем столе или нет,
нажмите кнопку «Установить». Дождитесь окончания процесса (рис. 3.5.8).
После завершения установки вам нужно будет принять еще одно решение:
сразу запустить программу или отложить это дело «на потом». Для этого оставьте
или снимите галочку в пункте «Запустить Notepad++» (рис. 3.5.9). Теперь нажмите
кнопку «Готово». Поздравляю — отныне на вашем компьютере установлен про-
фессиональный текстовый редактор!

Рис. 3.5.8
Ждем окончания процесса

Рис. 3.5.9
Завершение установки
Познакомимся с ним поближе. Как выглядит редактор, вы можете уви-
деть на рисунке 3.5.10.
Notepad++ позволяет:
— выполнять поиск по файлу в разных направлениях;
— производить замену по шаблону;

27
— устанавливать кодировки, в том числе необходимую для нашего про-
екта UTF-8;
— менять страницы, форматируя их по стандартам разных операционных
систем — Windows, Unix или macOS;
— подсвечивать код, выделяя разные по назначению фрагменты, опера-
торы, функции, переменные, комментарии и т. д.;
— выбирать синтаксис документа (благодаря чему меняются варианты
подсветки кода), например HTML, CSS, JavaScript или PHP;
— сохранять файлы с разным расширением;
— менять стили оформления окна программы — вариантов достаточно,
найдется любой по желанию;
— выполнять еще многие и многие операции.

Рис. 3.5.10
Редактор Notepad++ с открытым в нем файлом JavaScript

3.6. Установка файлов zip-архива


на локальный хостинг
Чтобы запустить CMS на локальном хостинге вашего компьютера, вы-
полните следующие действия:
— удалите из папки C:\Apache24\htdocs файл index.html, поставляемый
вместе с сервером Apache, и файл test.php, который мы создавали для проверки
работоспособности интерпретатора PHP;
— скачайте на рабочий стол компьютера zip-архив, который можно полу-
чить по адресу https://editjs.ru/CMS.zip;
— распакуйте архив в папку CMS на рабочем столе;

28
— скопируйте содержимое папки CMS\htdocs_1 — все файлы и каталоги
(это версия системы с уже готовым демонстрационным сайтом);
— поместите скопированные файлы и каталоги в папку
C:\Apache24\htdocs;
— запустите браузер и в строке адреса введите http://localhost/ — загру-
зится главная страница демонстрационного сайта, разработанного на нашей
CMS (рис. 3.6.1);
— чтобы посмотреть код любого из разбираемых в той или иной главе
файлов, открывайте их в редакторе Notepad++.

Рис. 3.6.1
Главная страница проекта, запущенного на локальном хостинге

3.7. Перенос проекта на удаленный хостинг


По этой книге нам предстоит изучить базовую модель CMS и демонстра-
ционный сайт на ее основе. Однако наступит время, когда вы захотите создать
какой-то настоящий проект и перенести его с локального на реальный хостинг.
Как это сделать, будет рассказано в этом разделе.
Итак, предположим, что у вас есть сайт, полностью сделанный на локаль-
ном хостинге персонального компьютера. Все страницы сверстаны, все файлы
написаны, все функции протестированы. Пора выложить свое творение в Ин-
тернет. Как это сделать, каков порядок действий? Схема должна быть примерно
следующая:
1) регистрация доменного имени для сайта;
2) приобретение виртуального хостинга;
3) выяснение особенностей панели управления на хостинге;
4) подготовка проекта к переносу;
5) перенос проекта на хостинг;
29
6) редактирование файла .htaccess и установка прав на файлы PHP;
7) указание DNS-серверов на сайте регистратора домена;
8) тестирование и устранение проблем;
9) продвижение ресурса.
Пойдем по порядку.
Доменное имя лучше всего регистрировать в компании RU-CENTER (АО
«Региональный сетевой информационный центр»). Их сайт: https://-
www.nic.ru/. Зарегистрируйтесь, заполните анкету, выберите свободное имя
для вашего сайта и произведите оплату. Сообщение об успешной регистрации
домена придет вам на почту. Также эту информацию можно будет увидеть на
сайте компании в разделе «Для клиентов».
Следующий этап — приобретение виртуального хостинга у одного из
провайдеров. Сейчас много компаний, предоставляющих серверы для размеще-
ния сайтов за весьма небольшую плату. Не торопитесь с выбором, сравните це-
ны и предоставляемые услуги.
Особенно важно, чтобы хостинг удовлетворял следующим требованиям.
1. Наличие интерпретатора PHP версии 7 или 8 (при этом надо заме-
тить, что CMS испытывалась также с интерпретатором PHP 5-й версии —
и все работало).
2. Возможность размещать файлы PHP в корневой директории сайта.
3. Возможность устанавливать загружаемым по умолчанию фай-
лам index.php.
Обратите внимание! У ряда провайдеров существуют тарифы, со-
гласно которым на виртуальном хостинге нельзя размещать файлы PHP, а
только HTML. Такие тарифы обычно самые дешевые, но нам они не под-
ходят!
Некоторые провайдеры предоставляют тестовый период для проверки хо-
стинга. За тестовый период вносить оплату не надо. Что является хорошей воз-
можностью изучить за это время панель управления, ознакомиться с ее функ-
ционалом и понять, все ли вас устраивает. Ну и, конечно, перенести ваш проект
и проверить его работоспособность на данном хостинге.
Но прежде чем перемещать файлы и папки на хостинг, необходимо под-
готовить ваш проект к этому событию. А именно переформатировать все фай-
лы — php, txt, css, js — из Windows в Unix представление.
Опишем этот шаг подробнее. Сначала создайте на «Рабочем столе» папку, в
которую скопируйте ваш сайт из папки htdocs сервера Apache. Загрузите по очере-
ди все файлы в редактор Notepad++. В нижней части панели редактора найдите
текст «Windows (CR LF)» и сделайте на нем двойной щелчок. В появившемся меню
выберите пункт «Преобразовать в Unix (LF)» и нажмите его (рис. 3.7.1). Файл будет
преобразован. Сохраните изменения.
Перенести файлы и папки на хостинг можно разными способами. Напри-
мер, воспользовавшись механизмами, которые предоставляет панель управле-
ния. Или загрузив проект по протоколу ftp. На второй случай провайдер должен
дать вам логин и пароль для входа.

30
Рис. 3.7.1
Процесс подготовки файла к переносу на хостинг
Сейчас автор познакомит вас со способом, который используют крайне
редко, — и напрасно. Способ настолько прост, что дальше некуда. Для этого
мы воспользуемся стандартной программой «Проводник», которую предостав-
ляет операционная система Windows.
1. Откройте любую папку на вашем компьютере и в левой верхней строке
введите адрес вашего сайта по ftp-протоколу (рис. 3.7.2).

Рис. 3.7.2
Начинаем процесс входа на хостинг по протоколу ftp
2. Появится окно для входа на ftp-сервер. Введите в нем имя пользователя
(логин) и пароль (рис. 3.7.3).

31
Рис. 3.7.3
Ввод имени пользователя и пароля
3. Откроется папка, предназначенная для файлов вашего сайта. Скопи-
руйте все компоненты проекта и переместите их на хостинг (рис. 3.7.4).

Рис. 3.7.4
Загружаем файлы проекта в папку вашего сайта
Следующий этап работ — редактирование файла .htaccess и установка
прав на файлы PHP.

32
Файл .htaccess чаще всего изначально находится в папке, созданной про-
вайдером для вашего сайта. Этот файл необходим, чтобы указать индивидуаль-
ные настройки сервера. Обычно администрация хостинга предоставляет необ-
ходимую информацию по таким настройкам.
Что касается установки прав на файлы PHP, мы рассмотрим два варианта
таких действий.
1. Если вы загружали проект через «Проводник», то можете сразу после
перемещения файлов установить необходимые права. Для этого щелкните на
пиктограмме файла правой (!) кнопкой мыши. В открывшемся меню выберите
пункт «Свойства» (рис. 3.7.5) и кликните на нем. Появится окно настроек
(рис. 3.7.6). Здесь можно установить права на чтение, запись и выполнение для
разных пользователей. Нужно установить следующие разрешения:
— владельцу — на чтение, запись и выполнение;
— группе — на чтение и выполнение;
— всем пользователям — на чтение и выполнение.
Завершайте процесс нажатием кнопки «ОК».
2. Выполнить настройки прав в панели управления. Один из вариантов
может выглядеть так (на примере хостинга SpaceWeb):
— щелкаете правой кнопкой мыши на пиктограмме файла;
— в открывшемся меню выбираете пункт «Изменить права доступа»;
— устанавливаете их для разных пользователей согласно описанию, при-
веденному выше (рис. 3.7.7), — в результате в окне «Разрешение» должны по-
явиться цифры 755.

Рис. 3.7.5
Выбор пункта «Свойства» из контекстного меню

33
Рис. 3.7.6
Установка прав доступа к файлу в «Проводнике»

Рис. 3.7.7
Установка прав доступа в панели настроек хостинга
Теперь узнайте у своего провайдера номера DNS-серверов (обычно их коли-
чество от 2 до 4). На сайте регистратора зайдите на свою страницу, откройте вклад-
ку с перечнем ваших доменов и кликните ссылку «Делегировать» напротив имени
нужного домена (рис. 3.7.8 — показано на примере компании RU-CENTER). От-
кроется страница управления DNS-серверами (рис. 3.7.9). Заполните поля под стро-
кой «DNS-сервер» и нажмите кнопку «Сохранить изменения». Будьте готовы к то-
34
му, что придется подождать несколько часов, пока ваш сайт не станет виден
в Интернете.

Рис. 3.7.8
Страница с перечнем ваших доменов

Рис. 3.7.9
Страница управления DNS-серверами
Этап под названием «тестирование и устранение проблем». Обычно
ошибки в работе сайта возникают по следующим причинам:
— забыли перекодировать какой-то файл из Windows в Unix-формат;
— забыли изменить права доступа какого-нибудь исполняемого файла;
— указали не все необходимые настройки в файле .htaccess.
Обязательно проверьте все функции вашего сайта, чтобы убедиться в от-
сутствии проблем!
Ваш проект заработал? Пора заняться его раскруткой. Дело это настолько
непростое, что автор советует прочитать специальные книги с рекомендациями
по продвижению и аналогичные статьи в Интернете.

35
4. ТЕСТИРОВАНИЕ ПРОГРАММ
Как ни хороши современные веб-обозреватели, но кое-чем они автору ка-
тегорически не нравятся. А именно тем, что в них все очень тщательно проду-
мано и предусмотрено. В результате, даже получив некачественный код, брау-
зеры понимают, чего хотел добиться программист, и демонстрируют правиль-
ную веб-страницу. А это провоцирует разработчиков небрежно относиться к
своим обязанностям. В результате миллионы сайтов за красивой оболочкой со-
держат множество ошибок и нарушений стандартов, которые веб-обозреватели
успешно исправляют.
Удивительно, но даже в ресурсах лидеров мировой веб-индустрии немало
ошибок и нарушений.
Большинство из таких сайтов сделаны на популярных «движках», как
иногда называют всем известные CMS. Ошибки и отклонения от стандартов за-
ложены у них в модулях, формирующих страницы.
Автор относится к тому немногочисленному отряду программистов, ко-
торые выпускают «продукцию» в свет, только добившись абсолютно чистого
кода. Поэтому я проверил разметку, стили и сценарии самым тщательным обра-
зом. Вот перечень проведенных работ:
— проверка работоспособности сценариев в различных браузерах, в том
числе достижение идентичности результатов показа страниц;
— проверка кода с помощью валидаторов и устранение ошибок.
Подробнее об этом — в следующих разделах.

4.1. Тестирование сценариев в браузерах


Один из важнейших этапов тестирования файлов со сценариями — про-
верка их работоспособности в разных веб-обозревателях. Написав программу и
убедившись, что она корректно выполняется в одном браузере, нельзя останав-
ливаться на достигнутом. Ведь может оказаться, что в другом браузере сайт бу-
дет работать совсем не так, как вы ожидали.
Чем в большем количестве веб-обозревателей проведены испытания, тем
лучше для вашего проекта. Это гарантирует, что все посетители сайта увидят на
его страницах именно то, что вы хотели продемонстрировать.
На мой взгляд, обязательный набор разработчика должен включать ми-
нимум 5 веб-обозревателей. Если вы решили серьезно заниматься веб-
программированием, советую установить на свой компьютер эти браузеры.
Расскажу о них по порядку.
1. Браузер Microsoft Edge. Он входит в состав операционной системы
Windows 10, поэтому не нуждается в скачивании и установке. Многие опытные
программисты не любят данный браузер и игнорируют его при проверке сцена-
риев. Автор считает, что это серьезная ошибка. Дело в том, что Microsoft Edge
является браузером по умолчанию в операционной системе Windows 10. Мно-
36
гие пользователи, не такие продвинутые, как программисты, просто использу-
ют то, что у них изначально установлено на ПК. Следовательно, большой про-
цент посетителей Интернета прибегают к услугам Microsoft Edge.
2. Google Chrome. На субъективный взгляд автора, лучший браузер. И,
судя по некоторым опросам, наиболее популярный. Он моложе некоторых сво-
их конкурентов, но снабжен самыми передовыми решениями и очень быстро
развивается. И это неудивительно — ведь за ним стоит такой гигант, как ком-
пания Google. Скачать ПО можно здесь: https://www.google.ru/chrome/.
3. Яндекс.Браузер. После его создания компания «Яндекс» вложила за-
метные средства в рекламу и популяризацию своей разработки. Поэтому дан-
ный браузер стоит у многих владельцев компьютеров с ОС Windows. Этих лю-
дей обязательно надо учитывать. Скачать дистрибутив можно здесь:
https://browser.yandex.ru/.
4. Opera — один из старейших браузеров, существовавших еще со вре-
мен борьбы за лидерство между Netscape Navigator и Internet Explorer. В России
у него много поклонников. Недаром среди российских пользователей Интерне-
та показатель его популярности в несколько раз выше, чем общемировой уро-
вень. Кстати, браузер Opera был одним из первых, кто начал поддерживать таб-
лицы стилей. Скачать программное обеспечение можно здесь:
https://www.opera.com/ru.
5. Mozilla Firefox. В этом браузере сценарии необходимо проверять в
обязательном порядке. У него достаточно высокий уровень популярности. При
этом есть ряд отличий в обработке кода по сравнению с четырьмя перечислен-
ными выше браузерами. Что-то Mozilla Firefox обрабатывает аналогично
остальным браузерам, а что-то — по-своему. Во всяком случае автор неодно-
кратно сталкивался с ситуациями, когда код, отлично работавший в других
браузерах, начинал «капризничать» в Mozilla Firefox. Скачать браузер можно
здесь: https://www.mozilla.org/ru/firefox/.
И уж совсем идеальной можно считать проверку, которую, кроме всего
прочего, удалось провести на устройствах Apple с операционной системой
macOS и браузером Safari.

4.2. Тестирование сценариев в валидаторах


Валидатор — это специальная программа для проверки кода, написанного
на одном из языков, предназначенном для создания веб-проектов. Задача такой
программы — найти ошибки, если они есть, и выдать их перечень разработчику
или сообщить, что код безупречный, если ошибок нет.
Можно сказать и по-другому: назначение того или иного валидатора —
проверять код на соответствие стандартам языка программирования.
Стандарты HTML и CSS разрабатывает Консорциум Всемирной паути-
ны — World Wide Web Consortium (W3C). Он же предоставляет и валидаторы
для проверки разметки страниц и написания таблиц стилей. Вот их адреса в Се-
ти:
— валидатор HTML5 — https://validator.w3.org/;

37
— валидатор CSS3 — http://jigsaw.w3.org/css-validator/.
Данные валидаторы очень удобны. Они позволяют проверять разметку и
таблицы стилей:
— страниц, уже загруженных в Сеть;
— файлов, загруженных в валидатор с вашего компьютера;
— в коде, скопированном из вашего файла и помещенном в специальное
текстовое поле.
Например, после обработки кода разметки все ошибки и предупреждения
с комментариями на английском языке появятся на странице HTML-валидатора
в ее нижней части. При этом ошибки будут сопровождаться надписью Error на
розовым фоне, а предупреждения — надписью Warning на желтом фоне.
Например, строка кода <script type="text/javascript"> получит предупрежде-
ние, так как по современным стандартам указывать тип скрипта не надо, но в то
же время данная ошибка некритична, так как не мешает нормальному отобра-
жению страницы. Зато следующая строка <img src="img/im1.jpg"> будет от-
мечена надписью Error, так как у рисунка пропущен обязательный согласно
стандартам HTML5 атрибут alt.
У HTML-валидатора есть еще одна полезная функция: по завершении
проверки он показывает, сколько времени было потрачено на загрузку вашей
страницы.
Если ошибок нет, валидаторы сообщат вам об этом. Любопытно заметить,
что о безупречности HTML-кода вы получите простое текстовое подтвержде-
ние. А вот качественную таблицу стилей Консорциум Всемирной паутины
предлагает отметить специальным знаком — что-то вроде знака качества, кото-
рый вам будет предложено разместить на странице сайта (по крайней мере так
было на момент написания книги).
Естественно, что разметка и таблицы стилей наших файлов в обязатель-
ном порядке проверялись в этих валидаторах. Результаты такой проверки вы
можете видеть на рисунках 4.2.1 и 4.2.2.
Кстати, совет на будущее. Есть немало заказчиков, которые не примут го-
товый «продукт», пока досконально не проверят, что именно вы им сделали.
Так что чем строже вы оцениваете свою работу, чем чище ваш код, тем больше
вероятность написать качественный сценарий с первого раза и не тратить время
на исправления и переделки. В идеале код должен быть совершенно чистым и
не содержать ни единой ошибки или нарушения стандартов!
Что касается валидатора JavaScript, то здесь ситуация несколько иная. Та-
ких валидаторов много. Автор опробовал несколько и выбрал из них вариант, в
котором реализован самый строгий подход к оценке кода. Вот его адрес: вали-
датор JavaScript — https://beautifytools.com/javascript-validator.php.
Он позволяет вставить в специальное текстовое поле ваш код, после чего
сразу, без нажатия каких-либо кнопок, начинается проверка скрипта. Един-
ственное неудобство этого валидатора, обнаруженное на момент написания
книги, — сообщения об ошибках, предупреждения и рекомендации он выдает в
общем списке, не выделяя пункты этого списка по степени важности.

38
Рис. 4.2.1
Проверка кода одного из файлов в HTML-валидаторе. Как видите, ошибок нет

Рис. 4.2.2
Проверка таблицы стилей одного из файлов в CSS-валидаторе.
Как видите, и здесь ошибок нет
Думаю, читатели уже догадываются, что все сценарии проекта, написан-
ные на JavaScript, были проверены в данном валидаторе. На момент написания
книги отклонений от стандартов языка и нарушений синтаксиса в сценариях
не было (рис. 4.2.3).
Особенность всех упомянутых валидаторов в том, что они находят все
ошибки в разметке, в таблицах стилей, в коде программ, в том числе пропущен-
ные символы, опечатки (но не в обычном тексте), необъявленные переменные
и т. д. Понятно, что задача хорошего программиста — исправить эти ошибки и
получить идеально чистый и правильный код. Что и было сделано автором.

39
Автор пытался тестировать и код PHP, но, хотя PHP-валидаторы суще-
ствуют, ни одного надежного на просторах Сети обнаружить не удалось.

Рис. 4.2.3
Проверка одного из сценариев. И опять ни одной ошибки

4.3. Кодировка файлов


Создавая примеры, автор сохранял их в кодировке utf-8. Если вы
будете самостоятельно писать код, обязательно сохраняйте все файлы —
php, txt, css, js — в формате utf-8.
Как это сделать? Откройте редактор Notepad++ и наберите код програм-
мы. Затем в меню редактора кликните закладку «Кодировки» и посмотрите, в
каком формате написан файл. Если не в utf-8, то в открывшемся меню щелк-
ните на строке «Преобразовать в UTF-8» (рис. 4.3.1). Сохраните результаты
вашей работы.

Рис. 4.3.1
Выбор кодировки для файла

40
5. СТРУКТУРА ПРОЕКТА
Прежде чем начать детальное рассмотрение проекта, разберемся с его
«комплектующими».
Напомню, что архив с полным набором всех компонентов находится по
адресу: https://editjs.ru/CMS.zip.
Архив состоит из 3 каталогов:
1) htdocs — содержит «чистую» CMS с набором установок по умолча-
нию, которые разработчик меняет в процессе конструирования сайта;
2) htdocs_1 — CMS с готовым вариантом сайта, на примере которого мы
будем рассматривать принцип действия системы;
3) htdocs_2 — практически аналогичный сайт, отличающийся от приве-
денного выше некоторыми настройками размеров блоков.
По сути, ресурсы из htdocs_1 и htdocs_2 демонстрируют один и тот же сайт,
разница в положении меню: в первом случае оно горизонтальное, во втором — вер-
тикальное.

5.1. Комплектующие системы


Теперь на примере каталога htdocs выясним, из чего состоит каждый эк-
земпляр системы.
Внутри скрываются 22 основных файла, написанные на PHP, иконка для
демонстрации во вкладке браузера (файл favicon.ico) и 8 папок:
— content — по умолчанию содержит два текстовых файла с данными
главной страницы (index.txt), а также страницы для отправки сообщений адми-
нистратору сайта (cont.txt), в этой же папке создаются файлы для новых стра-
ниц;
— css — содержит таблицы стилей сайта (index.css) и системы управле-
ния;
— draw — хранит картинки, используемые в системе управления;
— file — предназначена для файлов, которые владелец ресурса хочет
продемонстрировать посетителям, например doc, rtf, xl, pdf, txt;
— img — место хранения изображений, которые администратор разме-
щает на страницах сайта;
— js — здесь собраны JavaScript-сценарии, обслуживающие сайт (index.js
и cont.js) и систему управления;
— pict — папка для рисунков, образующих дизайн сайта;
— set — содержит ряд файлов, необходимых для функционирования си-
стемы управления, а также файлы данных рекламы и нижнего блока.
Из 22 основных файлов, написанных на PHP, один является сборщиком
страниц (index.php), еще один (cont.php) выполняет отправку сообщений ад-
министратору, остальные 20 обслуживают систему управления.

41
5.2. Сайт
Итак, часть файлов из архива образуют структуру сайта, а другая часть —
системы управления. Сначала выясним, как взаимодействуют файлы, необхо-
димые для работы сайта.
«Скелет» любой страницы — HTML-код с разметкой (рис. 5.2.1). Непо-
средственно в теле документа прописаны все его элементы, кроме:
— основного содержания;
— рекламы;
— нижнего блока;
— таблицы стилей;
— сценариев на JavaScript (они необходимы для реализации ряда функ-
ций сайта).
Созданием разметки на странице управляет оболочка, написанная на PHP.

Рис. 5.2.1
Взаимодействие файлов сайта
Таблица стилей и сценарии добавляются в HTML-код в процессе загрузки
документа, минуя «посредников». А вот основное содержание, реклама и со-
держание нижнего блока вставляются с помощью PHP-оболочки из соответ-
ствующих txt-файлов. В PHP-коде предусмотрен механизм определения, какой
контент должен быть загружен для той или иной страницы.
У оболочки есть еще одна функция: проверять, соответствует ли адрес в
строке запроса адресу реально существующей страницы. Если искомая страни-
ца отсутствует, то никаких предупреждений не выводится, а просто загружает-
ся главная страница.

5.3. CMS
Как вы уже поняли, структура сайта довольно простая, соответственно
объем кода для обслуживания его страниц в несколько раз меньше, чем объем
кода системы управления. Ее структура показана на рисунке 5.3.1.
Вход в CMS и вывод меню обеспечивает один и тот же файл — cms.php.
Сначала администратор видит чистую страницу с текстовым полем для пароля.
После его ввода выполняется проверка, а затем, если пароль верный, открыва-
ется страница меню системы управления. Далее, когда необходимо перейти
42
в один из разделов, пароль вместе с запросом методом POST отправляется со-
ответствующему файлу. Здесь он вновь проверяется, после чего загружается
необходимая страница.
В этом месте сделаем небольшое отступление и поясним, что происходит,
если в систему пытается проникнуть злоумышленник, который знает структуру
файлов CMS. Во-первых, попытка передать пароль методом GET в теле запроса
окончится неудачей. Во-вторых, если недоброжелатель создал нужную форму и
отправил ее методом POST в качестве запроса к любому файлу вашей системы
управления (подчеркиваем — к любому), то он столкнется с защитой в виде
обязательной проверки пароля. Правда, реакция на попытку несанкциониро-
ванного использования файлов CMS может быть разной. Об этом мы погово-
рим в дальнейшем.

Рис. 5.3.1
Структура системы управления
Вернемся к теме. Основное меню CMS составляют 6 разделов.
1. Конструктор сайта. Его задача — компоновка блоков страниц, установ-
ка их размеров, выбор цвета фонов.
2. Специальные настройки. Здесь вы закачиваете необходимые для ди-
зайна сайта изображения, удаляете лишние, а также фиксируете адрес элек-
тронной почты администратора — именно на нее будут приходить сообщения
от посетителей.
3. Замена пароля. Все экземпляры CMS поставляются с паролем admin.
Естественно, его надо заменить на придуманный вами.
4. Список страниц. Тут вы видите, какие страницы существуют в вашем
проекте, добавляете новые, удаляете старые. Только из данного раздела вы мо-
жете перейти в режим редактирования той или иной страницы (в раздел «Ре-
дактор страниц»).
5. Добавление фото. Обеспечивает добавление, просмотр, удаление изоб-
ражений, которые администратор размещает на страницах сайта.

43
6. Добавление файлов. В этом разделе добавляются и удаляются файлы,
доступ к которым владелец ресурса хочет предоставить посетителям. Это могут
быть документы в формате doc, rtf, xl, pdf, txt и др.
В разделе 5.1 мы уже коротко прокомментировали состав компонентов
сайта и системы управления. Как вы уже поняли, сайт и каждый из разделов
меню управляется не одним, а несколькими файлами. Структуру их взаимодей-
ствия, принципы работы, особенности выполнения программного кода нам
предстоит выяснить в последующих главах.

44
6. ВХОД И ГЛАВНАЯ СТРАНИЦА CMS
Вы уже переместили систему управления на локальный хостинг. Посмотрели
вариант простого сайта, сделанного автором в демонстрационных целях. Теперь
пора разбираться, как и с помощью чего создаются этот и любой другой сайты.
Откройте браузер и наберите в строке адреса http://localhost/cms.php. Вы
окажетесь на странице входа в систему с полем для ввода пароля.
Для эксперимента наберите адрес другого раздела, например добавления
фотографий: http://localhost/photo.php. Вы вновь увидите поле для ввода пароля.
Эти примеры подтверждают уже изложенный факт — без знания пароля в
систему не войти. С правил входа мы и начнем изучение CMS.
Доступ в систему управления обеспечивают следующие файлы:
— cms.php — файл, создающий форму ввода пароля и главную страницу
CMS;
— set/passw.php — файл хранения пароля;
— set/tab.php — таблица шифрования;
— css/cms.css — определяет дизайн главной страницы CMS;
— css/every.css — определяет внешний вид вспомогательного меню на
всех страницах системы управления.
Последняя таблица стилей одинакова для всех разделов CMS, поэтому в
дальнейшем мы не станем упоминать о ней. Разбирая код, вы неоднократно
увидите вот такой адрес в заголовочных частях документов:

<link rel="stylesheet" href="css/every.css">

6.1. Вход в систему


Итак, вы запустили файл cms.php и на экране компьютера увидели стра-
ницу, показанную на рисунке 6.1.1. Здесь всего один элемент — поле ввода па-
роля. Наберите в нем admin и нажмите «Enter». Пароль верный, следовательно,
вход будет успешным, и теперь появится основное меню CMS. О нем мы пого-
ворим в следующем разделе, а пока выясним, как выполняется проверка.
Вообще, в профессиональных системах пароли шифруются сложным много-
ступенчатым образом. У нас CMS простая, соответственно, городить огород мы не
станем, но все-таки предусмотрим два элементарных способа защиты пароля.

Рис. 6.1.1
Вход в систему управления

45
Способ первый — хранить пароль в файле с расширением .php. Он нахо-
дится в папке set, называется passw.php, а его содержимое выглядит так:

<?php
tiТоjR3Vr14wylIFS0uq

В чем фокус такого способа хранения? Если злоумышленник попробует


запустить данный файл, чтобы узнать пароль, в ответ он получит сообщение об
ошибке примерно такого вида: «Parse error: syntax error, unexpected end of file
in C:\Apache24\htdocs\set\passw.php on line 2», что в переводе означает
«Ошибка синтаксического анализа: синтаксическая ошибка, неожиданный ко-
нец файла в C:\Apache24\htdocs\set\passw.php в строке 2». Или, как вариант (на
примере хостинга SpaceWeb), «Сейчас эта страница не работает». Так что
узнать пароль не получится.
Теперь гипотетически предположим, что недоброжелатель все-таки про-
ник во внутренности файла passw.php. Вы помните, что у нас пароль был
admin. А тут в файле он увидит следующую запись: tiТоjR3Vr14wylIFS0uq.
Почему? Дело в том, что пароль еще и зашифрован. Правда, система шифрова-
ния довольно простая, но все-таки это шифрование.
Обратите внимание: незашифрованный пароль должен содержать от 5 до
10 знаков и состоять только из латинских букв (в любом регистре) и цифр от 0 до 9.
Мы выяснили, что представляют собой механизмы защиты пароля. Мож-
но переходить к разбору кода, который обеспечивает функцию входа.
Каждый файл нашего ПО начинается с одного и того же блока:

<?php
if(isset($_POST['pas']) && strlen($_POST['pas'])>4
&& strlen($_POST['pas'])<11)
{
...
}
else
echo '...
<form method="POST" action="cms.php">
<p id="passw"><input type="password"
name="pas" minlength="5" maxlength="10"
id="pas"></p>
</form>';
?>

Первым делом проверяем наличие пароля в переданном запросе и его


длину. Она должна быть не менее 5 символов и не более 10. Проверка длины
необходима на случай, если злоумышленник пытается «подвесить» нашу си-
стему, введя в своей форме чрезвычайно длинный набор символов.
Если условия входа нарушены, то загружаем в браузер форму для ввода
пароля:

else
echo '...
<form method="POST" action="cms.php">
46
<p id="passw"><input type="password"
name="pas" minlength="5" maxlength="10"
id="pas"></p>
</form>';
Подчеркну, что когда вы набираете в браузере адрес
http://localhost/cms.php, в этом запросе пароль отсутствует, поэтому браузер
как раз и выводит данную форму на страницу. Вот теперь мы и вводим пароль.
Идем дальше. Считаем, что первый этап проверки был успешным. Сле-
дующий шаг — сравнение введенного пароля с зашифрованным, хранящимся в
файле passw.php. Для этого загружаем таблицу шифрования, которая находит-
ся в папке set в файле tab.php:

include 'set/tab.php';

Вот ее код:
<?php
$ar = ['A' => 'сеN0',
'a' => 'tiТ',
...,
'9' => 'ibqCE'];

Здесь мы создаем массив, где каждому разрешенному символу соответ-


ствует определенный шифр.
Следующий этап — шифрование пароля, полученного из формы:
$pas=$_POST['pas'];
$let=str_split($_POST['pas']);
$sum='';
for($t=0; $t<count($let); $t++)
$sum.=$ar[$let[$t]];

Сначала мы «разбираем» пароль на отдельные символы


$let=str_split($_POST['pas']);

а затем в цикле

for($t=0; $t<count($let); $t++)

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


все в одну строку:
$sum.=$ar[$let[$t]];

Считываем зашифрованный пароль из файла passw.php


$passw=file('set/passw.php');

и сравниваем его с шифром, полученным в цикле:


if($sum==$passw[1])

47
Если сравнение было успешным, выводим содержимое страницы:

if($sum==$passw[1])
{
echo '...';
}

Введен неверный пароль? Вновь загружаем в браузер форму для его по-
вторного ввода:

else
echo '...
<form method="POST" action="cms.php">
<p id="passw"><input type="password"
name="pas" minlength="5" maxlength="10"
id="pas"></p>
</form>';

Обратите внимание: после того как вы начали использование данной


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

6.2. Меню системы


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

<div id="basis">
...
</div>

Первый такой блок — это вспомогательное меню, расположенное в верх-


ней части страницы и отделенное от остального содержимого тонкой чертой:

<div id="bas">
<a href="index.php" target="_blank">
<img src="draw/site.png" id="site" alt="..."></a>
<span id="system">Система управления сайтом</span>
<form method="POST" action="cms.php">
<input type="hidden" name="pas"
value="'; echo $pas; echo '">
<input type="image" src="draw/home.png" id="home"
alt="..." title="...">
</form>
</div>
В данном меню всего два пункта.

48
1. Просмотр сайта — ссылка в левом верхнем углу в виде рисунка с изоб-
ражением монитора. Если щелкнуть по нему, то в новой вкладке загрузится
главная страница сайта.
2. Возврат на главную страницу системы управления — ссылка в правом
верхнем углу в виде картинки с изображением крыш домов. Клик на ней воз-
вращает администратора в меню CMS.

Рис. 6.2.1
Меню системы управления
Как видно из приведенного ранее кода, рисунок с крышами на самом деле
является кнопкой отправки формы, состоящей всего из одного скрытого поля с
паролем:

<input type="hidden" name="pas"


value="'; echo $pas; echo '">

Здесь PHP отвечает за фиксацию пароля в скрытом поле:

value="'; echo $pas; echo '"

Возврат в главное меню CMS происходит путем передачи запроса и па-


роля методом POST.
Под вспомогательным меню расположено основное, состоящее, как мы
уже знаем, из 6 блоков:

<div id="designer" class="mainmenu">


<form method="POST" action="design.php">
<input type="hidden" name="pas"
value="'; echo $pas; echo '">

49
<input type="image" src="draw/designer.png" alt="..."
title="...">
<br>Конструктор сайта
</form>
</div>

...
<div id="files" class="mainmenu">
<form method="POST" action="file.php">
<input type="hidden" name="pas"
value="'; echo $pas; echo '">
<input type="image" src="draw/files.png" alt="..."
title="...">
<br>Добавление файлов
</form>
</div>

Картинки служат кнопками отправки форм тому или иному файлу с про-
граммой соответствующего блока. Как и в предыдущем случае, в каждой форме
всего одно скрытое поле с паролем. Адреса файлов указываются в атрибутах
action-форм. Данные опять же передаются методом POST.
Кликнув на любой из шести основных картинок, вы попадаете в соответ-
ствующий раздел системы управления. О них сейчас и пойдет речь.

50
7. ЗАМЕНА ПАРОЛЯ
Рассмотрение функций разделов мы начнем не в порядке их расположе-
ния в меню, а с того блока, к которому надо обратиться в первую очередь. Речь
идет о замене стандартного пароля. Это необходимо сделать в целях обеспече-
ния безопасности вашего экземпляра CMS.
На всякий случай напомним, что в системе заданы следующие требова-
ния к незашифрованному паролю:
— он должен содержать от 5 до 10 знаков;
— в нем можно использовать только латинские буквы (в любом регистре)
и цифры от 0 до 9.
Также напомним, о чем говорилось в предыдущей главе. Порядок ваших
действий должен быть такой:
1) поменяйте наборы символов в таблице шифрования;
2) в соответствии с ее новыми значениями вручную зашифруйте пароль
admin;
3) запишите получившийся результат во второй строке файла
set/passw.php и не нажимайте после пароля «Enter»!
Теперь можно входить в систему управления и придумывать новый пароль.
Программный блок замены пароля объединяет следующие файлы:
— pas.php — формирует страницу замены пароля в системе управления;
— css/pas.css — определяет дизайн страницы;
— js/pas.js — формирует Ajax-запрос на перезапись пароля и обрабаты-
вает ответ серверной программы;
— newpas.php — программа записи пароля;
— set/passw.php — файл хранения пароля.
Заметим, что все операции передачи данных серверным программам во
всех блоках системы управления происходят в фоновом режиме по технологии
Ajax. В дальнейшем автор не станет упоминать об этом факте, считая, что чита-
тели запомнили данную информацию.

7.1. Страница замены пароля


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

<form method="POST" id="newp" name="dat">


Новый пароль: <input id="newpas" name="newpas"
minlength="5" maxlength="10">
<input type="hidden" name="pas"
value="'; echo $pas; echo '">

51
<input type="button" id="but" value="Записать">
</form>

Нажатие кнопки запускает процесс, который мы подробно рассмотрим в


следующем разделе.

Рис. 7.1.1
Страница замены пароля

7.2. Замена пароля


Нажатие кнопки на странице замены пароля обрабатывается скриптом
js/pas.js.
Регистрацию обработчика для кнопки производим после загрузки в брау-
зер документа с формой:

addEventListener("load", function()
{
document.getElementById("but").
addEventListener("click", pas);
...
});

Это стандартная процедура для всех файлов JavaScript, существующих в


проекте. Сначала загружаем документ, а уже потом регистрируем обработчики,
вводим переменные, пишем функции:

addEventListener("load", function()
{
// Обработчики, переменные, функции

...
});

Так мы будем поступать всегда. Запомните это.


Теперь создадим регулярное выражение для проверки пароля. В нем мо-
гут быть только латинские буквы

a-z

52
и цифры

\d

Знаков должно быть несколько:

[a-z\d]+

Строка может начинаться только с этих символов и заканчиваться


только ими:

/^[a-z\d]+$/

И, естественно, добавим режим игнорирования регистра букв, так как они


могут быть заглавными и строчными:

/^[a-z\d]+$/i

Получившийся шаблон присвоим переменной:

let rv=/^[a-z\d]+$/i;

Для сокращения кода введем 2 переменные, указывающие на поле ввода


пароля и кнопку отправки данных:

let np=document.getElementById("newpas");
let but=document.getElementById("but");

Теперь запишем функцию pas:

function pas()
{
...
}

У нее три стадии выполнения. Первая — проверка длины нового пароля:


if(np.value.length<5 || np.value.length>10)
{
...
}

Если пароль слишком короткий или чересчур длинный, выводим на


кнопку отправки данных сообщение об этом (рис. 7.2.1):
but.value="НЕВЕРНАЯ ДЛИНА ПАРОЛЯ !";

и прерываем выполнение сценария:

return false;
53
Здесь мы опять сталкиваемся с приемом, который используется во всех
блоках системы управления. Любые сообщения от любого сценария мы всегда
выводим на кнопку, которая запускает данный сценарий. Так что привыкайте к
этому.

Рис. 7.2.1
Программа сообщает, что пароль слишком короткий
Второй этап — проверка введенного пароля на соответствие регулярному
выражению:
if(rv.test(np.value)==false)
{
...
}
Найдены отклонения от шаблона? Сообщаем об этом администратору
(рис. 7.2.2) и прерываем выполнение функции:
but.value="НЕДОПУСТИМЫЕ СИМВОЛЫ В ПАРОЛЕ !";
return false;

Рис. 7.2.2
Использовав в начале пароля кириллическую букву «я», мы допустили ошибку
Проверка была успешной? Переходим к третьему шагу — отправке дан-
ных. Для этого создаем новый объект данных из формы:
let dt=new FormData(document.forms.dat);

и новый экземпляр XMLHttpRequest:


let re=new XMLHttpRequest();

Формируем запрос:
re.open("POST", "newpas.php", true);

54
и отправляем его серверной программе:
re.send(dt);

Задаем сценарию инструкцию вывести ответ в теле отдельной функции show:


re.addEventListener("load", show);

Что происходит дальше? Запускается серверный скрипт newpas.php.


Первая его задача — проверить «законность» обращения к программе.
Для этого из скрытого поля формы передается текущий пароль. В файле
newpas.php проверка выполняется по методике, изученной нами в главе 6, ко-
гда мы обсуждали механизм входа в систему управления. Вот фрагмент кода,
отвечающий за данную процедуру:
<?php
if(isset($_POST['pas']) && strlen($_POST['pas'])>4
&& strlen($_POST['pas'])<11)
{
include 'set/tab.php';

$let=str_split($_POST['pas']);
$sum='';

for($t=0; $t<count($let); $t++)


$sum.=$ar[$let[$t]];

$passw=file('set/passw.php');

if($sum==$passw[1])
{
...
}
else
echo '2';
}
else
echo '2';

Этот фрагмент нам уже знаком. Единственное отличие от кода из гла-


вы 6 — в случае попытки несанкционированного запуска программы или сбоя
при передаче пароля открывается не страница с формой для входа, а возвраща-
ется цифра 2. Как она интерпретируется сценарием из файла pas.js, мы разбе-
ремся чуть позже. Взломщик же увидит в своем браузере просто цифру 2.
Если информация поступает законным порядком, выполняется вторая
проверка — введено ли значение в поле newpas и укладывается ли количество
символов в установленные границы:
if(isset($_POST['newpas'])
&& strlen($_POST['newpas'])>4
&& strlen($_POST['newpas'])<11)
{
...
}

55
Если проверка завершается успешно, происходит шифрование нового па-
роля уже знакомым нам способом:
$let=str_split($_POST['newpas']);
$sum='';
for($t=0; $t<count($let); $t++)
$sum.=$ar[$let[$t]];

Затем формируется строка для записи в файл passw.php:


$fin="<?php\n".$sum;

Осталось записать новый пароль. Если операция прошла успешно,


JavaScript-сценарию отправляется в ответ цифра 1:
if(file_put_contents('set/passw.php', $fin))
echo "1";

Если запись не состоялась, возвращается цифра 2:


else
echo "2";

Настало время скрипту pas.js обработать ответ. За это, как вы помните,


отвечает функция show:
function show()
{
let ans=this.responseText;
if(ans==1)
but.value="ПАРОЛЬ ИЗМЕНЕН !";
if(ans==2)
but.value="ОШИБКА !";
setTimeout(()=>{but.value="Записать";}, 2000);
}

При успешной записи нового пароля (когда ответ серверной программы 1) на


кнопку выводится сообщение «ПАРОЛЬ ИЗМЕНЕН !» (рис. 7.2.3). В случае не-
удачи (ответ в виде цифры 2) на кнопке появится предупреждение «ОШИБКА !».
Через две секунды кнопке будет возвращена изначальная надпись:
setTimeout(()=>{but.value="Записать";}, 2000);

Рис. 7.2.3
Сообщение об успешной записи нового пароля
56
8. СПЕЦИАЛЬНЫЕ НАСТРОЙКИ
И опять рассмотрение блоков системы продолжится не по порядку их
расположения в основном меню, а в последовательности, которая упрощает по-
нимание функциональности CMS. На этот раз мы рассмотрим страницу «Спе-
циальные настройки».
К специальным настройкам (их можно еще назвать предварительными)
относятся:
— функция закачки изображения, которое необходимо в дизайне сайта;
— функция просмотра закачанных рисунков и удаление лишних;
— функция записи адреса электронной почты администратора.
Эти действия обеспечиваются следующим набором файлов:
— sett.php — формирует раздел специальных настроек в системе управ-
ления;
— css/sett.css — определяет дизайн страницы;
— js/sett.js — формирует запросы на фиксацию настроек и обрабатывает
ответы серверных программ;
— newsett.php — программа загрузки картинок, используемых в дизайне
страниц сайта;
— delsett.php — файл удаления лишних картинок;
— newmail.php — файл записи адреса электронной почты администратора;
— set/email.txt — файл хранения адреса электронной почты администратора.

8.1. Страница специальных настроек


Указанную страницу вы можете видеть на рисунке 8.1.1. Кроме вспомо-
гательного меню она содержит еще три поля.
1. «Добавить новое фото для дизайна сайта» с кнопками «Выберите
файл» и «Добавить».
2. «Удалить фото» с кнопками «Посмотреть/Выбрать» и «Удалить».
3. «E-mail администратора» с кнопкой «Записать» (поле уже содержит
адрес, который записан в соответствующий файл по умолчанию — его и надо
заменить на реальный).
Чтобы создать такую страницу, мы используем следующую разметку. Для
поля закачки рисунка она выглядит так:
<form method="POST" id="newp" name="dat1"
enctype="multipart/form-data">
Добавить новое фото для дизайна сайта:
<input type="file" id="newfoto" name="newfoto">
<input type="hidden" name="pas"
value="'; echo $pas; echo '">
<input type="button" id="but1" value="Добавить">
</form>

57
Как видите, это обычная форма с указанием enctype, полем file и скры-
тым полем hidden с паролем, который проверяется скриптом записи, чтобы
убедиться в «законности» поступающих данных. Обратите внимание: в име-
ни загружаемого фото должны быть только латинские буквы и цифры без
пробелов!

Рис. 8.1.1
Раздел специальных настроек
Форма удаления лишних снимков:
<form method="POST" id="delp" name="dat2">
Удалить фото: <input id="photo" name="photo">
<input type="hidden" name="pas"
value="'; echo $pas; echo '">
<input type="button" id="but"
value="Посмотреть / Выбрать">
<input type="button" id="but2" value="Удалить">
</form>

Нажатие кнопки «Посмотреть/Выбрать» открывает вкладку со всеми


имеющимися в папке pict изображениями (рис. 8.1.2). По умолчанию она уже
содержит два картинки — _test_.jpg и net.jpg. Первую можно смело удалить,
заменив необходимым вам фото. А вот вторую надо оставить — она принимает
некоторое участие в просмотре снимков на страницах сайта. Для выбора удаля-
емого кадра нажмите на него указателем мыши. Вкладка закроется, а адрес вы-
бранного рисунка окажется в поле «Удалить фото». Жмете кнопку «Удалить»,
и картинка исчезает с сервера и вкладки (при закачке нового фото, хотя переза-
грузка страницы и не происходит, изображение сразу появляется во вкладке —
обновлять страницу не надо!).
Вкладку просмотра и выбора картинок создает следующий код:

<div id="list_d">
<img src="draw/close.png" id="list_i" alt="Закрыть"
title="Закрыть">
<p id="p_list">';

58
$opdir=opendir("pict");
while($redir=readdir($opdir))
{
if(strpos($redir, "."))
echo '<img class="roster" src="pict/'.$redir.'"
id="'.$redir.'" alt="Фото">';
}
closedir($opdir);

echo '</p></div>

Открываем директорию

$opdir=opendir("pict");

и в цикле

while($redir=readdir($opdir))
{
...
}

получаем имена всех фотографий, предназначенных для использования в ди-


зайне сайта.

Рис. 8.1.2
Вкладка просмотра и удаления изображений
К сожалению, в список попадут такие элементы, как точка и две точки
(. и ..), которые обозначают текущий и вышестоящий каталоги.
Чтобы преодолеть эти проблемы, устроим проверку:

if(strpos($redir, "."))
...

59
Операция
strpos($redir, "."))

«отсекает» обозначения каталогов.


Точка и две точки возвратят индекс первого вхождения элемента — ноль.
Условие if воспримет данный результат как false и не включит эти элементы в
список изображений. Зато у остальных картинок, у которых точка стоит перед
расширением после нескольких символов имени файла, индекс вхождения ока-
жется больше нуля и условие примет значение true. То есть реальные фотогра-
фии окажутся на вкладке:

echo '<img class="roster" src="pict/'.$redir.'"


id="'.$redir.'" alt="Фото">';

Обратите внимание: в качестве id мы присваиваем рисунку имя его файла:


id="'.$redir.'"

На последнем шаге закрываем директорию

closedir($opdir);

Вкладка, которую мы сейчас разобрали, необходима не только для удале-


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

$mai=file('set/email.txt');

echo '<form method="POST" id="mail" name="dat3">


E-mail администратора: <input id="admin" name="admin"
value="'; echo $mai[0]; echo '"
minlength="8" maxlength="50">
<input type="hidden" name="pas"
value="'; echo $pas; echo '">
<input type="button" id="but3" value="Записать">
</form>

Здесь мы считываем существующий адрес из файла set/email.txt

$mai=file('set/email.txt');

и создаем форму для замены e-mail. Минимальная длина адреса электронной


почты — 8 знаков: два в начале + @ + два символа доменного имени второго
уровня + точка + два символа доменного имени первого уровня (теоретически
возможны и более короткие имена, но они настолько уникальное явление, что

60
учитывать их существование не имеет смысла). Максимальная длина e-mail,
установленная в системе, 50 знаков.

8.2. Добавление фото


В этом разделе нам придется немного забежать вперед. Дело в том, что
шаблон сайта устроен так, что в его верхней части обязательно находится ка-
кая-то картинка. Размеры данной картинки:
— ширина должна быть равна планируемой ширине страницы — например,
в двух демонстрационных сайтах из zip-архива это одна и та же картинка шириной
1000 пикселей;
— высота — произвольная, по вашему желанию.
Итак, вы подготовили рисунок, и его можно закачивать на сервер (в папку
pict). Эту процедуру выполняют два файла:
1) js/sett.js — он формирует запросы и обрабатывает ответы серверных
программ;
2) newsett.php — загружает изображения.
Разберем, как сценарий JavaScript участвует в загрузке снимков.
Естественно, что сначала регистрируем обработчик нажатия кнопки «До-
бавить»:

document.getElementById("but1").
addEventListener("click", cont);

Теперь создаем на эту кнопку ссылку:


let but1=document.getElementById("but1");

Первое, что делает функция cont, — проверяет, загружена ли картинка:

if(document.getElementById("newfoto").value=="")
{
but1.value="ВЫ НЕ ЗАГРУЗИЛИ ИЗОБРАЖЕНИЕ !";
return false;
}

Если вы не выбрали рисунок из имеющихся на вашем компьютере, то на


кнопке выводится предупреждение «ВЫ НЕ ЗАГРУЗИЛИ ИЗОБРАЖЕНИЕ !».
А если снимок был загружен? С тем, что происходит дальше, вы уже зна-
комы по главе 7, где была описана замена пароля. Аналогичный код JavaScript
для отправки данных серверной программе мы используем и в этот раз:

else
{
let dt=new FormData(document.forms.dat1);
let re=new XMLHttpRequest();
re.open("POST", "newsett.php", true);
re.send(dt);
re.addEventListener("load", show);
}

61
Но теперь функция show будет несколько сложнее:

function show()
{
let ans=this.responseText;
let arans=ans.split(";");
if(arans[0]==1)
{
...
}
if(arans[0]==2)
...
...
}

Ответ от серверной программы newsett.php, которую мы разберем чуть


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

let ans=this.responseText;
let arans=ans.split(";");

Теперь по первому значению arans[0] определяем, была ли загрузка


успешной. Если «нет», выводим соответствующее предупреждение на кнопку:

if(arans[0]==2)
but1.value="НЕКОРРЕКТНЫЕ ДАННЫЕ !";

Если «да», то выполняем обработку второго параметра:


if(arans[0]==1)
{
but1.value="ИЗОБРАЖЕНИЕ ЗАГРУЖЕНО !";
let point='<img class="roster"
src="pict/'+arans[1]+'"
id="'+arans[1]+'" alt="Фото">';
document.getElementById("p_list").
insertAdjacentHTML("beforeend", point);
}

В этом блоке мы сначала сообщаем об успешной загрузке

but1.value="ИЗОБРАЖЕНИЕ ЗАГРУЖЕНО !";

а затем формируем новый элемент img с адресом и id, соответствующими име-


ни нового файла, полученного из второго значения arans[1]:
let point='<img class="roster"
src="pict/'+arans[1]+'"
id="'+arans[1]+'" alt="Фото">';
На последнем этапе добавляем новое изображение в конец списка уже
имеющихся на вкладке:

62
document.getElementById("p_list").
insertAdjacentHTML("beforeend", point);

и возвращаем кнопке изначальную надпись:

setTimeout(()=>{but.value="Записать";}, 2000);

Теперь посмотрим, что делает серверный скрипт newsett.php.


Начало у данной программы традиционное — проверка пароля. Этот
фрагмент мы опустим — как вы уже видели, он стандартный для всех сервер-
ных приложений нашего проекта. Затем мы проверяем, передан ли файл и явля-
ется ли он изображением:

if(($_FILES['newfoto']['error']==0) &&
(strpos($_FILES['newfoto']['type'], 'image')===0))
{
...
}

Если «нет» (например, вместо картинки вы загрузили текстовый файл),


отправляем отрицательный ответ:

else
echo '2';

Если проверка завершилась успешно, закачиваем файл во временное хра-


нилище и переносим его оттуда в папку pict:
move_uploaded_file($_FILES['newfoto']['tmp_name'],
'pict/'.basename($_FILES['newfoto']['name']));

Осталось передать в качестве ответа необходимые данные:

echo '1;'.basename($_FILES['newfoto']['name']);

Как они обрабатываются и интерпретируются, мы уже выяснили.

8.3. Просмотр фото


Просмотром изображений на вкладке управляют три анонимные функ-
ции.
Первая выводит вкладку на экран после нажатия кнопки «Посмот-
реть/Выбрать»:
document.getElementById("but").
addEventListener("click", function()
{
document.getElementById("list_d").
style.visibility="visible";

63
});

Вторая скрывает вкладку после клика на значке крестика в верхнем левом


углу:
document.getElementById("list_i").
addEventListener("click", function()
{
document.getElementById("list_d").
style.visibility="hidden";
});

Третья функция управляет добавлением имени файла в поле «Удалить


фото»:

document.getElementById("p_list").
addEventListener("click", function(ev)
{
if(ev.target.tagName=="IMG")
{
let pho=ev.target.src.split('pict/');
document.getElementById("photo").value=pho[1];
document.getElementById("list_d").
style.visibility="hidden";
}
});

Здесь, как вы видите, проверяется, на каком элементе произошел щелчок


мышью. Если на фотографии
if(ev.target.tagName=="IMG")

то последовательно выполняются три инструкции.

Первая необходима, чтобы определить имя графического файла. Этот ре-


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

let pho=ev.target.src.split('pict/');

Полученное значение добавляется в поле photo:

document.getElementById("photo").value=pho[1];

После чего вкладка закрывается:

document.getElementById("list_d").
style.visibility="hidden";

64
8.4. Удаление фото
Этот процесс «доверен» серверной программе delsett.php. Она очень про-
стая. За проверкой пароля следует проверка заполнения поля photo:

if(isset($_POST['photo']) &&
strlen($_POST['photo'])>3)
{
...
}
else
echo '2';

Если адрес снимка присутствует, то происходит его удаление:

unlink('pict/'.$_POST['photo']);

и в качестве ответа передается единица:

echo '1';

Взаимодействие с серверной программой обеспечивает один из сценариев


файла sett.js.
Регистрируем обработчик:

document.getElementById("but2").
addEventListener("click", del);

Объявляем переменную — ссылку на кнопку «Удалить»:

let but2=document.getElementById("but2");

и пишем функцию del, в которой проверяем заполнение поля «Удалить фото» и


производим отправку данных серверному скрипту:

function del()
{
if(document.getElementById("photo").value.length<3)
{
but2.value="ВЫ НЕ ВЫБРАЛИ ИЗОБРАЖЕНИЕ !";
return false;
}
else
{
if(window.confirm("Вы действительно хотите
удалить это фото?")==true)
{
let dt=new FormData(document.forms.dat2);
let re=new XMLHttpRequest();
re.open("POST", "delsett.php", true);
re.send(dt);
re.addEventListener("load", see);
}
65
}
}

Обратите внимание: перед удалением файла мы выводим диалоговую


форму с вопросом:

if(window.confirm("Вы действительно хотите


удалить это фото?")==true)

Здесь администратор может либо подтвердить действие, либо отменить


его. Так мы страхуемся от случайных ошибок.
Полученный ответ интерпретирует функция see:

function see()
{
let ans=this.responseText;
if(ans==1)
{
but2.value="ИЗОБРАЖЕНИЕ УДАЛЕНО !";
let el=document.getElementById("photo").value;
document.getElementById(el).remove();
}
if(ans==2)
but2.value="НЕКОРРЕКТНЫЕ ДЕЙСТВИЯ !";
setTimeout(()=>{but2.value="Удалить";}, 2000);
}

Если удаление завершилось успешно, подтверждаем данный факт:


but2.value="ИЗОБРАЖЕНИЕ УДАЛЕНО !";

формируем имя удаленного файла:


let el=document.getElementById("photo").value;

и «вычеркиваем» его из списка на вкладке:


document.getElementById(el).remove();

8.5. Запись e-mail администратора


Этот процесс доверен функции email скрипта sett.js.
Как и в предыдущих случаях, регистрируем обработчик нажатия кнопки
«Записать»:
document.getElementById("but3").
addEventListener("click", email);

и вводим переменную для обращения к данной кнопке:

let but3=document.getElementById("but3");

66
Проверяем длину электронного адреса:
if(np.value.length<8 || np.value.length>50)
{
but3.value="НЕКОРРЕКТНАЯ ДЛИНА E-MAIL !";
return false;
}

Если все в порядке, то выясняем, правильно ли введен адрес. Для этого


создаем регулярное выражение:
let rv=/^[a-z][a-z\d.-]+[a-z\d]@[a-z\d][a-z\d-]+
[a-z\d]\.[a-z]{2,13}$/i;

Его мы создали по правилам, описанным ниже.


Вообще, корректный e-mail может содержать самые разнообразные сим-
волы. Но мы будем ориентироваться на требования, которые предъявляют к ад-
ресам российские почтовые сервисы. А они заметно снижают перечень таких
символов. Например, в Яндекс.Почте установлены следующие требования:
— разрешены буквы латинского алфавита, цифры, точки, дефисы;
— адрес не должен начинаться с цифры, дефиса или точки;
— адрес не может заканчиваться точкой или дефисом.
Есть правила и для доменных имен второго уровня:
— они могут содержать латинские буквы, цифры и дефис;
— имя может начинаться с цифры или буквы;
— имя не может завершаться дефисом.
Начнем разбор по пунктам.
Адрес не должен начинаться с цифры, дефиса или точки. Этому требова-
нию удовлетворяет вот такой фрагмент выражения:
[a-z]

То есть первый символ — это только буква. Затем идет некоторое коли-
чество произвольных символов из тех, что разрешены, — буквы, цифры, точка,
дефис:
[a-z\d.-]+

Знак + означает, что символов может быть более одного. Наконец, завер-
шающий символ — буква или цифра:

[a-z\d]

Дальше используем почтовый знак @.


Теперь проверка доменного имени второго уровня. Оно начинается с бук-
вы или цифры:
[a-z\d]

Следом могут идти буквы, цифры и дефис:

67
[a-z\d-]+

Завершающий символ — буква или цифра:

[a-z\d]

Имя домена второго уровня отделяется точкой от имени домена первого


уровня:

\.

Самое короткое доменное имя первого уровня состоит из двух символов


(например, ru). Самое длинное из обнаруженных автором — из 13
(international). Поэтому адрес почты должен завершаться после точки мини-
мум двумя, а максимум тринадцатью буквами:

[a-z]{2,13}

Напомню, что регулярное выражение заключается в апострофы /. Для


обозначения начала и конца ввода используются специальные знаки ^ и $.
В конечном итоге у нас получилось регулярное выражение, показанное
выше:

/^[a-z][a-z\d.-]+[a-z\d]@[a-z\d][a-z\d-]+
[a-z\d]\.[a-z]{2,13}$/i;

Если e-mail корректный, отправляем данные на сервер:


let dt=new FormData(document.forms.dat3);
let re=new XMLHttpRequest();
re.open("POST", "newmail.php", true);
re.send(dt);
re.addEventListener("load", res);

Вариантов ответа сервера может быть два — положительный и отрица-


тельный:
function res()
{
let ans=this.responseText;
if(ans==1)
{
but3.value="E-MAIL ЗАПИСАН !";
}
if(ans==2)
but3.value="НЕКОРРЕКТНЫЕ ДАННЫЕ !";

setTimeout(()=>{but3.value="Записать";}, 2000);
}

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

68
9. САЙТ
В этой главе мы отступим от последовательного описания устройства
CMS. Автор посчитал, что до изучения следующего модуля — конструктора —
было бы неплохо сначала выяснить на конкретных примерах, какие сайты поз-
воляет разрабатывать наша система. Этим мы сейчас и займемся.
Думаю, не имеет смысла долго объяснять, что задача проекта — не впе-
чатлить читателей навороченным дизайном и сложным контентом, а рассказать,
как можно создавать, менять и редактировать этот контент. Поэтому автор ре-
шил в основу проекта положить создание максимально простого сайта с макси-
мально простой структурой и максимально простым дизайном. Каждая страни-
ца ресурса, опять же для простоты, имеет фиксированную ширину (чтобы не
усложнять дело адаптивной версткой) и включает пять компонентов (четыре из
них — с настраиваемой шириной):
— заставка в самой верхней части страницы (заставка создается тем или
иным рисунком);
— блок меню с элементами навигации по страницам ресурса;
— контейнер для основного содержания;
— блок рекламы;
— нижний блок (он единственный обладает фиксированной шириной —
100%).
Хотя два последних блока имеют строгое название, размещать в них
можно любую информацию. Нужно только учитывать один важный момент:
содержимое этих блоков одинаково для всех страниц сайта.
В создании и дизайне страниц по умолчанию участвуют сразу 9 файлов:
— index.php — файл шаблона страниц сайта;
— css/index.css — определяет дизайн сайта;
— js/index.js — управляет просмотром фотографий;
— cont.php — отправляет данные формы со страницы «Контакты»;
— js/cont.js — проверяет заполнение формы и передает запрос программе
отправки данных;
— content/cont.txt и content/index.txt — текстовые файлы с данными
страницы «Контакты» и главной страницы;
— set/advert.txt — файл хранения рекламной информации;
— set/footer.txt — файл хранения контента нижнего блока.
9.1. Варианты компоновки страниц
Конструктор позволяет создавать четыре основных варианта дизайна и
еще несколько максимально упрощенных.
Любая страница сайта включает 6 блоков.
1. Заставка в верхней части страницы. В качестве нее предусмотрено ис-
пользовать изображение произвольной высоты и шириной, строго равной ши-
рине границ страницы (не путайте с шириной окна браузера!).
69
2. Все остальные блоки помещаются во flex-контейнере, который, меняя
позиционирование, можно перемещать в вертикальном направлении. В том
числе «заезжая» на заставку. Последний вариант можно увидеть в следующем
разделе на рисунке 9.2.1. Или загрузив на локальный хостинг содержимое пап-
ки htdocs_1 с первым демонстрационным сайтом.
3. Меню. Оно может быть горизонтальным или вертикальным. Вариант
настраивается по вашему желанию путем изменения размеров блоков, входя-
щих во flex-контейнер.
4. Основное содержание — контейнер, предназначенный для размещения
основных текстов, рисунков, таблиц и т. д.
5. Рекламный блок. Название условное, так как в этой области можно по-
мещать не только рекламу, но и вообще любой контент, который одинаков для
всех страниц.
6. Нижний блок. Обычно служит для различной дополнительной инфор-
мации, например копирайта, адреса электронной почты. Но, как и в предыду-
щем блоке, здесь может быть любая информация, одинаковая для всех страниц.
Еще раз подчеркну: основное содержание наполняется отдельно для каж-
дой страницы. Контент нижнего блока и рекламного остается одним и тем же
на всех страницах.
Перейдем к вариантам компоновки. Они показаны ни рисунке 9.1.1.

Рис. 9.1.1
Варианты компоновки блоков страницы
70
Первый вариант — один из наиболее распространенных на просторах Ин-
тернета. Меню — горизонтальное. Под ним в одном ряду основное содержание
и рекламный модуль. Последним, как и положено, стоит нижний блок.
Второй вариант показывает вертикальное меню слева, основное содержа-
ние — посередине, реклама — справа. Нижний блок под ними.
Третья компоновка — построение всех модулей по вертикали.
В последней — четвертой — компоновке меню и основное содержание
находятся в одном ряду. Под ними сначала реклама, а дальше нижний блок.
Какими приемами достигаются подобные компоновки, мы рассмотрим в
следующей главе, когда перейдем к рассмотрению конструктора. Также в сле-
дующей главе автор пояснит, как можно исключить рекламу и нижний блок из
структуры сайта, сделав его дизайн совсем простым.
Обращаю ваше внимание: изменение компоновки достигается без ручной
правки файлов, а только стандартными настройками в конструкторе.

9.2. Файл index.php


Данный файл является сборщиком страниц. Фактически это HTML-
разметка, заключенная в оболочку PHP. Если адрес страницы не указан (напри-
мер, http://localhost/, http://localhost/index.php) или введен неверно, то загру-
жается главная страница. В случае ошибочного адреса никакие предупрежде-
ния об отсутствии данной страницы не выводятся.
В остальных случаях адрес загружаемой страницы передается значением
параметра a в строке запроса, например: http://localhost/index.php?a=index
(index — идентификатор главной страницы), http://localhost/index.php?a=cont
(cont — идентификатор страницы «Контакты»).
Примеры того, как могут выглядеть сайты с горизонтальным и верти-
кальным меню, собранные скриптом index.php, показаны на рисунках 9.2.1 и
9.2.2.
Как уже было сказано выше, установлено правило: если клиент запраши-
вает несуществующую страницу, то в его браузер загружается главная страни-
ца. Поэтому начинается проверка с того, что мы создаем переменную и присва-
иваем ей адрес главной страницы:

$adr="index";

Следом узнаем, передан ли в запросе параметр с адресом страницы:

if(isset($_GET["a"]))
{
...
}

71
Рис. 9.2.1
Один из множества возможных вариантов страницы сайта
Если не передан, то загружаем главную страницу, так как значение пере-
менной $adr не изменилось.
Если параметр передан, присваиваем его значение новой переменной
$test_adr:
if(isset($_GET["a"]))
{
$test_adr=$_GET["a"];
...

72
}

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


очередно выясняем имена всех файлов из папки content:

$opdir=opendir("content");
while($redir=readdir($opdir))
{
...
}
closedir($opdir);

и сравниваем их с адресом, переданным в запросе:


if($test_adr.".txt"==$redir)
{
...
}

Совпадения не обнаружены? Значит, переменная $adr не изменит своего


значения и загрузится главная страница.
Совпадение обнаружено? Переменная $adr получает новое значение —
адрес страницы, которая должна быть загружена по запросу клиента:

$adr=$test_adr;

после чего цикл перебора файлов из папки content прерывается:

break;

Рис. 9.2.2
Еще один вариант компоновки того же сайта

73
В конце процедуры открываем текстовый файл, идентификатор которого
мы только что определили, и построчно считываем этот файл в массив:
$page='content/'.$adr.'.txt';
$soder=file($page);

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


жания, а также тег title и метатеги.
Вот такой код у нас получился в итоге:

<?php
$adr='index';
if(isset($_GET['a']))
{
$test_adr=$_GET['a'];
$opdir=opendir('content');
while($redir=readdir($opdir))
{
if($test_adr.'.txt'==$redir)
{
$adr=$test_adr;
break;
}
}
closedir($opdir);
}

$page='content/'.$adr.'.txt';
$soder=file($page);
?>

Начинаем загрузку страницы в браузер:


<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">

В текстовом файле контента страницы четвертой строкой идет описание.


Подставляем эту строку ($soder[3]) в метатег с именем description:

<meta name="description"
content="<?php echo rtrim($soder[3]); ?>">

Пятый элемент массива $soder[4] содержит ключевые слова. Добавляем их:


<meta name="keywords"
content="<?php echo rtrim($soder[4]); ?>">

Теперь заполняем название страницы, которое показывается во вкладке


окна браузера:
<title><?php echo rtrim($soder[2]); ?></title>

74
Завершаем процесс загрузкой таблицы стилей и сценария для просмотра
снимков:

<link rel="stylesheet" href="css/index.css">


<script src="js/index.js"></script>

Если клиент просматривает страницу «Контакты», то дополнительно за-


гружаем сценарий отправки данных администратору:

<?php
if($adr=='cont')
echo '<script src="js/cont.js"></script>';
?>

На этом подводим черту в заголовочной части документа:


</head>

В теле страницы у нас несколько важных фрагментов. Например, слои


для показа увеличенных фотографий:
<div id="bas"></div>
<div id="view">
<img id="pict" src="pict/net.jpg" alt="Фото">
</div>

Их назначение мы разберем в разделе 9.4.


Контейнером всего остального содержимого «работает» слой basis:

<div id="basis">
...
</div>

Первый его элемент — заставка:

<div id="header"></div>

Второй — flex-контейнер:

<div id="divfl">
...
</div>

Flex-контейнер, как вы помните, вмещает несколько блоков, первый из


которых — меню.

<nav id="menu">
...
</nav>

75
Его формирование — довольно непростой процесс. Сначала мы объявля-
ем пустой массив, в который предстоит занести данные кнопок меню:
$srtar=array();

Затем открываем директорию content

$opdir=opendir('content');
...
closedir($opdir);

и в цикле
while($redir=readdir($opdir))
{
if(strpos($redir, '.'))
{
...
}
}

по очереди выясняем два важных параметра:


— идентификатор каждого текстового файла, который представляет со-
бой имя файла без расширения .txt

$rep=str_replace('.txt', '', $redir);

— содержимое каждого текстового файла

$name=file('content/'.$redir);

Если контент данного файла разрешен к показу на сайте, формируем со-


ответствующую кнопку меню:

if(rtrim($name[1])=='ДА')
$srtar[$name[0]]=
'<a href="index.php?a='.$rep.'"
class="tdn"><h4 class="lin">'.
$name[2].'</h4></a>';

где в ссылке стоит идентификатор текстового файла:

<a href="index.php?a='.$rep.'" class="tdn">

а в надписи кнопки — название страницы:

<h4 class="lin">'.$name[2].'</h4>

полученное ранее при открытии файла.


В результате на выходе мы имеем заполненный ассоциативный массив
$srtar, в котором ключами служат рейтинги страниц, а значениями — HTML-
76
коды пунктов меню. Сортируем массив по значениям ключей (а значит, по зна-
чениям рейтингов):

$newar=ksort($srtar);

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

foreach ($srtar as $key=>$value)


echo $srtar[$key];

Осталось вывести на страницу три модуля.


Начинаем с контента страницы из текстового файла (его мы уже открыли
в самом начале программы):

<div id="content">
<?php
echo $soder[5];
?>
</div>

Затем открываем файл рекламы и печатаем его содержимое:

<div id="advert">
<?php
$page='set/advert.txt';
$soder=file_get_contents($page);
echo $soder;
?>
</div>

Последний — файл нижнего блока:

<div id="footer">
<?php
$page='set/footer.txt';
$soder=file_get_contents($page);
echo $soder;
?>
</div>

На этом формирование документа завершено.

9.3. Таблица стилей


Мы до сих пор не обращали внимания на настройки стилей для тех или
иных страниц CMS, но в этом случае отступим от привычного подхода и уде-
лим внимание таблице стилей сайта.
Она находится по адресу css/index.css.
77
Главная особенность данного файла — каждый раз, когда вы в конструк-
торе меняете настройки дизайна, таблица стилей полностью перезаписывается.
Каким образом — мы поговорим в десятой главе.
Сейчас мы разберем все параметры за исключением слоев, предназначен-
ных для показа увеличенных фотографий. Дизайн этих элементов мы обсудим в
следующем разделе.
Особенность приведенных ниже фрагментов в том, что у настраиваемых
параметров вместо значений стоит троеточие.
Тело документа выровнено посередине окна браузера, а вот цвет фона
выбирает разработчик:

body {margin: 0; background: ...;}

Дальше идет основной контейнер, содержащий все элементы страницы.


У него задается ширина:

#basis {position: relative; margin: auto; width: ...;}

Блок с заставкой имеет ширину, равную 100% от базового контейнера.


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

#header {position: absolute; top: 0; left: 0;


z-index: 1; width: 100%; height: ...;
background: url(...);}

Наличие свойства

z-index: 1

станет понятно из дальнейших объяснений.


Flex-контейнер «спроектирован» таким образом, чтобы его внутренние
элементы располагались слева направо в линию, но при переполнении контей-
нера смещались вниз на следующую строку:

#divfl {position: absolute; left: 0; display: flex;


flex-wrap: wrap; z-index: 2; width: 100%; top: ...;}

Здесь вы можете настроить только вертикальное смещение от верхней


границы документа. При этом установлено следующее значение для перпенди-
кулярного положения контейнера:

z-index: 2

Благодаря тому, что flex-контейнер перемещается в вертикальном


направлении, а его z-index выше, чем у заставки, внутренние элементы контей-

78
нера могут «наползать» на заставку. Подобный случай мы видели в предыду-
щем разделе на рисунке 9.2.1.
Стили меню тоже заданы таким образом, чтобы его внутренние элементы
располагались слева направо в линию, но при переполнении контейнера сме-
щались вниз на следующую строку:
#menu {display: flex; flex-wrap: wrap;
justify-content: center; align-content: flex-start;
width: ...; margin-bottom: ...; margin-right: ...;
font-family: ...; font-size: ...;}

Если вы зададите ширину данного блока, равную ширине базового слоя


документа, то меню будет горизонтальным. Если задать ширину, равную или
чуть больше ширины кнопки меню, то последнее станет вертикальным.
Вы можете задать для горизонтального меню интервал между ссылками
справа:
margin-right: ...;

а для вертикального — интервал между ними снизу:


margin-bottom: ...;

Текст кнопки одновременно является ссылкой, которая лишена характер-


ного подчеркивания:
.tdn {text-decoration: none;}

Настройки кнопок выглядят следующим образом:

.lin {display: flex; align-items: center;


justify-content: ...; margin: ...; width: ...;
height: ...; background: ...; color: ...;
...

Здесь вы определяете размеры кнопок, выравнивание их надписей, рас-


стояние между кнопками, цвет фона и букв.
Строка свойств кнопок заканчивается многоточием, потому что в стилях
можно задать кнопкам тень (а можно и не задавать). Тогда в этой строке по-
явится вот такое свойство:
box-shadow: 5px 5px 10px #000000;

Перечень свойств завершается скруглением:


border-radius: ...;}

Если установить значение 0, то кнопки будут с четкими углами.


Дальше идут стили блока основного содержания:
#content {min-height: 100px; min-height: 50px;
width: ...; margin-bottom: ...; background: ...;}
#content img {cursor: pointer;}
79
Вы можете задать размер, отступ снизу от границ остальных элементов,
цвет фона. Кроме того, на всех изображениях, которые вы разместите в основ-
ном содержании, при наведении мыши указатель будет «превращаться» в руку.
Данное свойство показывает, что картинку можно увеличить. В остальных бло-
ках — рекламы и нижнем — режим увеличения не действует.
В стилях рекламы задается ширина блока, отступ слева и снизу (послед-
нее свойство важно, если рекламный блок располагается под основным содер-
жанием), цвет фона:
#advert {min-height: 100px; min-height: 50px;
width: ...; margin-left: ...; margin-bottom: ...;
background: ...;}
Стили нижней части страницы:

#footer {width: 100%; height: ...; background: ...;}

9.4. Просмотр фото


Если на странице сайта в области основного контента есть какое-либо
изображение, посетитель может увеличить его, щелкнув мышью по картинке.
При этом страница будет закрыта полупрозрачным слоем установленного в
конструкторе цвета с увеличенным изображением (рис. 9.4.1). Если теперь
кликнуть на нем или прокрутить страницу, увеличенная картинка и полупро-
зрачный слой исчезнут. Управляет данным процессом сценарий, написанный на
JavaScript. Его мы разберем позже, а пока выясним, что необходимо добавить в
HTML-код страницы для просмотра увеличенных фотографий.
Как вы помните, автор поместил в разметке файла index.php вот такой
фрагмент:
<div id="bas"></div>
<div id="view">
<img id="pict" src="pict/net.jpg" alt="Фото">
</div>

Стили этого блока кода:


#bas, #view {position: absolute; left: 0; top: 0;
width: 100%; height: 100%; visibility: hidden;}
#bas {z-index: 3; opacity: 0.95; background: ...;}
#view {z-index: 4; display: flex;
justify-content: center; align-items: center;}

Как видите, здесь 2 слоя. Нижний


<div id="bas"></div>

имеет цветной фон с уровнем непрозрачности 0.95. Верхний


<div id="view">
...
</div>

80
служит контейнером для увеличенного рисунка:

<img id="pict" src="pict/net.jpg" alt="Фото">

В исходном состоянии на данный слой загружено изображение net.jpg


размером 1×1 пиксель. Сделано это, чтобы угодить требованиям стандартов
Консорциума Всемирной паутины, которые не допускают в разметке изображе-
ний с пустым атрибутом src (ведь в исходном состоянии никакое фото со стра-
ницы в контейнер div id=«view» не загружено).
При загрузке увеличенного рисунка цветной фон занимает все простран-
ство окна браузера, а сама картинка располагается строго посередине (рис. 9.4.1).

Рис. 9.4.1
Просмотр одной из фотографий страницы
Для вставки данного фрагмента кода автор выбрал место сразу после от-
крывающего тега body:
...
<body>

<div id="bas"></div>
<div id="view">
<img id="pict" src="img/net.jpg" alt="Фото">
</div>

<div id="basis">
...

81
Сценарий index.js из папки js подключаем в заголовочной части доку-
мента (файла index.php) следующим образом:
<script src="js/index.js"></script>

Что мы найдем в файле сценария? Две функции. Первая необходима для


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

addEventListener("load", function()
{
...
});
Для сокращения кода, в котором несколько раз происходят обращения к
одним и тем же компонентам, создадим три переменные:

let view=document.getElementById("view");
let bas=document.getElementById("bas").style;
let pict=document.getElementById("pict");

Теперь зарегистрируем обработчик для клика в области основного содер-


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

document.getElementById("content").
addEventListener("click", function(ev)
{
...
});

Обратите внимание: функция просмотра фото увеличивает снимки только


из области основного содержания. Эта область представляет собой контейнер div
с id="content". После щелчка, произошедшего на слое content, надо выяснить,
был ли этот щелчок на изображении или нет:
if(ev.target.tagName=="IMG")
{
...
}

Если был, то нужно продемонстрировать увеличенную картинку, распо-


ложив ее по центру страницы, насколько бы она ни была прокручена по верти-
кали. Для этого первым делом определяем величину прокрутки:

let sc=window.pageYOffset;

а затем «передвигаем» слой фона и слой с изображением на эту величину:


82
bas.top=sc+"px";
view.style.top=sc+"px";

Вычисляем адрес «кликнутого» фото и присваиваем этот адрес атрибуту


src картинки со слоя:
pict.src=ev.target.src;

Завершающий этап — включение режима просмотра:


view.style.visibility="visible";
bas.visibility="visible";

Чтобы выйти из режима просмотра, зарегистрируем один обработчик для


двух разных событий:
view.addEventListener("click", del);
addEventListener("scroll", del);

Первый запускает функцию del, когда мы щелкаем по увеличенному


изображению или фону, а второй — при прокрутке страницы.
В самой функции всего три инструкции:
function del()
{
view.style.visibility="hidden";
bas.visibility="hidden";
pict.src="pict/net.jpg";
}

Первая и вторая скрывают фон и картинку, а третья присваивает рисунку


со слоя адрес исходного изображения. Этого момента коснемся подробнее.
В принципе, рисунку со слоя можно было бы и не присваивать исходный
адрес точечного изображения. Но (!) в таком случае при низкой скорости со-
единения с сервером можно столкнуться с неприятным эффектом: допустим, вы
посмотрели одно фото, завершили просмотр и щелкнули на второй фотогра-
фии. Вновь запустится режим просмотра, но из-за медленной загрузки второго
снимка вы некоторое время будете видеть первый. А это, согласитесь, не очень
хорошо. Присваивая src значение pict/net.jpg, мы избавляемся от подобных не-
приятностей, ведь маленькая белая точка загружается очень быстро и совер-
шенно не будет видна в момент медленной загрузки второго снимка. То есть
мы просто будем видеть цветной полупрозрачный фон до тех пор, пока не по-
явится следующее изображение.

9.5. Страница «Контакты»


Эта страница отличается только тем, что в нее загружается не текст с фо-
тографиями, а форма для отправки заявок. Отправкой сообщений администра-
тору по электронной почте управляют файлы cont.js и cont.php.

83
Наполнение страницы «Контакты» представлено на рисунке 9.5.1. Форма
имеет три поля — «Ваше имя», «Телефон или e-mail» и «Сообщение», а также
кнопку «Отправить». Если какое-то поле оставить незаполненным, то появляет-
ся соответствующее предупреждение. Причем выводится оно, как и в системе
управления, прямо на кнопку «Отправить», заменяя ее изначальный текст
(рис. 9.5.2).

Рис. 9.5.1
Страница «Контакты»
Отправка заявки выполняется с помощью технологии Ajax. То есть в
браузере остается та же страница. В это время сценарий из файла cont.js снача-
ла проверяет данные, а потом отправляет запрос программе cont.php. Там дан-
ные проверяются еще раз, после чего происходит отправка информации адми-
нистратору по e-mail. На последнем этапе программа cont.php возвращает фай-
лу cont.js отчет об ошибке, если была попытка отправить запрещенные данные
(рис. 9.5.3). Список запрещенных символов введен, чтобы защитить программу
от недоброжелателей. Если данные успешно прошли все проверки, то на по-
следнем шаге сценарий из cont.js выводит на страницу подтверждение о полу-
чении заявки (и опять на кнопку отправки данных — рис. 9.5.4).

Рис. 9.5.2
Предупреждение, если заполнены не все поля
84
Рис. 9.5.3
Предупреждение, если введены запрещенные символы. В данном случае
в поле «Сообщение» есть запрещенный знак <

Рис. 9.5.4
Подтверждение успешной отправки сообщения
Форма отправки сообщения находится в файле content/cont.txt. Выглядит
она очень просто:
<form method="POST" name="dat">
<table ...>
<tr>
<td ...>Ваше имя:</td>
<td><input type="text" id="nam" name="nam"
minlength="2" maxlength="50" ...></td>
</tr>
<tr>
<td ...>Телефон или e-mail:</td>
<td><input type="text" id="con" name="con"
minlength="8" maxlength="50" ...></td>
</tr>
<tr>
<td ...>Сообщение:</td>
<td><textarea id="tex" name="tex"

85
minlength="10" maxlength="1000" ...></textarea></td>
</tr>
<tr>
<td>&nbsp;</td>
<td><p><input type="button" value="Отправить"
id="but" ...></p></td>
</tr>
</table>
</form>

Как видите, все поля и текст размещаются в таблице, которая отвечает за


компоновку элементов формы.
Самое короткое мужское имя — Ян, женское — Ия. Поэтому минималь-
ное количество знаков в поле «Ваше имя» 2, максимальное — 50. Вряд ли есть
имена длиннее.
Для поля «Телефон или e-mail» минимальное количество символов 8. Это
самый короткий адрес электронной почты, укладывающийся в стандарты рос-
сийских потовых сервисов. Верхняя планка — 50 символов.
Сообщение должно быть не короче 10 знаков и не длиннее 1000. Подоб-
ное же ограничение установлено в файле cont.php, чтобы злоумышленник не
мог перегрузить ваш сервер, отправив огромный объем текста.

9.6. Отправка сообщений


Перейдем к разбору программ, выполняющих отправку сообщений.
Начнем с файла cont.js.
Как мы уже выяснили, этот сценарий подключается в головной части до-
кумента (файла index.php) исключительно при загрузке страницы «Контакты».
Схема процесса выглядит следующим образом. После загрузки страницы
регистрируется обработчик для нажатия кнопки «Отправить» — функция cont:
addEventListener("load", function()
{
document.getElementById("but").
addEventListener("click", cont);
...
});

Объявляем ссылку на кнопку:


let but=document.getElementById("but");

Представим, что мы не заполнили форму и пытаемся отправить «ничего» на


сервер. На этот случай функция cont выполняет три проверки. Первая — заполнено
ли поле «Ваше имя»:
if(document.getElementById("nam").value.length<2)
{
but.value="ВЫ НЕ ВВЕЛИ ИМЯ !";
return false;
}

86
Обнаружив, что данное поле пустует, выводим предупреждение «ВЫ НЕ
ВВЕЛИ ИМЯ !» и прерываем отправку данных. Напомню, что здесь мы вновь
использовали креативное решение — все предупреждения выводятся непосред-
ственно на кнопку but вместо ее первоначальной надписи «Отправить»:
but.value="ВЫ НЕ ВВЕЛИ ИМЯ !";

Итак, введем какое-то имя и вновь попробуем отправить данные из фор-


мы. Однако у нас опять ничего не получится, так как функция теперь проверит
заполнение поля «Телефон или e-mail»:
else
{
if(document.getElementById("con").value.length<8)
{
but.value="ВЫ НЕ ВВЕЛИ ТЕЛЕФОН ИЛИ E-MAIL !";
return false;
}
...
}
Самый короткий номер телефона содержит минимум 10 символов:
9990001155. Самый короткий адрес электронной почты — 8. Поэтому выбира-
ем количество символов для успешной проверки не менее 8.
Заполним второе поле. Но и сейчас отправить форму не получится, так
как третье поле осталось пустым, что обнаружит следующий фрагмент кода:
else
{
if(document.getElementById("tex").value.length<10)
{
but.value="ВЫ НЕ ВВЕЛИ СООБЩЕНИЕ !";
return false;
}
...
}

Введем текст сообщения — минимум 10 символов, — и только теперь у


нас появится возможность отправить данные.
Отправлять заявки мы будем, используя технологию Ajax. Для этого со-
здадим новый объект с данными из формы:
let dt=new FormData(document.forms.dat);

и новый объект интерфейса XMLHttpRequest:


let re=new XMLHttpRequest();

Методом open формируем запрос:


re.open("POST", "cont.php", true);

и отправляем его серверной программе cont.php:


re.send(dt);

87
Также устанавливаем, что после загрузки ответа должна запускаться
функция show:
re.addEventListener("load", show);

Ее задача — на основе полученных чисел вывести то или иное сообщение на


кнопке отправки данных.
Напомню, что при желании особо рьяные недоброжелатели могут обойти
проверку на стороне клиента. Поэтому основной заслон необходимо ставить на
стороне сервера, в программе cont.php, которая выполняет отправку информа-
ции по электронной почте.
Первым делом серверный скрипт выясняет, введены ли данные:
if(isset($_POST['nam']) && ($_POST['con']) &&
($_POST['tex']))
{
...
}
else
echo '2';

Если данные в наличии, получаем их:


$t1=$_POST['nam'];
$t2=$_POST['con'];
$t3=$_POST['tex'];

Проверяем минимальное количество символов:

if((strlen($t1)<2) || (strlen($t2)<8) ||
(strlen($t3)<10))
echo '2';
else
{
...
}

Затем максимальное:
if((strlen($t1)>50) || (strlen($t2)>50) || (strlen($t3)>1000))
echo '3';
else
{
...
}

Четвертая проверка — на наличие запрещенных символов (например,


цифр в имени) и на попытку передать в сообщении исполняемый код или ссыл-
ку:
if((preg_match('/[^a-zA-Zа-яА-ЯёЁ ]/u', $t1)) ||
(preg_match('/[<>]/', $t3)) ||
(strpos($t3, 'function')) ||
(strpos($t3, 'script')) ||
(strpos($t3, 'onload')) ||

88
(strpos($t3, 'return')) ||
(strpos($t3, 'href')))
echo '3';
else
{
...
}

С сообщением все в порядке? Тогда отправляем его администратору по e-


mail, взятому из файла set/email.txt:
$ema=file('set/email.txt');
if(mail($ema[0], "Информация с сайта",
"$t1\r\n $t2\r\n $t3\r\n", "From: $ema[0]"))
echo '1';
else
echo '4';

echo '1';

Обращаем ваше внимание, что блок отправки данных в файле cont.php


закомментирован. Сделано это для того, чтобы на локальном хостинге вы мог-
ли имитировать отправку письма. По-настоящему отправить почту с локально-
го хостинга не удастся. Перенося CMS на удаленный хостинг, не забудьте
убрать символы комментария. А также подробно выясните все особенности от-
правки писем из скриптов у вашего провайдера. С этим могут быть разные
сложности.
Итак, сообщение отправлено, а управление возвращается сценарию
cont.js. Получив ответ, мы запускаем функцию show, которая переводит цифры
в тот или иной текст на кнопке «Отправить»:

function show()
{
let re=document.getElementById("but");
let ans=this.responseText;
if(ans==1)
re.value="СООБЩЕНИЕ ОТПРАВЛЕНО !";
if(ans==2)
re.value="ЗАПОЛНЕНЫ НЕ ВСЕ ПОЛЯ !";
if(ans==3)
re.value="НЕКОРРЕКТНЫЕ ДАННЫЕ !";

setTimeout(()=>{but.value="Отправить";}, 3000);
}

Последняя инструкция указывает программе, что через 3 секунды нужно


вернуть кнопке исходную надпись:

setTimeout(()=>{but.value="Отправить";}, 3000);

89
10. КОНСТРУКТОР САЙТА
Сейчас мы обсудим один из самых важных модулей, который отвечает за
дизайн сайта. Он называется конструктором. В работе этого модуля участвуют
следующие файлы:
1) design.php — формирует страницу конструктора;
2) css/design.css — определяет вид страницы;
3) js/design.js — управляет переключением вкладок с параметрами и со-
здает запрос к программе записи данных;
4) newdesign.php — записывает настройки дизайна в соответствующие
файлы;
5) css/index.css — сюда записываются стили, установленные для сайта в кон-
структоре;
6) set/design.txt — хранит параметры дизайна (назначение объясним позже).

10.1. Страница конструктора


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

Рис. 10.1.1
Страница конструктора

90
Руководствуясь этими подсказками, вы упростите себе выбор настроек в
соответствии со своими пожеланиями.
Второй элемент — кнопка записи, нажатие которой фиксирует выбранные
настройки.
Третий элемент — вкладки, в которых необходимо ввести нужные значения.
Сообщения, подтверждающие успешную запись информации, выводятся
на кнопке «Записать».

10.2. Блоки
Код страницы, образованной программой design.php, состоит из двух
блоков: иллюстраций вариантов дизайна и вкладок с настройками.
HTML-код иллюстраций:
<img src="draw/struct.jpg" id="struct"
alt="..." title="...">
<img src="draw/struct_2.jpg" id="struct2"
alt="..." title="...">

Клики на рисунках обрабатывает сценарий (из файла design.js), который


мы сейчас и разберем.
Регистрируем обработчики для кликов по картинкам:
document.getElementById("struct").
addEventListener("click", function()
{
...
});
document.getElementById("struct2").
addEventListener("click", function()
{
...
});

После загрузки страницы видна схема с id="struct". Клик по ней скрыва-


ет исходную картинку и демонстрирует ту, что пока была не видна:

document.getElementById("struct").style.visibility=
"hidden";
document.getElementById("struct2").style.visibility=
"visible";

Если теперь щелкнуть на втором изображении, то оно скроется и появит-


ся первая схема:

document.getElementById("struct").style.visibility=
"visible";
document.getElementById("struct2").style.visibility=
"hidden";

91
Данные дизайна хранятся в двух файлах. Первый — css/index.css. Его мы
уже комментировали. В этот фал записываются свойства элементов любой
страницы. Второй файл — set/design.txt — нужен для записи настроек в виде,
удобном для чтения модулем «Конструктор сайта». Чтобы не вычленять необ-
ходимые параметры из сложной таблицы стилей index.css, лучше хранить эти
же самые значения в дополнительном файле design.txt и загружать эти данные
при открытии страницы «Конструктор сайта».
CMS поставляется с настройками по умолчанию, которые и загружаются
в модуль после его первого запуска.
Вернемся к файлу design.php. Считываем текстовый файл design.txt:
$val=file('set/design.txt');
for($i=0; $i<count($val); $i++)
$val[$i]=rtrim($val[$i]);

и загружаем параметры в форму с тремя вкладками:

<form method="POST" name="dat">


<input type="button" id="but" value="Записать"
title="...">
<input type="hidden" name="pas" value="'.$pas.'">
<div id="main">Основные настройки</div>
<div id="menu">Меню</div>
<div id="subs">Содержание</div>';

...

<div id="maindiv">
...
</div>

<div id="menudiv">
...
</div>

<div id="subsdiv">
...
</div>

</form>

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


стема выбора картинок аналогична тому, что мы видели в других главах:

<div id="list_d">
<img src="draw/close.png" id="list_i" alt="Закрыть"
title="Закрыть">
<p id="p_list">';
$opdir=opendir("pict");
while($redir=readdir($opdir))
{
if(strpos($redir, "."))
echo '<img class="roster" src="pict/'.$redir.'"
92
id="'.$redir.'" alt="Фото">';
}
closedir($opdir);
echo '</p></div>

Сценарий выбора фото (файл design.js) тоже стандартный:

document.getElementById("bf5").
addEventListener("click", function()
{
document.getElementById("list_d").
style.visibility="visible";
});

document.getElementById("list_i").
addEventListener("click", function()
{
document.getElementById("list_d").
style.visibility="hidden";
});

document.getElementById("p_list").
addEventListener("click", function(ev)
{
if(ev.target.tagName=="IMG")
{
let pho=ev.target.src.split('pict/');
document.getElementById("f5").value=pho[1];
document.getElementById("list_d").
style.visibility="hidden";
}
});

Здесь bf5 — id кнопки «Выбрать», которая открывает вкладку.


Заменив исходные настройки на выбранные вами, нажмите кнопку «За-
писать». Сайт получит новый дизайн.
Чтобы освоиться с пока незнакомой системой, вы можете поэксперимен-
тировать с одним из вариантов демонстрационного сайта. Меняйте уже настро-
енные параметры и наблюдайте, какие это дает результаты.

10.3. Вкладки
Их три. «Основные настройки» — в этой вкладке шесть полей с главными
параметрами страницы. В «Меню» настраиваем положение и параметры кнопок
меню. «Содержание» — в этой вкладке устанавливаем настройки блока основ-
ного содержания, рекламы и нижнего.
Во вкладке основных настроек в качестве фото заставки выбирается ри-
сунок из имеющихся в папке pict. Код слоя с иллюстрациями, одинаковый для
многих модулей системы управления, мы уже посмотрели в предыдущем раз-
деле.
Ряд параметров — шрифты, выравнивание, наличие тени — выбирается
из выпадающих списков.
93
Установка цвета тех или иных элементов работает очень просто. Нажмите
кнопку открытия окна выбора цвета (на рис. 10.3.1 эта кнопка обозначена верх-
ней стрелкой). Определите требуемый тон, передвигая круглый ползунок по
цветовой шкале (обозначен нижней стрелкой). Перемещая круглый курсор
(обозначен средней стрелкой) по прямоугольнику палитры, выберите необхо-
димый оттенок. Нажмите кнопку «Выбрать» — и код цвета появится в соответ-
ствующем поле. Обратите внимание: в браузере Firefox вкладка выбора цвета
выглядит совершенно иначе, нежели аналогичные вкладки в других браузерах
(рис. 10.3.2).

Рис. 10.3.1
Выбор цвета текста или его фона

Рис. 10.3.2
Выбор цвета в браузере Firefox
Переключением вкладок управляет сценарий из файла design.js.
Регистрируем три обработчика кликов на вкладках:
document.getElementById("main").
addEventListener("click", func);
document.getElementById("menu").
94
addEventListener("click", func);
document.getElementById("subs").
addEventListener("click", func);

Нажатая кнопка на той или иной вкладке запускает функцию func:


function func(ev)
{
let ar=["main", "menu", "subs"];
for(let n=0; n<ar.length; n++)
{
let ardiv=ar[n]+"div";
let arst=document.getElementById(ar[n]).style;
let ardivst=document.
getElementById(ardiv).style;
if(ev.target.id==ar[n])
{
arst.zIndex="30";
ardivst.zIndex="29";
ardivst.visibility="visible";
}
else
{
arst.zIndex="10";
ardivst.zIndex="20";
ardivst.visibility="hidden";
}
}
}

В этом сценарии в цикле создаются переменные со ссылками на свойства


разных слоев:
for(let n=0; n<ar.length; n++)
{
let ardiv=ar[n]+"div";
let arst=document.getElementById(ar[n]).style;
let ardivst=document.getElementById(ardiv).style;
...
}

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


if(ev.target.id==ar[n])
{
arst.zIndex="30";
ardivst.zIndex="29";
ardivst.visibility="visible";
}

а остальные — на задний:
else
{
arst.zIndex="10";
ardivst.zIndex="20";
ardivst.visibility="hidden";
}
Благодаря этой программе происходит переключение вкладок с настройками.

95
10.4. Запись результата
Тут все идет стандартно — по «тропинке», неоднократно «проторенной»
в предыдущих сценариях и серверных скриптах.
После нажатия кнопки «Записать» отправляем запрос на сервер:
document.getElementById("but").
addEventListener("click", function()
{
let dt=new FormData(document.forms.dat);
let re=new XMLHttpRequest();
re.open("POST", "newdesign.php", true);
re.send(dt);
re.addEventListener("load", show);
});

Серверный скрипт проверяет пароль. Затем компонует список данных для


записи в файл design.txt:
$fin=$_POST['f1']."\n".$_POST['f2']."\n".
$_POST['f3']."\n".$_POST['f4']."\n".
$_POST['f5']."\n".$_POST['f6']."\n".
$_POST['f7']."\n".$_POST['f8']."\n".
$_POST['f9']."\n".$_POST['f10']."\n".
$_POST['f11']."\n".$_POST['f12']."\n".
$_POST['f13']."\n".$_POST['f14']."\n".
$_POST['f15']."\n".$_POST['f16']."\n".
$_POST['f17']."\n".$_POST['f18']."\n".
$_POST['f19']."\n".$_POST['f20']."\n".
$_POST['f21']."\n".$_POST['f22']."\n".
$_POST['f23']."\n".$_POST['f24']."\n".
$_POST['f25']."\n".$_POST['f26']."\n".
$_POST['f27']."\n".$_POST['f28'];

Ответ сценарию формируется в два этапа. Сначала вводим переменную-


идентификатор:

$ret=0;

После успешной записи в указанный файл меняем значение идентифика-


тора (первый этап):
if(file_put_contents('set/design.txt', $fin))
$ret=0.5;

Следом производим запись в таблицу стилей сайта и получаем оконча-


тельное значение ответа:

$inp="...";

if(file_put_contents('css/index.css', $inp))
echo $ret+=0.5;
В итоге код ответа — 1 (0.5 + 0.5).

96
Переменная $inp содержит полный список свойств и значений, записыва-
емых в таблицу стилей. Список этот весьма объемный, поэтому мы не стали
приводить его. Посмотрите файл newdesign.php.
На последнем этапе сценарий из файла design.js добавляет соответству-
ющий текст на кнопку «Записать»:

function show()
{
let re=document.getElementById("but");
let ans=this.responseText;
if(ans==1)
re.value="ЗАПИСАНО";
else
re.value="НЕ ЗАПИСАНО";

setTimeout(()=>{document.getElementById("but").
value="Записать";}, 2000);
}

Подтверждение записи вы можете увидеть на рисунке 10.4.1.

Рис. 10.4.1
Подтверждение успешной записи настроек дизайна

97
11. ДОБАВЛЕНИЕ ФОТО
Будем считать, что вы уже завершили настройку дизайна будущего ре-
сурса. Но приступать к созданию и наполнению страниц еще рано. У нас пока
не загружены необходимые иллюстрации. К этому процессу мы сейчас и пе-
рейдем. Правда, рассказ будет достаточно короткий. Дело в том, что модуль за-
качки фотографий очень напоминает соответствующий модуль добавления и
удаления снимков, который мы рассматривали в главе 8. Поэтому обоснованно
сократим повествование. Скажем только, что в функционировании раздела до-
бавления фото принимают участие следующие пять файлов:
— photo.php — формирует раздел добавления и удаления снимков;
— css/photo.css — определяет дизайн страницы;
— js/photo.js — формирует запросы на закачку и удаление изображений,
а также отвечает за их просмотр;
— newphoto.php — программа загрузки картинок, используемых на страни-
цах сайта;
— delphoto.php — программа удаления лишних картинок.

11.1. Страница добавления фото


Данная страница показана на рисунке 11.1.1. Очень похоже на то, что мы
видели в модуле «Специальные настройки». Только теперь у нас не 3, а 2 поля:
для загрузки рисунков и для их просмотра/удаления.
В разметке страницы три основные части:
— форма загрузки картинок;
— форма для их удаления;
— скрытая вкладка с изображениями, хранящимися в папке img.

Рис. 11.1.1
Страница добавления и удаления фото
HTML-код первой формы:
<form method="POST" id="newp" name="dat1"
enctype="multipart/form-data">
Добавить новое фото для страниц сайта:

98
<input type="file" id="newfoto" name="newfoto">
<input type="hidden" name="pas"
value="'; echo $pas; echo '">
<input type="button" id="but1" value="Добавить">
</form>

Эта разметка один в один повторяет то, что мы видели в разделе специ-
альных настроек. Даже имена полей совпадают.
Напомним: в имени загружаемого фото должны быть только латин-
ские буквы и цифры без пробелов!
Код второй формы:
<form method="POST" id="delp" name="dat2">
Удалить фото: <input id="photo" name="photo">
<input type="hidden" name="pas"
value="'; echo $pas; echo '">
<input type="button" id="but"
value="Посмотреть / Выбрать">
<input type="button" id="but2" value="Удалить">
</form>

Здесь нам тоже все знакомо.


И, наконец, вкладка с фотографиями:

<div id="list_d">
<img src="draw/close.png" id="list_i" alt="Закрыть"
title="Закрыть">
<p id="p_list">';

$opdir=opendir("img");
while($redir=readdir($opdir))
{
if(strpos($redir, "."))
echo '<img class="roster" src="img/'.$redir.'"
id="'.$redir.'" alt="Фото">';
}
closedir($opdir);

echo '</p></div>

Единственное отличие этого фрагмента — открывается директория img, ко-


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

11.2. Добавление фото


Коротко рассмотрим, как на программном уровне выполняется закачка
иллюстраций. В файле сценариев photo.js регистрируем обработчик нажатия
кнопки «Добавить» и вводим переменную для сокращенного обращения к
свойствам этой кнопки:

document.getElementById("but1").
addEventListener("click", cont);
...
let but1=document.getElementById("but1");
99
В теле функции cont проверяем, загружен ли рисунок:

if(document.getElementById("newfoto").value=="")
{
but1.value="ВЫ НЕ ЗАГРУЗИЛИ ИЗОБРАЖЕНИЕ !";
return false;
}
else
{
...
}

В случае положительного исхода проверки отправляем серверному


скрипту newphoto.php данные из формы:

let dt=new FormData(document.forms.dat1);


let re=new XMLHttpRequest();
re.open("POST", "newphoto.php", true);
re.send(dt);
re.addEventListener("load", show);

Получив ответ, запускаем функцию show. Проверяем содержимое ответа:

let ans=this.responseText;
let arans=ans.split(";");
if(arans[0]==1)
{
but1.value="ИЗОБРАЖЕНИЕ ЗАГРУЖЕНО !";
...
}
if(arans[0]==2)
but1.value="НЕКОРРЕКТНЫЕ ДАННЫЕ !";

Добавление нового фото на вкладку происходит уже знакомым нам мето-


дом:

let point='<img class="roster" src="img/'+arans[1]+


'" id="'+arans[1]+'" alt="Фото">';
document.getElementById("p_list").
insertAdjacentHTML("beforeend", point);

Серверная программа newphoto.php тоже работает по знакомому нам


принципу:
— проверяет пароль;
— проверяет поступление изображения:

if(($_FILES['newfoto']['error']==0) &&
(strpos($_FILES['newfoto']['type'], 'image')===0))
{
...
}
else
echo '2';

100
— перезаписывает файл в место постоянного хранения:

move_uploaded_file($_FILES['newfoto']['tmp_name'],
'img/'.basename($_FILES['newfoto']['name']));

— отправляет ответ с необходимыми данными:

echo '1;'.basename($_FILES['newfoto']['name']);

11.3. Просмотр фото


Этим процессом управляет сценарий из файла photo.js. Сценарий нам
уже знаком:
— регистрируем обработчик нажатия кнопки «Посмотреть/Выбрать»:
document.getElementById("but").
addEventListener("click", function()
{
document.getElementById("list_d").
style.visibility="visible";
});

— регистрируем обработчик нажатия крестика на вкладке


document.getElementById("list_i").
addEventListener("click", function()
{
document.getElementById("list_d").
style.visibility="hidden";
});

— пишем анонимную функцию, которая обрабатывает событие выбора


снимка, помещая имя файла в соответствующее поле и закрывая вкладку:
document.getElementById("p_list").
addEventListener("click", function(ev)
{
if(ev.target.tagName=="IMG")
{
let pho=ev.target.src.split('img/');
document.getElementById("photo").value=pho[1];
document.getElementById("list_d").
style.visibility="hidden";
}
});

11.4. Удаление фото


Сценарий удаления картинки из папки img:
— регистрируем обработчик нажатия кнопки «Удалить» и вводим пере-
менную для сокращенного обращения к свойствам этой кнопки:
document.getElementById("but2").
addEventListener("click", del);

101
...
let but2=document.getElementById("but2");

— проверяем наличие имени удаляемого рисунка в соответствующем поле:


if(document.getElementById("photo").value.length<3)
{
but2.value="ВЫ НЕ ВЫБРАЛИ ИЗОБРАЖЕНИЕ !";
return false;
}
else
{
...
}
— выясняем, подтверждает ли администратор удаление:

if(window.confirm("Вы действительно хотите


удалить это фото?")==true)

— отправляем запрос на сервер:

let dt=new FormData(document.forms.dat2);


let re=new XMLHttpRequest();
re.open("POST", "delphoto.php", true);
re.send(dt);
re.addEventListener("load", see);

Теперь в действие вступает скрипт delphoto.php. Проверяем пароль, а за-


тем поступившие данные:

if(isset($_POST['photo']) && strlen($_POST['photo'])>3)


{
...
}
else
echo '2';

Если все в порядке, удаляем снимок и отправляем подтверждение


JavaScript-сценарию:

unlink('img/'.$_POST['photo']);
echo '1';

Сценарий проверяет ответ сервера и в случае успешного завершения опе-


рации выводит на кнопку необходимое сообщение и удаляет картинку со
вкладки:
but2.value="ИЗОБРАЖЕНИЕ УДАЛЕНО !";
let el=document.getElementById("photo").value;
document.getElementById(el).remove();

102
12. ДОБАВЛЕНИЕ ФАЙЛОВ
Этот блок системы тоже имеет много общего с разделом специальных
настроек и, соответственно, с функционалом добавления фото. Поэтому про-
цессу загрузки файлов уделим минимальное внимание.
В работе блока участвуют следующие компоненты:
— file.php — формирует раздел добавления и удаления файлов;
— css/file.css — определяет дизайн страницы;
— js/file.js — формирует запросы на закачку и удаление файлов;
— newfile.php — программа загрузки новых файлов;
— delfile.php — программа удаления лишних файлов.
Закачанные файлы добавляются в папку file.

12.1. Страница добавления файлов


Часто администратору необходимо на страницах сайта продемонстриро-
вать не только изображения, но и файлы с какой-либо особенной информацией.
Например, образец бланка для заполнения или таблицу с большим количеством
данных. Поэтому в системе управления предусмотрена возможность загружать
файлы в форматах doc, rtf, xl, pdf, txt и др. А в редакторе страниц, разбирать
который мы будем несколько позже, предусмотрен механизм добавления ссы-
лок на эти файлы.
Внешний вид страницы данного блока показан на рисунке 12.1.1. Как ви-
дите, у нас есть всего два поля — загрузки файлов и удаления. Механизм про-
смотра файлов не предусмотрен. Обратите внимание: в имени загружаемого
файла должны быть только латинские буквы и цифры без пробелов!

Рис. 12.1.1
Страница загрузки и удаления файлов
На странице две формы: добавления файлов и удаления.
Первая форма нам уже хорошо знакома:

103
<form method="POST" id="newf" name="dat1"
enctype="multipart/form-data">
Добавить новый файл для страниц сайта:
<input type="file" id="newfile" name="newfile">
<input type="hidden" name="pas"
value="'; echo $pas; echo '">
<input type="button" id="but1" value="Загрузить">
</form>

Всего три отличия от аналогичных форм — поле загрузки имеет имя и id


newfile, id формы newf, а на кнопке текст «Загрузить».
Вторая форма имеет важное отличие — вместо текстового поля для запи-
си в него имени удаляемого элемента здесь использован выпадающий список
имеющихся файлов:

<form method="POST" id="delf" name="dat2">


Удалить файл:
<select id="sele" name="sele">';
...
echo '</select>
<input type="hidden" name="pas"
value="'; echo $pas; echo '">
<input type="button" id="but2" value="Удалить">
</form>

В основу формирования списка положен уже известный нам прием — от-


крытие директории и считывание имен файлов с одновременным удалением
символов текущего и вышестоящего каталогов:

$ident=0;
$opdir=opendir("file");
while($redir=readdir($opdir))
{
if(strpos($redir, "."))
{
...
}
}
closedir($opdir);

Переменная $ident выполняет роль идентификатора, необходимого для


определения, была ли уже напечатана строка option selected или еще нет.
Заполнение списка происходит следующим образом:

if($ident==0)
{
echo '<option selected value="'.$redir.'"
id="'.$redir.'">'.$redir.'</option>';
$ident=1;
}
else
echo '<option value="'.$redir.'"
id="'.$redir.'">'.$redir.'</option>';

104
Если идентификатор имеет начальное значение 0, то в качестве выбранного
будет пункт с именем первого файла:

echo '<option selected value="'.$redir.'"


id="'.$redir.'">'.$redir.'</option>';

Затем идентификатору присваивается новое значение:

$ident=1;

условие if($ident==0) становится ложным и в списке печатаются имена


остальных файлов:

else
echo '<option value="'.$redir.'"
id="'.$redir.'">'.$redir.'</option>';

12.2. Добавление файла


Добавление файла выполняется по уже «накатанной траектории». Реги-
стрируем обработчик и объявляем переменную для обращения к свойствам
кнопки «Загрузить»:
document.getElementById("but1").
addEventListener("click", cont);
...
let but1=document.getElementById("but1");

Проверяем факт загрузки, после чего данные отправляем на сервер:


function cont()
{
if(document.getElementById("newfile").value=="")
{
but1.value="ВЫ НЕ ЗАГРУЗИЛИ ФАЙЛ !";
return false;
}
else
{
let dt=new FormData(document.forms.dat1);
let re=new XMLHttpRequest();
re.open("POST", "newfile.php", true);
re.send(dt);
re.addEventListener("load", show);
}
}

На сервере вслед за проверкой выполняется закачка файла и отправка


ответа:

if($_FILES['newfile']['error']==0)
{
move_uploaded_file($_FILES['newfile']['tmp_name'],

105
'file/'.basename($_FILES['newfile']['name']));
echo '1;'.basename($_FILES['newfile']['name']);
}
else
echo '2';

Завершается цикл выводом необходимого сообщения, добавлением оче-


редного пункта в список файлов и возвращением кнопке первоначальной
надписи:

function show()
{
let ans=this.responseText;
let arans=ans.split(";");
if(arans[0]==1)
{
but1.value="ФАЙЛ ЗАГРУЖЕН !";
let point='<option value="'+arans[1]+'"
id="'+arans[1]+'">'+arans[1]+'</option>';
document.getElementById("sele").
insertAdjacentHTML("beforeend", point);
}
if(arans[0]==2)
but1.value="НЕКОРРЕКТНЫЕ ДАННЫЕ !";
setTimeout(()=>{but1.value="Загрузить";}, 2000);
}

Заносим в переменную point данные нового пункта списка:


let point='<option value="'+arans[1]+'"
id="'+arans[1]+'">'+arans[1]+'</option>';

и добавляем этот пункт в самый конец перед закрывающим тегом </select>:


document.getElementById("sele").
insertAdjacentHTML("beforeend", point);

12.3. Удаление файла


Мы выбрали в списке ненужный файл и нажали кнопку «Удалить». За-
пускается второй сценарий из файла file.js. Это уже неоднократно разобранный
нами код:

document.getElementById("but2").
addEventListener("click", del);
...
let but2=document.getElementById("but2");
function del()
{
if(window.confirm("Вы действительно хотите
удалить этот файл?")==true)
{
let dt=new FormData(document.forms.dat2);

106
let re=new XMLHttpRequest();
re.open("POST", "delfile.php", true);
re.send(dt);
re.addEventListener("load", see);
}
}

Серверная программа delfile.php удаляет файл:

...
if(isset($_POST['sele']) && strlen($_POST['sele'])>2)
{
unlink('file/'.$_POST['sele']);
echo '1';
}
else
echo '2';
...

после чего управление возвращается функции see JavaScript-сценария:

function see()
{
let ans=this.responseText;
if(ans==1)
{
but2.value="ФАЙЛ УДАЛЕН !";
let el=document.getElementById("sele").value;
document.getElementById(el).remove();
}
if(ans==2)
but2.value="НЕКОРРЕКТНЫЕ ДЕЙСТВИЯ !";
setTimeout(()=>{but2.value="Удалить";}, 2000);
}

и происходит удаление имени файла из выпадающего списка:

let el=document.getElementById("sele").value;
document.getElementById(el).remove();

107
13. СПИСОК СТРАНИЦ
Данную страницу мы оставили на десерт, так как с нее начинается до-
вольно большой блок, который мы будем освещать на протяжении четырех
глав. Сама вкладка устроена довольно просто — ей мы посвятим только одну
главу. Но здесь открывается путь к редактору страниц. А он, как вы помните,
является наиболее сложной частью нашей CMS. Описанию редактора мы уде-
лим внимание в трех главах подряд. Собственно, завершив рассмотрение ре-
дактора, мы тем самым подведем черту под изучением системы управления.
Но пока вернемся к заявленной теме — странице со списком. В ее работе
участвуют следующие файлы:
— page.php — формирует раздел создания и удаления страниц сайта;
— css/page.css — определяет дизайн раздела;
— js/page.js — формирует запросы на создание и удаление страниц;
— newpage.php — программа создания новых страниц;
— delpage.php — программа удаления лишних страниц;
— content/cont.txt и content/index.txt — текстовые файлы с данными
страницы «Контакты» и главной страницы.

13.1. Страница со списком


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

Рис. 13.1.1
Список существующих страниц и форма добавления новых

108
Сразу после удаления лишнего документа он исчезает из списка. Сразу по-
сле добавления нового документа он появляется в списке на последнем месте.
В следующем разделе мы приступим к подробному рассмотрению модулей.

13.2. Блок списка страниц


Для списка мы создали таблицу с пятью столбцами:
<table id="list">
<tr>
<td class="dash td">Название</td>
<td class="dash td">Идентификатор</td>
<td class="dash td">Рейтинг</td>
<td class="dash td">Видимость</td>
<td class="dash td"></td>
</tr>

В качестве названия страницы выводится текст, вписанный в тег title.


Идентификатор — это имя файла на компьютере. По данному идентифи-
катору происходит запрос страницы на сайте. Напомню, что для защиты от
злоумышленников открыть по ссылке можно только реально существующую
страницу. Попытки проникнуть в какие-либо иные файлы приводят к загрузке в
браузер главной страницы.
Рейтинг — число, определяющее место страницы в меню. Теоретически
оно может быть любым. Но тут советую соблюдать меру — не назначать мно-
гозначных рейтингов, если можно обойтись однозначным или двузначным
числом. Главный принцип следующий: чем меньше число, тем раньше имя
страницы расположится в списке и в меню сайта. Исходя из этого, самый вы-
сокий рейтинг (1) имеет главная страница, как бы она ни называлась. Учиты-
вайте еще один момент — в операционных системах Windows и Unix приме-
няются разные механизмы сортировки. Поэтому в Windows страницы с рей-
тингами 1, 5, 7, 11 будут располагаться именно в таком порядке. В Unix будет
по-другому: 1, 11, 5, 7. Учитывайте эту особенность при выборе чисел для
рейтингов. Также еще один совет — не назначайте рейтинги подряд: 1, 2, 3, 4
и т. д. Лучше оставлять «промежуток», например: 1, 3, 5, 7. Это нужно делать
на случай, если вам необходимо добавить новую страницу не в конец меню, а
ближе к началу или в середину. Если рейтинги 1, 2, 3, 4, а место новой стра-
ницы второе в меню, придется менять рейтинги остальных страниц, которые
должны оказаться ниже. Когда сайт большой — это очень затратное по време-
ни мероприятие.
Четвертый пункт — видимость. Он определяет, будет ли видеть посети-
тель эту страницу в меню. Когда вы создаете новый документ, он пустой. Есте-
ственно, его лучше скрыть от клиентов до тех пор, пока вы не заполните его
текстом и картинками. Обратите внимание — главная страница видна всегда.
Заголовок у пятого столбца отсутствует. В его ячейках помещаются
иконки для удаления той или иной страницы. Естественно, что с главной стра-
ницей произвести подобную операцию нельзя.

109
Заполняются ячейки таблицы следующим образом. По аналогии с файлом
index.php создаем ассоциативный массив, открываем директорию content, в
цикле считываем имена текстовых файлов и по очереди загружаем их содержи-
мое в переменную:
$srtar=array();
$opdir=opendir('content');
while($redir=readdir($opdir))
{
if(strpos($redir, '.'))
{
$rep=str_replace('.txt', '', $redir);
$name=file('content/'.$redir);
...
}
}
closedir($opdir);

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


цы, а значением — строка HTML-кода с необходимыми данными — названием,
идентификатором, рейтингом, видимостью и кнопкой удаления. Начинается
данная операция так:
$srtar[$name[0]]='<tr id="'.$rep.'22">
<td class="td"><form method="POST" action="edit.php">
<input type="hidden" name="pas" value="'.$pas.'">
<input type="hidden" name="edit" value="'.$rep.'">
<input type="submit" value="'.$name[2].'">
</form></td>
<td class="td">'.$rep.'</td>
<td class="td">'.$name[0].'</td>
<td class="td">'.$name[1].'</td>
<td class="td">';

Здесь мы создаем форму с идентификатором и паролем страницы. Форма,


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

<input type="submit" value="'.$name[2].'">

Кликнув по ней, вы тем самым загрузите документ в визуальный редак-


тор, который появится после отправки формы.
Для главной страницы вместо пиктограммы с корзиной печатаем пробел:

$pict='&nbsp;';
...
$srtar[$name[0]].=$pict.'</td></tr>';

Для остальных страниц создаем форму удаления:

if($rep!='index')
$pict='<input type="hidden" id="pas" name="pas"
value="'.$pas.'">

110
<input type="hidden" name="delp" value="'.$rep.'">
<img src="draw/basket.png" class="imdel"
id="del'.$rep.'" alt="..." title="...">';
$srtar[$name[0]].=$pict.'</td></tr>';

Обратите внимание: тег form в этом фрагменте отсутствует. О механизме


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

$newar=ksort($srtar);
foreach ($srtar as $key=>$value)
echo $srtar[$key];

echo '</table>
...

13.3. Удаление лишних страниц


Процесс удаления начинается с запуска сценария из файла page.js. Обра-
батывается клик на таблице с данными. Первым делом проверяем, что щелчок
мышью произошел на картинке с изображением корзины:
document.getElementById("list").
addEventListener("click", function(ev)
{
if(ev.target.tagName=="IMG")
{
...
}
});

Затем выясняем, действительно ли вы хотите удалить одну из страниц:

if(window.confirm("Вы действительно хотите


удалить эту страницу?")==true)
{
...
}

Дальше получаем id картинки, на которой вы щелкнули, и на основе это-


го id определяем, какую страницу предстоит удалить:

let idc=ev.target.id;
let pg=idc.split("del");

Создаем объект новой формы


let dt=new FormData();

и помещаем в него информацию из скрытых полей:


dt.append("pas",
document.getElementById("pas").value);
111
dt.append("delp", pg[1]);

На завершающем этапе отправляем данные серверному скрипту


delpage.php:

let re=new XMLHttpRequest();


re.open("POST", "delpage.php", true);
re.send(dt);
re.addEventListener("load", see);

Итак, в дело вступает скрипт delpage.php. Он максимально простой. Прове-


ряем входящие данные и удаляем файл точно так же, как мы это делали, удаляя,
например, картинки:

if(isset($_POST['delp']))
{
unlink('content/'.$_POST['delp'].'.txt');
echo '1;'.$_POST['delp'];
}

Сценарий из файла page.js получает ответ и удаляет страницу из списка:

function see()
{
let ans=this.responseText;
let arans=ans.split(";");
if(arans[0]==1)
{
let el=arans[1]+"22";
document.getElementById(el).remove();
}
}

Процесс завершен.
13.4. Блок добавления новых страниц
Этот модуль довольно элементарный — он представляет собой обычную
форму, в которой необходимо заполнить все поля:
<form method="POST" name="dat">
<table id="newpage">
<caption id="capnewp">Создать новую страницу</caption>
<tr>
<td class="rig td">Название</td>
<td class="td"><input id="name" name="name"
minlength="2" maxlength="30"></td>
</tr>
<tr>
<td class="rig td">Идентификатор</td>
<td class="td"><input id="ident" name="ident"
minlength="3" maxlength="10"></td>
</tr>
<tr>
<td class="rig td">Рейтинг</td>

112
<td class="td"><input id="namber" name="namber"
minlength="1" maxlength="2"></td>
</tr>
<tr>
<td class="rig td">Видимость</td>
<td class="td"><select name="sele">
<option selected value="НЕТ">НЕТ</option>
<option value="ДА">ДА</option>
</select></td>
</tr>
</table>

<input type="hidden" name="pas"


value="'; echo $pas; echo '">
<input type="button" value="Создать" id="but">
</form>

Если какое-то поле будет вами пропущено, соответствующее предупре-


ждение появится на кнопке «Создать».
Несколько важных моментов, на которые нужно обратить внимание.
1. Идентификатор страницы (фактически имя текстового файла в папке
content) задается один раз и навсегда. Впоследствии изменить его невозможно.
Поэтому отнеситесь к его выбору ответственно.
2. Название, рейтинг, видимость можно изменить, перейдя в редактор
данной страницы.
3. Изначально в строке «Видимость» установлено значение НЕТ по при-
чинам, изложенным в разделе 13.2. Вы можете изменить этот параметр и вы-
брать из выпадающего списка пункт ДА. Тогда новая страница сразу появится в
меню сайта.
4. В CMS не предусмотрено ограничение количества страниц. Но не
предусмотрено и меню второго или третьего уровня. Поэтому, если вам к стра-
ницам первого уровня нужно добавить несколько страниц второго, прибегните
к небольшой хитрости. Создавайте новые документы, делая их невидимыми, а
на страницах первого уровня размещайте ссылки на эти документы.

13.5. Добавление новой страницы


Первым делом регистрируем в файле page.js обработчик события:
document.getElementById("but").
addEventListener("click", cont);

и объявляем ссылку на кнопку:

let but=document.getElementById("but");

Запускаем обработчик и проверяем введенные параметры на коррект-


ность:
function cont()
{
if(document.getElementById("name").value.length<2

113
|| document.getElementById("name").value.length>30)
{
but.value="НЕВЕРНАЯ ДЛИНА НАЗВАНИЯ СТРАНИЦЫ !";
return false;
}
else
{
if(document.getElementById("ident").value.
length<3 ||
document.getElementById("ident").value.
length>10)
{
but.value="НЕВЕРНАЯ ДЛИНА ИДЕНТИФИКАТОРА !";
return false;
}
else
{
if(document.getElementById("namber").value
.length<1 ||
document.getElementById("namber").
value.length>2)
{
but.value="НЕВЕРНАЯ ДЛИНА РЕЙТИНГА !";
return false;
}
else
{
...
}
}
}
}

Если все в порядке, отправляем данные программе newpage.php:


else
{
let dt=new FormData(document.forms.dat);
let re=new XMLHttpRequest();
re.open("POST", "newpage.php", true);
re.send(dt);
re.addEventListener("load", show);
}

Она еще раз проверяет поступающую информацию и создает в папке


content новый файл:
if(isset($_POST['name']) && ($_POST['ident']) &&
($_POST['namber']) && ($_POST['sele']))
{
$con=$_POST['namber']."\n".$_POST['sele']."\n".
$_POST['name'].
"\nОписание страницы\nКлючевые слова\nСодержание";

file_put_contents
('content/'.$_POST['ident'].'.txt', $con);
...
}
else

114
echo '2';

В первые три строки записывается полученная из формы информация:

$con=$_POST['namber']."\n".$_POST['sele']."\n".
$_POST['name'].

а затем вместо четвертой, пятой и шестой строк добавляется поясняющий


текст:

."\nОписание страницы\nКлючевые слова\nСодержание";

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


альным контентом.
Обратно сценарию на JavaScript отправляем набор данных, разделенных
точкой с запятой:
echo '1;'.$_POST['pas'].';'.$_POST['ident'].';'.
$_POST['name'].';'.$_POST['namber'].';'.
$_POST['sele'];

Сценарий обрабатывает их, формирует надпись на кнопке и добавляет


новую строку в список страниц:

function show()
{
let ans=this.responseText;
let arans=ans.split(";");
if(arans[0]==1)
{
let point='...';
document.getElementById("list").
insertAdjacentHTML("beforeend", point);
but.value="СТРАНИЦА СОЗДАНА !";
}
if(arans[0]==2)
but.value="НЕКОРРЕКТНЫЕ ДАННЫЕ !";
setTimeout(()=>{but.value="Создать";}, 2000);
}

Строка, записываемая в переменную point, весьма длинная, поэтому ав-


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

115
14. БАЗОВЫЕ КОМПОНЕНТЫ РЕДАКТОРА СТРАНИЦ
Редактор контента, пожалуй, наиболее своеобразный компонент нашей CMS.
Для разработчиков, привыкших к стандартным системам, он выглядит крайне не-
обычно. Во-первых, отсутствует специальное WYSIWYG-окно, в котором выпол-
няются все манипуляции с текстом, рисунками, таблицами и т. д. Во-вторых, окно с
HTML-кодом существует как бы отдельно и открывается по клику на соответству-
ющей кнопке. В-третьих, под панелью редактирования расположился большой
фрейм, в который полностью выводится требуемая страница. Все действия с кон-
тентом происходят в трех ее блоках — основном, рекламном и нижнем — прямо во
фрейме, благодаря чему вы сразу видите, как будет выглядеть страница после тех
или иных изменений. Напомним, что содержание рекламного и нижнего блоков по-
вторяется на всех страницах. Наполнять два последних можно, зайдя в редактор
любой страницы. Наконец, последний (четвертый) пункт — панель редактирования
можно перемещать по фрейму в вертикальном направлении, тем самым располагая
ее в наиболее удобном месте.
Перечислим компоненты редактора:
— edit.php — формирует страницу редактора;
— css/edit.css — отвечает за внешний вид редактора и его отдельных
элементов;
— js/edit.js — выполняет основной объем работы по всем манипуляциям,
которые предусмотрены в редакторе;
— rec.php — записывает результаты в текстовые файлы страниц, рекла-
мы и нижнего блока;
— content/cont.txt и content/index.txt — текстовые файлы с данными
страницы «Контакты» и главной страницы;
— set/advert.txt — файл хранения рекламной информации;
— set/footer.txt — файл хранения контента нижнего блока.

14.1. Компоновка редактора


Как выглядит редактор, показано на рисунке 14.1.1. Сейчас он работает с
главной страницей демонстрационного сайта.
Файлы таблицы стилей и сценариев JavaScript подключаем в заголовоч-
ной части документа:
<link rel="stylesheet" href="css/every.css">
<link rel="stylesheet" href="css/edit.css">
<script src="js/edit.js"></script>

В верхней части страницы, как и в остальных разделах системы управле-


ния, расположено вспомогательное меню. Ниже — фрейм, в который происхо-
дит загрузка редактируемой страницы:
<iframe id="page" src="index.php?a='.$adr.'">
</iframe>
116
Ее адрес узнаем следующим образом. Создаем временную переменную
$test_adr и присваиваем ей значение, полученное из скрытого поля edit для то-
го документа, который мы выбрали в списке страниц:

$test_adr=$_POST["edit"];

Рис. 14.1.1
Внешний вид редактора
Создаем переменную-идентификатор, по значению которой будем су-
дить, запрошена существующая страница или нет:
$ch=0;

Открываем директорию, содержащую текстовые файлы страниц:


$opdir=opendir("content");

и в цикле проходим по именам этих файлов, сравнивая их со значением, пере-


данным из формы:
while($redir=readdir($opdir))
{
if($test_adr.".txt"==$redir)
{
$adr=$test_adr;
$ch=1;
break;
}
}

117
Если совпадение найдено, вводим новую переменную $adr, которой при-
сваиваем значение временной переменной:

$adr=$test_adr;

В этом случае идентификатор получит значение 1:

$ch=1;

а цикл будет прерван:

break;

Естественно, что при любом исходе сравнений после завершения цикла


закрываем директорию:

closedir($opdir);

Совпадений не найдено? Тогда выводим в браузер форму ввода пароля и


завершаем программу:

if($ch==0)
{
echo '...
<form method="POST" action="cms.php">
<p id="passw"><input type="password"
name="pas" minlength="5" maxlength="10"
id="pas"></p>
</form>
</body>
</html> ';
return;
}

Главная панель, поле HTML-редактора и кнопка для его откры-


тия/скрытия располагаются на отдельном слое:

<div id="bpan">
...
</div>

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

14.2. WYSIWYG и текстовый режимы


Сразу после загрузки редактора он переходит в режим визуального редак-
тирования. Чтобы посмотреть или поправить HTML-код, необходимо открыть
окно текстового редактора. В начальном состоянии оно скрыто. Для переклю-

118
чения режимов его видимости есть специальная вкладка (или кнопка — кому
как удобнее называть) с надписью «ОТКРЫТЬ HTML-КОД»:

<div id="htm">ОТКРЫТЬ HTML-КОД</div>

После ее нажатия под кнопкой появляется окно с HTML-кодом того бло-


ка, который вы редактируете в данный момент (рис. 14.2.1). Одновременно на
вкладке возникает другая надпись: «СКРЫТЬ HTML-КОД». Если теперь клик-
нуть по вкладке, текстовое поле исчезнет.
Читателям необходимо обратить внимание, что кнопки редактиро-
вания в окне HTML-кода не работают!

Рис. 14.2.1
Окно HTML-редактора
В визуальном режиме выбор редактируемого блока происходит на глав-
ной панели из выпадающего списка «Редактировать» после нажатия кнопки
«Выбрать». Предположим, мы выбрали пункт «Основное содержание» и нажа-
ли кнопку. На 2 секунды она станет голубой, подтверждая, что выбор сделан, а
затем примет исходный вид. Теперь можно вставить курсор в выбранную об-
ласть и начать формирование или изменение основного контента. Выберем ре-
жим «Блок рекламы» и снова нажмем кнопку «Выбрать». Теперь можно вста-
вить курсор в правый блок и поменять его текст, рисунок или что-то добавить к
имеющемуся контенту.
Все изменения, которые произошли с контентом в визуальном режиме,
передаются в HTML-окно в момент нажатия вкладки «ОТКРЫТЬ HTML-КОД».

119
В обратном направлении — в страницу, загруженную во фрейм, — передача
кода выполняется при скрытии текстового поля. Кнопка «Записать» активна
только в визуальном режиме. Но при этом все данные для записи берутся из
текстового окна. Такой способ выбран, чтобы вся информация концентрирова-
лась в одном месте в одном контейнере.
Контейнер form необходим для передачи данных серверной программе
записи rec.php.
В форме 9 полей — текстовое с HTML-кодом основного содержания:

<textarea id="tex" name="tex"></textarea>

и 8 скрытых:
— с именем файла, предназначенного для перезаписи (если записываются
данные из блока основного содержания);

<input type="hidden" id="str" name="str"


value="'.$adr.'">

— с паролем;

<input type="hidden" name="pas" value="'.$pas.'">

— с идентификатором области, данные из которой подлежат записи;

<input type="hidden" id="namb" name="namb" value="">

— с основными данными редактируемой страницы (если записываются


данные из блока основного содержания):

<input type="hidden" id="m1s" name="m1s" value="">


<input type="hidden" id="m2s" name="m2s" value="">
<input type="hidden" id="m3s" name="m3s" value="">
<input type="hidden" id="m4s" name="m4s" value="">
<input type="hidden" id="m5s" name="m5s" value="">

Шести из восьми скрытых полей не присвоены никакие значения. Их


присвоение выполняется в момент отправки данных серверной программе. Об
этом мы поговорим позже.

14.3. Главная панель


Главная панель находится у верхней границы фрейма (рис. 14.3.1). Она
содержит:
— список редактируемых блоков и кнопку выбора;
— кнопку записи данных;
— кнопки навигации по внесенным изменениям (в виде синих стрелок);
— кнопки «Старт» и «Стоп» для включения и отключения режима пере-
мещения панели;
120
— 22 кнопки и окно выбора цвета, выстроенные в ряд;
— вкладку открытия текстового редактора.
Разберемся с этими элементами по порядку.
Со списком все очень просто: раскрываете его, выбираете интересующий
блок и нажимаете кнопку «Выбрать». С этого момента выбранный блок готов к
редактированию.

Рис. 14.3.1
Главная панель
Чтобы вывести список, напишем вот такой код:

Редактировать:
<select id="sel">
<option selected value="1">Основное содержание</option>
<option value="2">Блок рекламы</option>
<option value="3">Нижний блок</option>
</select>
<input type="button" value="Выбрать" id="selrec">

Как видите, все просто.


Теперь про кнопку записи данных. Она выполняет также функцию выво-
да сообщений о результатах этого процесса. Если запись была успешной, на
кнопке на 2 секунды возникает текст «ЗАПИСАНО». При неудаче появляется
сообщение «НЕ ЗАПИСАНО» такой же продолжительности. После чего воз-
вращается исходная надпись «Записать». HTML-код кнопки очень простой:

<input type="button" value="Записать" id="rec">

Навигация по этапам редактирования напоминает аналогичную функцию,


например, из программы Word. Верхняя изогнутая стрелка служит для переме-
щения по промежуточным результатам редактирования от исходного состояния
к финишному. Нижняя стрелка загружает промежуточные результаты в обрат-
ном порядке. Сигналом для записи промежуточного состояния контента в па-
мять служит нажатие одного из знаков препинания, изменение форматирования
текста или вставка какого-либо элемента.
Кнопки навигации записываются так:
<img src="draw/forw.jpg" id="forw" alt="Вперед"
title="Вперед">
<img src="draw/back.jpg" id="bac" alt="Назад"
title="Назад">

121
Кнопки включения/отключения режима перемещения панели находятся у
ее правого края:

<input type="button" id="start" value="Старт"


title="...">
<input type="button" id="stop" value="Стоп"
title="...">

В качестве основы для главной панели служат слой div и таблица с жест-
ко заданными размерами:

<div id="panel">
<table id="pan">
<tr>

<td id="td1">Редактировать:
<!-- Список блоков -->
...
<!-- Кнопка выбора -->
...</td>
<td id="td2">
<!-- Кнопка записи -->
...</td>

</tr>

<tr>

<td id="td3" colspan="2">


<!-- Кнопки редактирования -->
...
</td>
</tr>
</table>
<!-- Кнопки навигации по внесенным изменениям -->
...

<!-- Кнопки включения/отключения перемещения панели -->


...
</div>

Хотя кнопки при наведении указателя мыши дают подсказки, для чего
они предназначены, на всякий случай перечислим их функции. Будем считать
кнопки слева направо.
1. Настройка основных параметров страницы. Нажмите эту кнопку,
чтобы открыть окно, в котором можно ввести рейтинг страницы, ее заголовок,
установить видимость, добавить описание и ключевые слова.
2. Копирование фрагмента. Копирует только текстовое содержимое.
Если необходимо скопировать фрагмент вместе с форматированием и элемен-
тами, выделите его, нажмите правую кнопку мыши и в контекстном списке вы-
берите пункт «Копировать».
122
3. Удаление форматирования. Удаляет все HTML-теги из выделенного
фрагмента.
4. Вставка линии. При нажатии этой кнопки открывается вкладка с
настройками линии. Закрывается либо при нажатии на пиктограмму в виде кре-
стика, либо при вставке линии в окно редактирования. Правило распространя-
ется на все остальные вкладки, поэтому в дальнейшем автор не станет напоми-
нать о нем.
5. Вставка таблицы. Открывает вкладку с настройками таблицы.
6. Вставка рисунка. Открывает панель выбора и настроек рисунка.
7. Вставка фрейма. Открывает окно с настройками фрейма.
8. Выбор символа. Выводит на экран список из 50 символов, которые
можно вставить в текст.
9. Маркированный список. Необходимо сделать выделение и нажать
данную кнопку, чтобы превратить текст в маркированный список.
10. Добавить ссылку. Открывает окно настроек ссылки.
11. Удалить ссылку. Удаляет выделенную ссылку, оставляя только ее
текст.
12. Добавить заголовок. Открывает вкладку с настройками заголовков.
13. Выравнивание текста. Выводит панель с настройками вырав-
нивания.
14. Выбор шрифта. Открывает окно с настройками шрифта.
15. Выделение жирным. Необходимо выделить текст и нажать кнопку.
У следующих пяти кнопок принцип действия аналогичен. Делается выделение
в тексте и нажимается кнопка.
16. Выделение курсивом.
17. Выделение подчеркиванием.
18. Выделение зачеркиванием.
19. Сделать фрагмент текста надстрочным.
20. Сделать фрагмент текста подстрочным.
21. Сделать текст цветным. Сначала необходимо открыть окно настро-
ек цвета, выбрать интересующий оттенок, выделить фрагмент и нажать данную
кнопку.
22. Открыть окно выбора цвета. Открывает вкладку с палитрой цве-
тов (рис. 14.3.2). Напоминаем, что у браузера Firefox палитра цветов отличается
внешним видом. Об этом мы говорили в главе 10.
23. Сделать фон текста цветным. Сначала необходимо открыть окно
настроек цвета, выбрать интересующий оттенок, выделить фрагмент и нажать
данную кнопку.

123
Рис. 14.3.2
Окно выбора цвета в большинстве браузеров

14.4. Основные настройки страницы


К основным настройкам страницы относятся пять параметров:
— рейтинг;
— видимость;
— название;
— описание страницы;
— ключевые слова.
Поскольку у любого сайта всегда должна быть главная страница, в редак-
торе контента предусмотрено два варианта основных настроек:
— для главной страницы;
— для всех остальных.
Между ними два отличия: у главной страницы нельзя поменять рейтинг и
видимость — только название, описание и ключевые слова. Эту страницу посе-
тители сайта видят всегда и только на первом месте в меню. У остальных стра-
ниц можно менять все настройки. Разные варианты вкладок показаны на ри-
сунке 14.4.1.

Рис. 14.4.1
Основные настройки главной страницы и остальных страниц
Запись основных настроек происходит только тогда, когда в списке «Ре-
дактировать» выбрано основное содержание. Если нажать кнопку записи в ре-
124
жиме редактирования рекламы или нижнего блока, запись основных настроек
для данной страницы не будет выполнена — они останутся прежними.
Вкладки основных настроек создаются следующим кодом.
Считываем из папки content содержимое того txt-файла, который служит
основой, загруженной в редактор страницы:

$meta=file('content/'.$adr.'.txt');

Начинаем формирование вкладки:

<div id="meta_d" class="cust">


<img src="draw/close.png" id="meta_i" class="cldiv"
alt="Закрыть" title="...">
<div class="deploy">Рейтинг: ';

Если загружена главная страница

if($adr=='index')
{
...
}

то просто выводим на вкладку параметры рейтинга и видимости без возможно-


сти их изменения:

echo '1 &nbsp;&nbsp; <input type="hidden" id="meta1"


value="1">
Показывать: ДА &nbsp;&nbsp;
<input type="hidden" id="meta2" value="ДА">';

При загрузке любой другой страницы выводим текстовое поле со значе-


нием рейтинга и выпадающий список с вариантами «ДА» и «НЕТ». Обратите
внимание — пункт, выбранный по умолчанию, будет соответствовать видимо-
сти, указанной в файле:

else
{
echo '<input id="meta1" class="inptx"
value="'; echo rtrim($meta[0]); echo '">
Показывать: <select id="meta2"
class="sele">';

if(rtrim($meta[1])=='ДА')
echo '<option selected value="ДА">ДА</option>
<option value="НЕТ">НЕТ</option>
</select>';
else
echo '<option selected value="НЕТ">НЕТ</option>
<option value="ДА">ДА</option>
</select>';
}

125
Остальные три поля при загрузке любой страницы содержат соответ-
ствующие данные из текстового файла:

echo 'Название: <input id="meta3" class="inpbig"


value="'; echo rtrim($meta[2]); echo '"><br>
Описание: <input id="meta4" class="inpbig"
value="'; echo rtrim($meta[3]); echo '">
Ключевые слова: <input id="meta5" class="inpbig"
value="'; echo rtrim($meta[4]); echo '"></div>
</div>
... ';

Все данные мы получаем при считывании файла в массив $meta. Функ-


цию rtrim мы используем, чтобы удалить из элементов массива символы пере-
вода строк. Если этого не сделать, разметка получится некорректной с точки
зрения валидатора HTML-кода.

14.5. Кнопки простого редактирования


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

Рис. 14.5.1
Кнопки простого редактирования
Перечень кнопок простого редактирования:
— копирование фрагмента;
— удаление форматирования;
— маркированный список;
— удаление ссылки;
— выделение жирным;
— выделение курсивом;
— выделение подчеркиванием;
— выделение зачеркиванием;
— сделать фрагмент текста надстрочным;
— сделать фрагмент текста подстрочным.
В разметке код этих кнопок выглядит следующим образом:

<img src="draw/copy.jpg" id="cop" class="pers"


alt="Копировать" title="...">

126
<img src="draw/format.jpg" id="forma" class="pers"
alt="Очистить формат" title="...">
<img src="draw/ul_li.jpg" id="ulli" class="pers"
alt="Создать маркированный список" title="...">
<img src="draw/del_link.jpg" id="delin" class="pers"
alt="Удалить ссылку" title="...">
<img src="draw/bold.jpg" id="bol" class="but"
alt="Жирный" title="...">
<img src="draw/ital.jpg" id="ital" class="but"
alt="Курсив" title="...">
<img src="draw/under.jpg" id="under" class="but"
alt="Подчеркнутый" title="...">
<img src="draw/strike.jpg" id="strik" class="but"
alt="Зачеркнутый" title="...">
<img src="draw/sup.jpg" id="sup" class="but"
alt="Надстрочный" title="...">
<img src="draw/sub.jpg" id="sub" class="but"
alt="Подстрочный" title="...">

Несмотря на идентичные параметры рисунков кнопок простого редакти-


рования, они принадлежат к разным стилевым классам. Сделано так из сообра-
жений удобства регистрации обработчиков событий (объяснения этому будут
даны позже). Первые четыре кнопки относятся к классу pers, остальные
шесть — к классу but.
Кнопки открытия вкладок с настройками создаются так же, как и пере-
численные выше. Единственное отличие — они принадлежат к стилевому клас-
су swit.
Кнопки простого редактирования, как ясно из их названия, работают мак-
симально просто: выделяете необходимую часть текста, нажимаете кнопку — и
фрагмент копируется, избавляется от форматирования, меняет начертание
и т. д., в зависимости от функции, которая «приписана» к нажатой кнопке. Если
вы нажмете кнопку, не сделав выделение, откроется диалоговое окно с преду-
преждением «Вы не выделили текст!» (рис. 14.5.2).

Рис. 14.5.2
Предупреждение о том, что текст не выделен

14.6. Блок настроек линий


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

127
вид. Для этого предназначены специальные вкладки, которые появляются на
экране при нажатии соответствующей кнопки.
Первая на очереди — кнопка вставки линии. Вкладка с элементами
настройки линий показана на рисунке 14.6.1. Вы можете выбрать толщину, ши-
рину и цвет. При введении толщины необходимо указать числовое значение и
размерность — пиксели, записанные латинскими буквами в сокращенном виде
(px). Например, так: 3px. Ширина может быть назначена в пикселях или про-
центах. Соответственно в этом поле необходимо указать значения так: 200px
или 50%.
Обратите внимание! Для всех остальных вкладок действует анало-
гичный принцип: вводя числовые параметры, необходимо указывать не
только число, но также единицы измерения, указанные в тексте перед со-
ответствующим полем (без пробелов после числа)!
Завершив выбор настроек, вставьте курсор в то место, куда вы хотите по-
местить линию, и нажмите кнопку «Вставить». Вкладка скроется, а линия по-
явится в редактируемой области. Если вы забыли поместить курсор, то линия
окажется в самой верхней части редактируемого блока. Кстати, это правило со-
блюдается и при добавлении всех остальных самостоятельных элементов: таб-
лицы, рисунка, фрейма и символа.
Можно вставить линию, не вводя никаких значений. В этом случае она
будет черной, толщиной 1 пиксель и шириной 100%.

Рис. 14.6.1
Окно настроек линий
Ниже приведен код, создающий данную вкладку на странице редактора:
<div id="hr_d" class="cust">
<img src=" draw/close.png" id="hr_i" class="cldiv"
alt="Закрыть" title="...">
<div class="deploy">
Толщина, px: <input id="hr1" class="inptx">
Ширина, px или %: <input id="hr2" class="inptx">
Цвет: <input type="color" id="hr3c"
class="colored" title="...">
<input type="button" id="buthr" class="butin"
value="Вставить" title="...">
</div>

</div>

14.7. Блок настроек таблиц


Вкладка с элементами настройки таблиц показана на рисунке 14.7.1. Вы
можете выбрать:
— ширину таблицы;

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

Рис. 14.7.1
Окно настроек таблиц
Программа не даст создать таблицу, если вы не укажете количество строк
или столбцов. Об этом появится предупреждение, которое вы можете видеть на ри-
сунке 14.7.2.

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

Фрагмент кода, создающий данную вкладку, приведен ниже:

<div id="tabl_d" class="cust bighei">


<img src=" draw/close.png" id="tabl_i"
class="cldiv" alt="Закрыть" title="...">
<div class="deploy">
Ширина, px или %: <input id="tab1" class="inptx">
Строки: <input id="tab2" class="inptx">
Колонки: <input id="tab3" class="inptx">
Внутренний зазор, px: <input id="tab4"
class="inptx">

129
<br>
Внешний зазор, px: <input id="tab5" class="inptx">
Шрифт: <select id="tab6" class="sele">
<option selected value="Tahoma">Tahoma</option>
<option value="Arial">Arial</option>
...
<option value="Times New Roman">
Times New Roman</option>
</select>
Размер, px: <input id="tab7" class="inptx">
Цвет шрифта: <input type="color" id="tab8c"
class="colored" title="...">

<br>
Рамка, px: <input id="tab9" class="inptx">
Цвет рамки: <input type="color" id="tab10c"
class="colored" title="...">
Цвет фона: <input type="color" id="tab11c"
class="colored" value="#FFFFFF" title="...">
Положение: <select id="tab12" class="sele">
<option selected value="no">НЕТ</option>
<option value="left">Слева</option>
<option value="right">Справа</option>
</select>
<input type="button" id="butab" class="butin"
value="Вставить" title="...">
</div>
</div>

14.8. Блок настроек рисунков


Вкладка выбора и настроек изображений показана на рисунке 14.8.1.
В данном окне вы можете:
— указать адрес картинки с внешнего сайта;
— указать имя фото из папки img нашего проекта;
— ввести подпись, которая будет записана в атрибуты alt и title рисунка;
— позиционировать снимок относительно других элементов или текста;
— настроить внешний зазор (то есть расстояние от внешнего контура
изображения до соседних элементов);
— указать ширину картинки;
— выбрать размер и цвет рамки.

Рис. 14.8.1
Окно выбора и настройки изображения

130
Скрипт добавления рисунка требует двух обязательных параметров:
— адрес внешнего фото или имя внутреннего (одно из двух);
— подпись к рисунку (обязательное условие Консорциума Всемирной
паутины — атрибут alt с текстом должен присутствовать в теге img).
Если вы оставите остальные поля незаполненными, то будет вставлена
картинка шириной 250px с рамкой черного цвета толщиной 1px.
Код вкладки выбора изображения:
<div id="img_d" class="cust bighei">
<img src="draw/close.png" id="img_i" class="cldiv"
alt="Закрыть" title="...">

<div class="deploy">
Внешнее фото: <input id="ima1" class="inpbig">
Фото с сервера: <input id="ima2" class="inpmid">
<input type="button" id="list" class="swit butin"
value="Выбрать" title="...">

<br>
Подпись: <input id="ima3" class="inpbig">
Положение: <select id="ima4" class="sele">
<option selected value="no">НЕТ</option>
<option value="left">Слева</option>
<option value="right">Справа</option>
</select>
Внешний зазор, px: <input id="ima5" class="inptx">

<br>

Ширина, px: <input id="ima6" class="inptx">


Рамка, px: <input id="ima7" class="inptx">
Цвет рамки: <input type="color" id="ima8c"
class="colored" title="...">
<input type="button" id="butima" class="butin"
value="Вставить" title="...">
</div>

</div>

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


следующей схеме:
— если введен адрес внешнего рисунка, то добавляется внешний рису-
нок;
— если введено имя внутреннего снимка, то добавляется внутренний
снимок;
— если заполнены оба поля, то преимущество имеют данные внешнего
фото — вставляется оно.
Чтобы разместить в области редактирования картинку из папки img,
необходимо нажать кнопку «Выбрать». Откроется вкладка со всеми изображе-
ниями на сервере (рис. 14.8.2). Щелкните на необходимом фото. Вкладка закро-
ется, а имя картинки (с расширением) появится в поле «Фото с сервера». Чтобы

131
просто закрыть вкладку без выбора рисунка, нажмите пиктограмму «крестик» в
левом верхнем углу.

Рис. 14.8.2
Панель выбора рисунков из папки img нашего проекта
HTML-код окна выбора внутреннего снимка:

<div id="list_d">
<img src="draw/close.png" id="list_i" class="cldiv"
alt="Закрыть" title="Закрыть">
<p id="p_list">';

$opdir=opendir("img");
while($redir=readdir($opdir))
{
if(strpos($redir, "."))
echo '<img class="roster" src="img/'.$redir.'" alt="Фото">';
}
closedir($opdir);
echo '</p>
</div>

Как видите, здесь вновь задействован PHP, который выполняет операции,


уже неоднократно рассмотренные нами в предыдущих главах. Сначала мы откры-
ваем папку img:

$opdir=opendir("img");

и считываем из нее все содержимое:

while($redir=readdir($opdir))
{
...
}
closedir($opdir);

132
Затем отсекаем лишние символы текущего и вышестоящего каталогов:

if(strpos($redir, "."))
...

и выводим фотографии на вкладку:

echo '<img class="roster" src="img/'.$redir.'"


alt="Фото">';

14.9. Блок настроек фреймов


Окно с элементами настройки фреймов показано на рисунке 14.9.1. Обя-
зательным параметром является адрес страницы. При его отсутствии появится
предупреждение: «Вы не указали адрес загружаемой страницы!».
Кроме того, вы можете:
— настроить размеры фрейма;
— выбрать его позиционирование (графа «Положение»);
— установить толщину рамки и ее цвет;
— задать внешний зазор от окружающих элементов или текста.

Рис. 14.9.1
Окно настройки фреймов
При отсутствии необходимых параметров вставляется фрейм с черной
рамкой толщиной 1 px и размерами в соответствии с внутренними установками
браузера (обычно ширина такого фрейма примерно 300 пикселей).
Обратите внимание: в окне фрейма будет показана страница, загружен-
ная только по протоколу https. Так происходит во всех браузерах — из сообра-
жений безопасности.
Код вкладки с настройками фреймов:
<div id="ifr_d" class="cust bighei">
<img src="draw/close.png" id="ifr_i" class="cldiv"
alt="Закрыть" title="...">

<div class="deploy">
Адрес загружаемой страницы:
<input id="ifr1" class="inpbig">
Ширина, px: <input id="ifr2" class="inptx">

<br>

Высота, px: <input id="ifr3" class="inptx">


Положение: <select id="ifr4" class="sele">
<option selected value="no">НЕТ</option>
<option value="left">Слева</option>

133
<option value="right">Справа</option>
</select>
Рамка, px: <input id="ifr5" class="inptx">
Цвет рамки: <input type="color" id="ifr6c"
class="colored" title="...">

<br>
Внешний зазор, px: <input id="ifr7" class="inptx">
<input type="button" id="butifr" class="butin"
value="Вставить" title="...">
</div>
</div>

14.10. Блок выбора символов


Панель с различными математическими (и не только) символами показа-
на на рисунке 14.10.1. Работать с ней очень просто: вставляете курсор в необ-
ходимое место окна редактирования и нажимаете кнопку требуемого символа,
после чего панель закрывается, а символ появляется в тексте.

Рис. 14.10.1
Окно выбора символов
Вообще, полезных символов очень много, но автор выбрал лишь
50 наиболее часто применяемых (на его взгляд).
Код вкладки занимает в документе довольно большой объем, поэтому
приведем его в сокращенном виде:

<div id="copyr_d" class="cust">


<img src="draw/close.png" id="copyr_i" class="cldiv"
alt="Закрыть" title="...">

<div id="copyr_sym">
<input type="button" class="sym" id="s1"
value="&#165;">
...
<input type="button" class="sym" id="s25"
value="&#8592;">

<br>
<input type="button" class="sym" id="s26"
value="&#8594;">
...
<input type="button" class="sym" id="s50"
value="&#967;">
</div>

</div>

134
В коде страницы почти все символы записаны в виде HTML-сущностей:
&#165;, &#167; и т. д. В окне редактирования они преобразуются в реальные
символы.

14.11. Блок настроек ссылок


Блок настроек ссылок показан на рисунке 14.11.1.
Окно редактирования позволяет создать ссылку на внешний ресурс, внут-
реннюю страницу сайта, электронную почту или файл.
Помимо адреса вы можете указать:
— для внешней ссылки — тип протокола соединения: https, http или
mailto;
— для внешней ссылки — открывать ли ее в новом окне или нет (не дей-
ствует для внутренней ссылки);
— нужно ли подчеркивать ссылку или нет;
— цвет текста ссылки.
Ссылка на внутреннюю страницу всегда открывается в текущем окне,
ссылка на файл — всегда в новом окне.
Выбор между внешней и внутренней ссылками, а также ссылкой на файл
происходит по следующей схеме:
— если введен адрес стороннего ресурса, то добавляется именно он;
— если адрес стороннего ресурса не введен, а в списке «Ссылка на файл»
оставлено пустое поле, то добавляется ссылка на внутреннюю страницу;
— если адрес стороннего ресурса не введен, а в выпадающем списке вы-
брано имя файла, то добавляется ссылка на файл.

Рис. 14.11.1
Окно настроек ссылок
Начальные установки вкладки после ее открытия: выбрана внутренняя
страница, цвет ссылки — черный, с подчеркиванием.
Чтобы добавить ссылку, откройте вкладку, выполните требуемые
настройки, выделите текст и нажмите кнопку «Вставить». Забыли сделать вы-
деление? Появится окно с предупреждением.
Код вкладки:

<div id="link_d" class="cust bighei">


<img src="draw/close.png" id="link_i" class="cldiv"
alt="Закрыть" title="...">

<div class="deploy">Добавить ссылку на внешний


ресурс: <select id="link1" class="selena">
<option selected value="https://">https://</option>
<option value="http://">http://</option>

135
<option value="mailto:">mailto:</option>
</select>
<input id="link2" class="inpbig"><br>
Открывать в новом окне: <select id="link3"
class="sele">
<option selected value="yes">ДА</option>
<option value="no">НЕТ</option>
</select>
Ссылка на страницу: <select id="link4"
class="sele">
...
</select>
Ссылка на файл: <select id="link7" class="sele">
<option selected value="999">&nbsp;</option>
...
</select><br>
Оформление: <select id="link5" class="sele">
<option selected value="yes">
С подчеркиванием</option>
<option value="no">Без подчеркивания</option>
</select>
Цвет: <input type="color" id="link6c"
class="colored" title="...">
<input type="button" id="butlink" class="butin"
value="Вставить" title="...">
</div>

</div>

Отдельно разберем, как создается выпадающий список «Добавить ссылку


на страницу»:

<select id="link4" class="sele">


...
</select>

Создаем идентификатор, необходимый для определения, было ли выве-


дено поле option selected или еще нет:

$ident=0;

Открываем директорию content, не забыв в конце процедуры закрыть ее:


$opdir=opendir("content");
...
closedir($opdir);

В цикле считывания содержимого директории


while($redir=readdir($opdir))
{
...
}

136
уже знакомым нам методом отсекаем символы текущего и вышестоящего ката-
логов:

if(strpos($redir, "."))
{
...
}
Поочередно заносим в массив содержимое каждого файла:

$arpage=file('content/'.$redir);

Нам понадобятся третьи строки из каждого файла — названия страниц


(зачем — поясним ниже).
Записываем в переменную идентификатор текущего файла, удалив из его
имени символы .txt (пояснения к этому действию также дадим чуть позже):

$rep=str_replace(".txt", "", $redir);

Теперь делаем проверку:


if($ident==0)
{
...
}

Если выпадающий список только начинает формироваться, то необходи-


мо текущий файл поставить в первой строке option с атрибутом selected:

if($ident==0)
{
echo '<option selected value="'.$rep.'">'.
rtrim($arpage[2]).'</option>';
...
}

После этого идентификатору присваивается новое значение:

$ident=1;

Теперь выражение

if($ident==0)

дает результат false и выполняется второй блок условия, в котором записана


инструкция печати остальных названий страниц:
else
echo '<option value="'.$rep.'">'.
rtrim($arpage[2]).'</option>';

137
В качестве значений пунктов списка мы используем идентификаторы
файлов (вот для чего была нужна переменная $rep), а между открывающим и
закрывающим тегами option вписываем названия страниц (заодно отрезая в
конце символы перевода строк):

rtrim($arpage[2])

Согласитесь, что выбирать из списка страницу по ее названию (вот для


чего были нужны третьи строки) гораздо удобнее, чем по идентификатору фай-
ла.
Теперь разберем блок кода, который формирует список файлов. В этом
процессе использован метод, очень похожий на тот, что мы видели при форми-
ровании перечня страниц:
echo '...
Ссылка на файл: <select id="link7" class="sele">
<option selected value="999">&nbsp;</option>';
$opdir=opendir("file");
while($redir=readdir($opdir))
{
if(strpos($redir, "."))
{
echo '<option value="'.$redir.'">'.$redir.
'</option>';
}
}
closedir($opdir);

echo '</select>
...

Здесь есть два принципиальных отличия:


1) пункт списка с атрибутом selected пустой

<option selected value="999">&nbsp;</option>

2) в качестве остальных пунктов используются полные имена файлов

echo '<option value="'.$redir.'">'.$redir.'</option>';

14.12. Блок настроек заголовков


Вкладка настроек заголовков изображена на рисунке 14.12.1. В данном
окне вы можете выбрать:
— уровень заголовка от h1 до h6;
— шрифт, размер шрифта и его цвет.
Как видно из рисунка, по умолчанию для заголовка установлен первый
уровень и шрифт «Tahoma» черного цвета. Пустует только поле для ввода кегля
(размера). Если все так и оставить, размер для h1 будет примерно 32 px.

138
Откройте вкладку, выполните требуемые настройки, выделите текст, кото-
рый станет заголовком, и нажмите кнопку «Вставить». Вы получите заголовок
выбранного уровня.

Рис. 14.12.1
Вкладка настроек заголовков
HTML-код блока настроек заголовков:
<div id="h16_d" class="cust">
<img src="draw/close.png" id="h16_i" class="cldiv"
alt="Закрыть" title="...">
<div class="deploy">
Заголовок: <select id="h161" class="sele">
<option selected value="h1">h1</option>
<option value="h2">h2</option>
...
<option value="h6">h6</option>
</select>
Шрифт: <select id="h162" class="sele">
<option selected value="Tahoma">Tahoma</option>
<option value="Arial">Arial</option>
...
<option value="Times New Roman">
Times New Roman</option>
</select>
Размер, px: <input id="h163" class="inptx">
Цвет шрифта: <input type="color" id="h164c"
class="colored" title="...">
<br>
<input type="button" id="buth16" class="butin"
value="Вставить" title="...">
</div>

</div>

14.13. Блок настроек выравнивания текста в абзацах


Панель настроек выравнивания текста в абзацах изображена на рисунке
14.13.1. Как видите, здесь 5 полей. Создавая такую вкладку, автор руководство-
вался следующим соображением: выделяя часть текста в отдельный абзац, ад-
министратор, наверное, предполагает, что текст будет иметь какое-то оформле-
ние. Так почему этот процесс надо разбивать на две или три операции, когда
все можно совместить в одной? Делать все настройки за один присест, на
взгляд автора, рациональнее, быстрее и проще.
Поэтому в данном окне вы можете выбрать:
1) выравнивание по левому или правому краю, по центру, по ширине
страницы;
139
2) шрифт, его размер и цвет;
3) отступ первой строки абзаца от левого края.
На третий пункт обратите особое внимание. В этом параметре можно ука-
зывать не только положительные числа, но также и отрицательные, например:
–50px. В последнем случае образуется не отступ, а выступ начала первой стро-
ки левее левого края остального текста.

Рис. 14.13.1
Панель настроек выравнивания текста в абзацах
Код блока настроек выравнивания текста:
<div id="equa_d" class="cust">
<img src="draw/close.png" id="equa_i" class="cldiv"
alt="Закрыть" title="...">
<div class="deploy">
Выравнивание: <select id="equa1" class="sele">
<option selected value="left">По левому краю
</option>
<option value="center">По центру</option>
<option value="right">По правому краю</option>
<option value="justify">По ширине</option>
</select>
Шрифт: <select id="equa2" class="sele">
<option selected value="Tahoma">Tahoma</option>
<option value="Arial">Arial</option>
...
<option value="Times New Roman">
Times New Roman</option>
</select>
Размер, px: <input id="equa3" class="inptx">
<br>
Цвет шрифта:
<input type="color" id="equa4c"
class="colored" title="...">
Отступ начала абзаца, px:
<input id="equa5" class="inptx">
<input type="button" id="butequa" class="butin"
value="Вставить" title="...">
</div>

</div>

14.14. Блок настроек шрифтов


Окно настроек шрифтов показано на рисунке 14.14.1. У читателя может
появиться вопрос: зачем такое окно, если все предварительные установки мож-
но выполнить, создавая абзац? Ответ очень простой: отдельная настройка необ-

140
ходима на случай, если надо выделить одно или несколько слов шрифтом, от-
личающимся от остального текста.
Панель имеет уже хорошо знакомые вам три параметра: шрифт, цвет и
размер в пикселях.
Так как в этом окне нет каких-либо особенностей, на которые необходимо
обратить внимание, просто посмотрим его HTML-код. Он максимально прост:
<div id="font_d" class="cust">
<img src="draw/close.png" id="font_i" class="cldiv"
alt="Закрыть" title="...">
<div class="deploy">
Шрифт: <select id="font1" class="sele">
<option selected value="Tahoma">Tahoma</option>
<option value="Arial">Arial</option>
...
<option value="Times New Roman">
Times New Roman</option>
</select>
Размер, px: <input id="font2" class="inptx">
Цвет шрифта: <input type="color" id="font3c"
class="colored" title="...">
<input type="button" id="butfont" class="butin"
value="Вставить" title="...">
</div>
</div>

Рис. 14.14.1
Блок настроек шрифтов

14.15. Блок настроек цвета текста


Последняя из рассматриваемых функций — настройка цвета текста — не
имеет персонального окна. Все делается непосредственно с главной панели. На ри-
сунке 14.15.1 стрелками показаны кнопки, выполняющие заявленные операции. Их
три: изменение цвета текста, выбор цвета, изменение цвета фона под текстом.
Работает все очень просто. Нажмите кнопку открытия окна выбора цвета
(на рис. 14.15.1 эта кнопка обозначена средней стрелкой). Выберите требуемый
тон, передвигая круглый ползунок по цветовой шкале. Перемещая круглый
курсор по прямоугольнику палитры, выберите необходимый оттенок. Выделите
текст. Для изменения цвета букв нажмите кнопку с символом «А» синего цвета
(левая на рис. 14.15.1). Для изменения цвета фона под текстом нажмите кнопку
с буквой «А» на синем фоне (правая на рис. 14.15.1).
Код, создающий элементы управления цветом:
<img src="draw/color.jpg" id="col" class="colo"
alt="Цвет шрифта" title="...">
<input type="color" id="ccc" title="...">
141
<img src="draw/backgr.jpg" id="backgr" class="colo"
alt="Цвет фона шрифта" title="...">

Рис. 14.15.1
Кнопки настроек цвета текста

14.16. Удаление форматирования и ссылок


В заключение — короткая справка о том, как работают кнопки удаления
форматирования и удаления ссылок.
Во-первых, действие этих кнопок абсолютно одинаково — они исключа-
ют из текста все HTML-теги, которые там есть, оставляя только сам текст. Та-
ким образом, если в выделение попадет, например, ссылка, она тоже будет уда-
лена вместе с такими тегами форматирования, как p, span, b, i и др. На резон-
ный вопрос: зачем тогда две разные кнопки, если они выполняют одну и ту же
функцию, есть резонный ответ. В нашем проекте использованы компоненты
редакторов из книги В. В. Янцева «JavaScript. Визуальные редакторы». Там
описаны разные системы, а среди них есть такая, в которой данные кнопки дей-
ствуют по-разному. Поэтому для соблюдения идентичности главной панели
нашего проекта с панелями редакторов из упомянутой книги и были сохранены
обе кнопки. Вдруг вам захочется изучить книгу, полностью посвященную визу-
альным редакторам, и использовать в своих проектах вариант, в котором при-
сутствуют описанные кнопки с разным механизмом действия.
Во-вторых, чтобы удалить форматирование из фрагмента текста, необхо-
димо сделать выделение на один символ слева и справа больше. Как это выгля-
дит на практике? Допустим, у вас есть слово «визуальный» с буквами аль по-
лужирного начертания. Чтобы привести их к нормальному отображению, надо
выделить мышью буквы уальн и нажать кнопку удаления форматирования
(выделение должно выглядеть так: визуальный). Если у вас есть фрагмент тек-
ста «этот визуальный редактор», в котором полужирным шрифтом набрано
слово «визуальный», то для его приведения к исходному состоянию надо вы-
делить не только само слово, но и пробелы до соседних слов (вот так: этот ви-
зуальный редактор).
На первых порах такой способ удаления форматирования покажется не-
привычным, но со временем вы привыкнете. Тем более что удаление формати-
рования при создании или редактировании контента — процесс довольно ред-
кий.
На этом разговор о кнопках и вкладках завершен. Теперь перейдем к са-
мому главному — сценариям на JavaScript, которые управляют всеми процес-
сами редактирования и записи данных.

142
15. ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ
Любой редактор всегда состоит как минимум из трех «ингредиентов»:
файла страницы, таблицы стилей и файла со сценариями, написанными на
JavaScript. По сути, JavaScript — это квинтэссенция, альфа и омега, основа
нашего визуального редактора. Именно сценарии меняют или создают контент
для сайта.
Как вы помните, сценарии нашего редактора собраны в файле js/edit.js.
Для начала перечислим компоненты, которые не отвечают за изменение кон-
тента:
— «главный» обработчик, служащий контейнером для всех остальных
обработчиков и функций;
— инструкции выбора редактируемого блока;
— инструкции, обеспечивающие взаимодействие визуального и текстово-
го редакторов;
— всего одна строка кода для фиксации выделенной области в окне визу-
ального редактора;
— функции открытия и закрытия вкладок с настройками;
— сценарий выбора рисунка с сервера (то есть из папки img);
— блок навигации по истории внесенных изменений с возможностью за-
фиксировать начальный, конечный или любой промежуточный результат;
— блок записи контента отредактированной страницы;
— код, отвечающий за перемещение панели.
Двинемся по порядку.

15.1. Регистрация «главного» обработчика


Один из самых простых фрагментов кода. Как вы помните, файл
JavaScript мы подключаем в заголовочной части документа. Следовательно,
прежде чем регистрировать обработчики нажатия различных кнопок, необхо-
димо сначала дождаться полной загрузки страницы, чтобы браузер «увидел»
эти кнопки. Значит, самым первым, «главным» обработчиком должен быть сле-
дующий:
addEventListener("load", function()
{
...
});

После загрузки документа в окно браузера можно регистрировать все


остальные обработчики:

addEventListener("load", function()
{
Регистрация обработчика 1

143
Регистрация обработчика 2
...
Регистрация обработчика N
});

Часто сами функции, выполняющие роли обработчиков, располагают вне


блока:
addEventListener("load", function()
{
...
});

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


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

15.2. Выбор редактируемого блока


Сейчас нам предстоит забежать немного вперед и выполнить ряд дей-
ствий, в том числе и таких, необходимость которых более детально мы станем
обсуждать в следующих разделах этой главы.
Выбор редактируемого блока тесно связан с:
— получением данных от той или иной области редактирования в стра-
нице из фрейма;
— передачей данных в текстовое поле;
— записью промежуточных результатов редактирования;
— появлением на кнопке выбора режима редактирования сообщения
«Выбрано» и возвратом первоначальной надписи.
По умолчанию после загрузки редактируемой страницы в качестве рабо-
чей области устанавливается слой content. Поэтому сначала мы вводим пере-
менную ed, необходимую для сокращенного обращения к редактируемому
слою:

let ed=frames[0].document.getElementById("content");

Затем добавляем переменную для сокращенного обозначения текстового


поля (напоминаю, в начальном состоянии оно скрыто):

let te=document.getElementById("tex");

Теперь создаем пустой массив для хранения промежуточных результатов


редактирования:

let arr=[];

144
и вводим две переменные:
— счетчик внесенных изменений

let cna=0;

— идентификатор начала и конца просмотра

let vnc=0;

Предварительные действия выполнены, теперь можно приступать к напи-


санию функции, обрабатывающей выбор области редактирования. Запускается
процесс нажатием кнопки «Выбрать»:

document.getElementById("selrec").
addEventListener("click", function()
{
...
}

Первая операция функции — получение значения выбранной области из


списка:
let sr=document.getElementById("sel").value;

В зависимости от этого значения выполняется один из трех блоков кода:


if(sr==1)
{
...
}
if(sr==2)
{
...
}
if(sr==3)
{
...
}

Какие здесь решаются задачи и выполняются операции, рассмотрим на


примере первого условия:
if(sr==1)
{
ed.setAttribute("contenteditable", false);
ed=frames[0].document.getElementById("content");
cna=0;
vnc=0;
arr=[];
arr[0]=ed.innerHTML;
ed.setAttribute("contenteditable", true);
te.value=ed.innerHTML;
}

145
Представим, что у нас уже был выбран какой-то режим, отличный от ре-
жима «Основное содержание». Теперь выберем пункт «Основное содержание».
Мы получим из выпадающего списка значение 1, а значит, условие, вынесенное
в начало блока, будет истинным и начнется выполнение приведенных ранее ин-
струкций.
Первым делом отменяем режим редактирования в ранее выбранном слое,
установив его атрибуту contenteditable значение false:
ed.setAttribute("contenteditable", false);

Следом записываем в переменную ed ссылку на новый слой — в данном


случае content:

ed=frames[0].document.getElementById("content");

При манипуляциях с содержимым в предыдущем слое у нас в массиве с


промежуточными результатами могли оказаться какие-то данные. А значит, и
текущие значения переменных cna и vnc сейчас отличны от нуля. Поскольку
мы приступаем к работе в новом слое, все данные из массива необходимо сте-
реть, а переменные обнулить:

cna=0;
vnc=0;
arr=[];

Теперь первым значением массива станет состояние выбранного слоя на


момент переключения режима:

arr[0]=ed.innerHTML;

В данном конкретном случае в элемент массива с индексом 0 мы помеща-


ем HTML-код из слоя content.
Устанавливаем для этого слоя атрибут contenteditable со значением true

ed.setAttribute("contenteditable", true);

и передаем HTML-код в текстовое поле:

te.value=ed.innerHTML;

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


перечисленные операции для слоев advert и footer после переключения обла-
стей редактирования:

if(sr==2)
{
ed.setAttribute("contenteditable", false);
ed=frames[0].document.getElementById("advert");
...
146
}
if(sr==3)
{
ed.setAttribute("contenteditable", false);
ed=frames[0].document.getElementById("footer");
...
}

Необходимо отметить, что данные о внесенных изменениях в предыду-


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

let symb=["Enter", "!", "?", ";", ":", ",", "."];


for(let i=0; i<symb.length; i++)
{
if(symb[i]==ev.key)
inter();
}

Этот фрагмент мы будем разбирать позже.


Последний этап выбора редактируемого слоя — вывод подтверждения о
сделанном выборе и возврат кнопке первоначальной надписи.
Вводим переменную для сокращенного обращения к кнопке выбора:
let dse=document.getElementById("selrec");

Выводим подтверждающий текст:


dse.value="Выбрано";

и меняем цвет кнопки:


dse.style.background="#DDEEFF";

147
По прошествии 2 секунд возвращаем кнопке первоначальную надпись и
цвет:
setTimeout(()=>{dse.value="Выбрать";
dse.style.background="#FFFFFF";}, 2000);

15.3. Взаимодействие текстового поля


и редактируемой области
Во всех сценариях различные функции неоднократно обращаются к трем
слоям загруженной во фрейм страницы, а также к окну текстового редактора.
Поэтому для сокращения кода мы в самом начале создали ссылки на эти окна:
let ed=frames[0].document.getElementById("content");
let te=document.getElementById("tex");

и по мере необходимости меняли адрес первой ссылки внутри функции выбора


на
ed=frames[0].document.getElementById("advert");

или на
ed=frames[0].document.getElementById("footer");

Как происходит передача информации из редактируемой области в тек-


стовое поле, вы уже видели в предыдущем разделе, когда мы разбирали функ-
цию выбора:

te.value=ed.innerHTML;

Помимо такого взаимодействия предусмотрено еще одно. Оно происхо-


дит во время переключения режима работы с визуального представления на
HTML-вкладку и обратно. Это взаимодействие включает два этапа, которые
объединены в одну анонимную функцию. Поскольку в ней неоднократно
встречаются обращения к одним и тем же элементам редактора, для сокраще-
ния кода запишем две ссылки:
— на стили текстового поля

let tex=document.getElementById("tex").style;

— на кнопку записи

let rec=document.getElementById("rec");

Объявим переменную, которая будет выступать в роли идентификатора


открытия текстового поля:

let cou=1;

148
В анонимной функции
document.getElementById("htm").
addEventListener("click", function()
{
...
})

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


запускается, когда текстовое поле скрыто, идентификатор имеет значение 1 и
мы нажали кнопку «ОТКРЫТЬ HTML-КОД»:
if(cou==1)
{
...
}

Начальная операция этого блока — передача HTML-кода из редактируе-


мого слоя в текстовую область:
te.value=ed.innerHTML;

Вторая операция — открытие текстового поля:


tex.visibility="visible";

Мы условились, что в текстовом режиме отправка данных серверной про-


грамме невозможна. Поэтому деактивируем кнопку «Записать», установив ей
атрибут disabled:
rec.setAttribute("disabled", true);

А чтобы подчеркнуть отключение кнопки, делаем ее полупрозрачной:

rec.style.opacity=0.2;

Теперь выводим на кнопку HTML-редактора новый текст:

document.getElementById("htm").innerHTML=
"СКРЫТЬ HTML-КОД";

и меняем значение идентификатора:

cou=2;

Чтобы скрыть вкладку HTML-кода, нажмем на кнопку «СКРЫТЬ HTML-


КОД». После этого начнутся обратные процессы:
— данные из текстового поля поступят в контейнер основного содержи-
мого страницы

ed.innerHTML=te.value;
149
— вкладка текстового редактора скроется

tex.visibility="hidden";

— кнопка записи снова вернется в рабочее состояние

rec.style.opacity=1;
rec.removeAttribute("disabled");

— на вкладке появится исходный текст


document.getElementById("htm").innerHTML=
"ОТКРЫТЬ HTML-КОД";

— идентификатор опять получит значение 1

cou=1;

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


текстового поля завершен.

15.4. Фиксация в памяти выделенного фрагмента


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

let sel=frames[0].document.getSelection();

Когда выделенная область создана, ее можно «обрамлять» различными


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

15.5. Открытие и закрытие вкладок с настройками


Уже упоминалось, что кнопки сложного редактирования относятся к сти-
левому классу swit (к этому же классу относится и кнопка «Выбрать», откры-
вающая вкладку с перечнем изображений в папке img). Данный фактор исполь-
зуется в универсальной функции открытия вкладок с настройками элементов.
На первом этапе мы получаем массив всех кнопок, относящихся к упомя-
нутому классу:

let s=document.querySelectorAll(".swit");

150
А затем в цикле регистрируем список обработчиков нажатия этих кнопок:

for(let i=0; i<s.length; i++)


{
let idc=s[i].id;
let nid=idc+"_d";
document.getElementById(idc).
addEventListener("click", ins.bind(null, nid));
}

Сначала получаем id очередной кнопки

let idc=s[i].id;

после чего выясняем имя вкладки, которая должна быть открыта:

let nid=idc+"_d";

Каждый редактор написан так, что определенной кнопке с некоторым id


соответствует вкладка, идентификатор которой отличается добавлением ниж-
него подчеркивания и символа d к id кнопки.
Третий шаг — регистрация обработчика нажатия очередной кнопки:

document.getElementById(idc).
addEventListener("click", ins.bind(null, nid));

Обращаем внимание читателя, что метод bind создает новую функцию,


которая при вызове устанавливает в качестве контекста выполнения некоторое
значение, а также принимает набор аргументов. В нашем случае аргумент
один — id вкладки с настройками. К bind мы прибегли, так как иначе не смогли
бы передать в функцию-обработчик необходимый параметр. Поскольку мы не
привязываем к функции контекст выполнения, то первым «пунктом» указываем
null.
Функция ins, которая записана в качестве обработчика для нажатия лю-
бой из кнопок сложного редактирования, содержит всего одну инструкцию —
открытия вкладки с id, переданным в аргументе:
function ins(k)
{
document.getElementById(k).style.visibility=
"visible";
}
Для закрытия вкладок служит функция clo:

function clo(c)
{
document.getElementById(c).style.visibility=
"hidden";
}

151
Она срабатывает после нажатия иконки с крестиком или после нажатия
кнопки «Вставить» на вкладке с настройками, а также после нажатия на любом
из знаков вкладки специальных символов.
В качестве аргумента функция получает id окна, которое необходимо за-
крыть. Следующий код регистрирует функцию clo в качестве обработчика
нажатия иконок с крестиком:
let t=document.querySelectorAll(".cldiv");
for(let i=0; i<t.length; i++)
{
let idc=t[i].id;
let nid=idc.replace("_i", "_d");
document.getElementById(idc).
addEventListener("click", clo.bind(null, nid));
}

Иконки закрытия окон относятся к стилевому классу cldiv. Получаем


массив иконок, в цикле выясняем их id и заменяем в идентификаторах букву d
на i (id любой иконки закрытия отличается от id вкладки символом i вместо d
после нижнего подчеркивания). Последний этап — регистрация функции clo в
качестве обработчика щелчка на каждой из иконок.
Как формируется обращение к функции clo после выбора настроек на
вкладках, мы рассмотрим позже, когда перейдем к подробному описанию
редакторов.

15.6. Выбор рисунка


В окне настроек изображений есть кнопка «Выбрать». Если ее нажать, то
откроется вкладка с галереей фото из папки img (рис. 15.6.1). Допустим, вы
изучили все варианты и остановились на каком-то рисунке. Нажмите на него.
Вкладка закроется, а имя файла выбранной картинки появится в текстовом поле
рядом с надписью «Фото с сервера».
Управляет процессом выбора одна довольно простая анонимная функция,
которая в качестве аргумента получает объект события и запускается при нажа-
тии на любое фото из представленной галереи:

document.getElementById("p_list").
addEventListener("click", function(ev)
{
...
});

152
Рис. 15.6.1
Панель выбора рисунков из папки img
Как видите, мы регистрируем не все случаи нажатия на каждый рисунок,
а только одно событие — клик в области
<p id="p_list">
...
</p>

окна выбора.
Дальше узнаем, пришелся ли щелчок на пустое пространство или на
изображение:
if(ev.target.tagName=="IMG")
{
...
}

Если условие верно, выясняем имя файла выбранной картинки. Для этого
берем у объекта события атрибут src и разбиваем полученное из него значение
на две части. В качестве разделителя служит имя папки с фотографиями img/:

let pho=ev.target.src.split('img/');

Второй элемент pho[1] из образовавшегося массива и будет содержать


имя файла. Передаем это имя в текстовое поле:
document.getElementById("ima2").value=pho[1];

и закрываем вкладку:

document.getElementById("list_d").style.visibility=
"hidden";

153
15.7. Навигация по внесенным изменениям
Редактор спроектирован таким образом, что записывает в память проме-
жуточные результаты изменений, внесенных в контент. Графические кнопки
«Вперед» и «Назад» позволяют перемещаться по этим этапам в прямом и об-
ратном направлении так же, как это делается, например, в программе Word.
Допустим, вы вставили в один из слоев рисунок, а потом выделили одно из
слов цветом, отличным от цвета остального текста. Если теперь один раз
нажать кнопку «Назад», то исчезнет цветовое выделение. Нажимаем второй
раз — исчезает картинка и редактируемый контент приобретает первоначаль-
ный вид. Теперь нажмем кнопку «Вперед» — картинка вновь займет свое ме-
сто. Нажмем кнопку еще раз — вновь появится цветовое выделение слова.
То есть контент примет тот же вид, что был достигнут по завершении послед-
него шага редактирования. Думаю, из этого описания алгоритм навигации по
внесенным изменениям понятен.
Пошаговая запись промежуточных данных выполняется в двух случаях:
— при внесении изменений с помощью кнопок редактирования главной
панели (то есть при изменении форматирования текста или добавлении любых
элементов);
— при нажатии на клавиатуре кнопок со знаками препинания или
«Enter».
Администратор должен учитывать второй случай, так как введенный
текст будет записан в память только после нажатия одной из перечисленных
кнопок.
Сохранение в памяти промежуточных данных выполняет специальная
функция inter. Чтобы обеспечить ее корректную работу, предварительно необ-
ходимо объявить несколько переменных. Сначала создаем пустой массив, в ко-
тором очередной промежуточный результат будет следующим элементом:

let arr=[];

Понятно, что первый элемент — HTML-код исходного состояния контен-


та в редактируемой области:

arr[0]=ed.innerHTML;

Как вы помните, мы еще объявляем две переменные:


— счетчик внесенных изменений

let cna=0;

— идентификатор начала и конца просмотра

let vnc=0;

Все готово, можно написать функцию:


154
function inter()
{
cna++;
vnc++;
arr[cna]=ed.innerHTML;
}

Как видите, при каждом ее вызове значения идентификатора и счетчика


увеличиваются на единицу, а в массив добавляется новый элемент с новыми
данными из редактируемого слоя:

arr[cna]=ed.innerHTML;

Когда происходит вызов функции inter? Допустим, вы нажали одну из


кнопок простого редактирования. Будет запущена функция, обрабатывающая
это событие. После того как контент изменится, одна из последних инструкций
вызовет функцию inter. Если вы добавляете в окно редактирования какой-то
элемент, то запускается функция, отзывающаяся на событие нажатия кнопки
«Вставить». И опять одна из ее последних инструкций вызовет функцию inter.
Для событий нажатия клавиш со знаками препинания или перевода стро-
ки у нас существует еще одна функция. Мы уже упоминали ее в разделе 15.2:
ed.addEventListener("keyup", function(ev)
{
for(let i=0; i<symb.length; i++)
{
if(symb[i]==ev.key)
inter();
}
});

Здесь в цикле проверяется, совпадает ли символ нажатой клавиши с сим-


волом из заранее объявленного массива symb:
let symb=["Enter", "!", "?", ";", ":", ",", "."];

Если совпадение обнаружено, вызывается функция inter.


Теперь рассмотрим, как выполняется навигация. Начнем с движения в
обратном направлении. Для его реализации файл содержит соответствующую
анонимную функцию, запускающуюся при щелчке на графической кнопке
«Назад»:
document.getElementById("bac").
addEventListener("click", function()
{
if(vnc>0)
{
vnc--;
ed.innerHTML=arr[vnc];
}
});

155
Первым делом надо убедиться, что значение идентификатора vnc больше
нуля (то есть результаты изменений не перемотаны до исходного состояния):
if(vnc>0)
{
...
}

Если это так, уменьшаем значение vnc на единицу:

vnc--;

после чего загружаем в область редактирования предыдущие данные:

ed.innerHTML=arr[vnc];

Перемоткой промежуточных результатов вперед управляет другая функция:

document.getElementById("forw").addEventListener("click", func-
tion()
{
if(vnc<arr.length-1)
{
vnc++;
ed.innerHTML=arr[vnc];
}
});

Здесь проверяется, не достигло ли значение идентификатора vnc верхней


границы:

if(vnc<arr.length-1)
{
...
}

В случае истинности условия значение идентификатора увеличивается на


единицу:

vnc++;

после чего вновь загружаем в редактируемую область последующие данные:

ed.innerHTML=arr[vnc];

Таким образом, можно загружать в соответствующий слой контент с раз-


ных стадий редактирования.

156
15.8. Запись отредактированной страницы
Отправка данных серверной программе для записи в файл и получение
ответа происходят в фоновом режиме. Для этого мы используем технологию
Ajax.
Схема процесса выглядит следующим образом. После нажатия кнопки
«Записать» HTML-код из области визуального представления считывается в
текстовый редактор, который является элементом формы. Вторым элементом
служит скрытое поле, содержащее адрес записываемой страницы. Третье поле
содержит пароль. Четвертое — индекс редактируемой области. И еще пять —
данные основных настроек страницы.
Информация из формы пересылается методом POST скрипту rec.php.
Скрипт проверяет входные параметры и, если они корректные, производит за-
пись в файл. В случае успешной записи rec.php возвращает 1, при неудаче — 2.
Сообщения о результатах на 2 секунды появляются на кнопке отправки данных
(рис. 15.8.1), после чего ей возвращается исходный текст.

Рис. 15.8.1
Сообщение о результатах записи на кнопке отправки данных
Для реализации перечисленных этапов у нас существуют две функции.
Первая запускается после щелчка на кнопке «Записать»:

document.getElementById("rec").
addEventListener("click", function()
{
...
});

Как мы помним, скрытое поле с индексом редактируемого слоя и скры-


тые поля для параметров основных настроек страницы в исходном состоянии
пусты. Поэтому на первом этапе мы заполняем эти поля данными, полученны-
ми из списка выбора редактируемой области
document.getElementById("namb").value=
document.getElementById("sel").value;

и текстовых полей основных настроек:

document.getElementById("m1s").value=
document.getElementById("meta1").value;

157
document.getElementById("m2s").value=
document.getElementById("meta2").value;
document.getElementById("m3s").value=
document.getElementById("meta3").value;
document.getElementById("m4s").value=
document.getElementById("meta4").value;
document.getElementById("m5s").value=
document.getElementById("meta5").value;

Затем удаляем все символы перевода строк в редактируемом слое:


let edin=ed.innerHTML.replace(/\n/g, "");

и копируем данные в textarea:


te.value=edin;

Далее создаем:
— новый объект с данными из формы
let dt=new FormData(document.forms.dat);

— новый объект интерфейса XMLHttpRequest


let re=new XMLHttpRequest();

Методом open формируем запрос:


re.open("POST", "rec.php", true);

и отправляем его серверному скрипту:


re.send(dt);

Также устанавливаем, что после загрузки ответа должна запускаться


функция show:

re.addEventListener("load", show);

Ее задача — на основе полученного от серверной программы ответа вы-


вести то или иное сообщение на кнопке отправки данных:
function show()
{
let re=document.getElementById("rec");
let ans=this.responseText;
if(ans==1)
re.value="ЗАПИСАНО";
else
re.value="НЕ ЗАПИСАНО";

setTimeout(()=>{document.getElementById("rec").
value="Записать";}, 2000);
}

158
Последняя инструкция сообщает программе, что через 2 секунды необхо-
димо вернуть кнопке исходную надпись:
setTimeout(()=>{document.getElementById("rec").
value="Записать";}, 2000);

Теперь перейдем к разбору файла записи данных rec.php. Первым делом


коснемся темы безопасности.
Файл rec.php выполняет следующие проверки:
— наличие и правильность пароля в данных из формы;
— наличие адреса страницы, предназначенной для записи данных (на
случай, если выполняется запись именно файла страницы);
— соответствие переданного адреса имени файла одной из существую-
щих страниц;
— проверка наличия параметра с данными для записи.
Как уже сказано, работа программы начинается с проверки пароля. Про-
цедура уже неоднократно описана, поэтому заострять на ней внимание не ста-
нем.
Следующий шаг — проверка адреса страницы (тоже знакомое действие
по разделу 14.1). Сначала выясняем, передан ли данный параметр:
if(isset($_POST['str']))
$str=$_POST['str'];
else
{
echo '2';
return;
}

Если «да», то присваиваем переменной $str имя файла страницы:

$str=$_POST['str'];

и продолжаем выполнение скрипта. Если нет, прерываем выполнение программы:


echo '2';
return;

Затем выясняем, существует ли такая страница:


$ch=0;
$opdir=opendir('content');
while($redir=readdir($opdir))
{
if($str.'.txt'==$redir)
{
$ch=1;
break;
}
}
closedir($opdir);

159
if($ch==0)
{
echo '2';
return;
}

Если существует, присваиваем переменной $adr адрес этой страницы:


$adr='content/'.$str.'.txt';

Если нет, то завершаем программу:


echo '2';
return;

У администратора сайта может возникнуть необходимость временно уда-


лить весь контент с одной из страниц. В этом случае программе записи будет
отправлен пустой параметр tex, что вызовет ошибку при записи в файл. Избе-
жать ошибки можно, если, например, заменить «пустоту» на пробел. В этом
случае программа посчитает, что данные из параметра переданы корректно, и
запишет в файл пробел. Но визуально страница останется пустой.
Исходя из этих соображений, построена проверка параметра с данными
контента:

$tex=' ';
if(isset($_POST['tex']))
{
if($_POST['tex']!='')
$tex=$_POST['tex'];
}
else
{
echo '2';
return;
}

Создаем переменную $tex и помещаем в нее пробел:

$tex=' ';

Выясняем, передан ли параметр с данными для записи:

if(isset($_POST['tex']))

Если «нет», то останавливаем выполнение программы:


echo '2';
return;

Если «да», то проверяем, не «пусто» ли в данных:


if($_POST['tex']!='')
160
Когда данные присутствуют, помещаем их в переменную $tex:

$tex=$_POST['tex'];

Данные отсутствуют? Значит, переменная $tex «остается» с пробелом.


Дальше получаем идентификатор отредактированного слоя:

$namb=$_POST['namb'];

а также информацию об основных настройках страницы:

$meta1=$_POST['m1s'];
$meta2=$_POST['m2s'];
$meta3=$_POST['m3s'];
$meta4=$_POST['m4s'];
$meta5=$_POST['m5s'];

Формируем строку для записи в текстовый файл страницы:


$fin=$meta1."\n".$meta2."\n".$meta3."\n".$meta4.
"\n".$meta5."\n".$tex;

Тут у программы начинается ветвление. Если запись ведется в файл ре-


кламы, то в качестве строки для записи используем переданную информацию
из поля HTML-кода, а файлом записи устанавливаем advert.txt:

if($namb==2)
{
$fin=$tex;
$adr='set/advert.txt';
}

Если необходимо поменять информацию в нижнем блоке, то действуем


аналогично, только файлом записи объявляем footer.txt:

if($namb==3)
{
$fin=$tex;
$adr='set/footer.txt';
}

Последний этап — непосредственная запись в файл:

if(file_put_contents($adr, $fin))
echo '1';
else
echo '2';

При успешной записи программа возвращает число 1 и в редакторе на


кнопке отправки данных выводится сообщение «ЗАПИСАНО». В случае не-
удачи возвращается число 2 и появляется сообщение «НЕ ЗАПИСАНО».
161
15.9. Перемещение панели
Главная панель со всеми вкладками и окном HTML-редактора свободно
перемещается по вертикали. Такая функциональная возможность делает про-
цесс редактирования очень удобным — всегда можно расположить панель ря-
дом с какой-либо частью контента (рис. 15.9.1).
Кликните на кнопке «Старт». Теперь нажмите, не отпуская, левую кла-
вишу мыши на свободном участке панели и перемещайте ее вверх-вниз,
насколько понадобится. Отпустите клавишу — панель зафиксируется в новом
месте. Чтобы полностью заблокировать панель, кликните на кнопке «Стоп».
Наличие кнопок «Старт» и «Стоп» надо пояснить отдельно. В принципе,
можно было обойтись без них — ведь панель все равно останавливается после
отпускания левой клавиши мыши. Но в этом случае мы можем столкнуться с
одним неприятным явлением. Допустим, вы открыли вкладку HTML-редактора
и решили удалить или скопировать часть кода длиной в несколько строк. Тут
вы обнаружите, что больше одной строки выделить невозможно — как только
вы переводите курсор строкой выше или ниже, панель синхронно двигается
вверх или вниз. На этот случай и предусмотрена кнопка «Стоп», которая пол-
ностью фиксирует панель, давая вам возможность выделять в текстовом поле
любое количество строк. Понятно, что отменяет фиксацию кнопка «Старт».
Модуль перемещения главной панели расположен в самом начале файла
edit.js. Но поскольку роль модуля второстепенна, мы рассматриваем эту часть
кода в последнюю очередь.
Для выполнения необходимых манипуляций зарегистрируем сначала два
обработчика кликов на кнопках «Старт» и «Стоп»:
addEventListener("load", function()
{
document.getElementById("start").
addEventListener("click", function()
{
document.getElementById("bpan").
addEventListener("mousedown", coor);
});
document.getElementById("stop").
addEventListener("click", function()
{
document.getElementById("bpan").
removeEventListener("mousedown", coor);
});
});

Как вы видите, после щелчка на кнопке «Старт» регистрируется обработ-


чик события нажатия указателя мыши на панели:
document.getElementById("bpan").
addEventListener("mousedown", coor);

162
Рис. 15.9.1
Перемещение главной панели
Теперь панель можно перемещать. Нажатие кнопки «Стоп» приводит к
удалению упомянутого обработчика:
document.getElementById("bpan").
removeEventListener("mousedown", coor);

Это означает полное закрепление панели на выбранном месте. На новые


нажатия кнопки мыши панель не станет реагировать до тех пор, пока вы вновь
не кликните «Старт».
Так же мы регистрируем еще один обработчик — отпускания кнопки
мыши в любой точке окна браузера:
addEventListener("mouseup", stco);

Дальше будем разбирать процессы, считая, что мы кликнули на кнопке


«Старт».
Введем переменную, предназначенную для хранения значения разности
между точкой клика на панели и вертикальной координатой верхнего края па-
нели (тем самым мы определяем, на каком расстоянии от указателя все время
должен находиться верхний край панели в процессе движения):
let dv=0;

Этот параметр необходим, чтобы при перемещении панели она двигалась


равномерно, а указатель мыши все время находился в одной и той же точке панели.
Напишем функцию coor, которая запускается после нажатия кнопки мы-
ши на панели:
function coor(ev)
{
...
}

163
Первая операция — определение вертикальной координаты указателя:

let v=ev.pageY;

Следом узнаем координаты верхнего левого угла панели:

let sv=document.getElementById("bpan").offsetTop;

Вычисляем разницу (рис. 15.9.2):

dv=v-sv;

Рис. 15.9.2
Вычисление начального смещения панели относительно верхней границы окна браузера
и регистрируем обработчик для события перемещения мыши по странице:

addEventListener("mousemove", neco);

Как только мы начинаем перемещать панель, запускается функция neco:

function neco(ev)
{
...
}

В ней сначала вычисляется новая вертикальная координата указателя:

let nv=ev.pageY;

потом из значения новой координаты nv вычитается значение переменной dv с


уже вычисленным начальным смещением:

let fv=nv-dv;

тем самым мы получаем новое значение координаты, в которой должен ока-


заться верхний край панели. Перемещаем панель в эту точку:
164
document.getElementById("bpan").style.top=fv+"px";

Повторные вычисления текущих координат и смещение панели в новое


положение продолжаются до тех пор, пока мы перемещаем мышь и пока нажа-
та ее левая кнопка. Как только мы отпускаем кнопку, «срабатывает» функция, в
которой всего одна инструкция — удалить обработчик события перемещения
мыши:
function stco()
{
removeEventListener("mousemove", neco);
}
Движение панели прекратится, и она займет новое положение, то, кото-
рое наиболее удобно администратору при редактировании контента. Если те-
перь вновь нажать указатель мыши над панелью, она вновь будет готова к пе-
ремещению. И так до полного отключения кнопкой «Стоп».
Мы рассмотрели вспомогательные компоненты редактора. Теперь можно
приступать к детальному разбору методов изменения содержимого страниц. Глав-
ной темой следующей главы будут методы и функции, используемые для измене-
ния или создания контента.

165
16. РЕДАКТОР КОНТЕНТА
В этой главе мы рассмотрим самые главные функции — те, которые поз-
воляют создавать и редактировать контент. Вы узнаете, как:
— записываются основные данные страницы;
— происходит включение режима редактирования;
— выполняется копирование текста;
— удаляются форматирование и ссылки;
— выполняется простое редактирование текста;
— меняется цвет букв или фона текста;
— добавляется разметка маркированного списка;
— вставляются линии, фреймы, рисунки, таблицы, символы;
— создаются ссылки;
— формируются заголовки;
— выравнивается текст в абзацах;
— текст оформляется разными шрифтами.
Но автор, прежде чем приступить к делу, советует во время чтения дан-
ной главы держать открытым на компьютере файл edit.js. А еще лучше допол-
нительно запустить локальный хостинг и не только в теории, но и на практике
наблюдать результаты работы тех или иных функций, написанных на
JavaScript. Так вы достигнете максимально ясного и объемного восприятия ма-
териала.

16.1. Основные данные страницы


Начнем с повторения пройденного. Автор хочет напомнить, что мы гово-
рили об основных настройках страниц. Как вы уже знаете, у нас есть 5 полей с
данными:
— рейтинг;
— видимость (на вкладке этот пункт записан так: «Показывать»);
— название;
— описание;
— ключевые слова.
После заполнения полей нужно закрыть вкладку настроек. Дальше с эти-
ми полями ничего не происходит до момента записи результатов редактирова-
ния. После нажатия кнопки «Записать» значения из вкладки заносятся в скры-
тые поля формы и отправляются серверной программе rec.php:
document.getElementById("m1s").value=
document.getElementById("meta1").value;
document.getElementById("m2s").value=
document.getElementById("meta2").value;
document.getElementById("m3s").value=
document.getElementById("meta3").value;
document.getElementById("m4s").value=

166
document.getElementById("meta4").value;
document.getElementById("m5s").value=
document.getElementById("meta5").value;

16.2. Включение режима редактирования


Этот раздел также служит напоминанием об уже изученном материале.
Как вы помните, включение редактирования происходит после выбора во
фрейме нужного слоя при нажатии кнопки «Выбрать»:
if(sr==1)
{
...
ed=frames[0].document.getElementById("content");
...
ed.setAttribute("contenteditable", true);
...
}
if(sr==2)
{
...
ed=frames[0].document.getElementById("advert");
...
ed.setAttribute("contenteditable", true);
...
}
if(sr==3)
{
...
ed=frames[0].document.getElementById("footer");
...
ed.setAttribute("contenteditable", true);
...
}

Добавляем тому или иному разделу страницы атрибут contenteditable —


и тем самым включаем возможность редактирования этого раздела.

16.3. Копирование текста


Начнем с вопроса: что произойдет, если в редакторе попытаться создать
элемент, забыв вставить курсор в редактируемый слой? Получится, что послед-
ний фрагмент из текущего документа, имевший фокус перед нажатием кнопки
«Вставить», продолжает находиться в фокусе. Сюда и будет добавлена таблица
или рисунок, даже если вы уже успели переключить редактируемый слой на
другой. Чтобы не происходили такие казусы, после нажатия кнопки «Вставить»
необходимо устанавливать фокус на текущей области редактирования.
Может появиться вопрос: сейчас мы разбираем копирование текста и ни-
каких элементов не создаем, причем же тут фокусировка? Дело в том, что ее
отсутствие в данной функции приводит к тому, что можно скопировать любой
фрагмент текста из любой части страницы. Вводя фокусировку, мы тем самым
ограничиваем область копирования редактируемой областью.

167
Теперь к делу. Регистрируем анонимную функцию в качестве обработчи-
ка события щелчка на кнопке копирования:

document.getElementById("cop").
addEventListener("click", function()
{
...
});

Первым делом устанавливаем фокус на поле редактирования:

ed.focus();

Следующая операция — проверка выделения текста. Если оно отсутству-


ет, выводим предупреждение в диалоговом окне (рис. 16.3.1):

if(sel=="")
alert("Вы не выделили текст!");

Если текст выделен, копируем его, используя свойство clipboard интерфейса


writeText:

else
navigator.clipboard.writeText(sel);

Напоминание: будет скопирован только текст без находящихся в нем


элементов и тегов форматирования.

Рис. 16.3.1
Если текст не был выделен, появляется диалоговое окно с предупреждением

16.4. Удаление форматирования и ссылок


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

document.getElementById("forma").
addEventListener("click", nofr);
document.getElementById("delin").
addEventListener("click", nofr);
168
Устанавливаем фокус на поле редактирования:

ed.focus();

Проверяем выделение текста. Если оно отсутствует, выводим предупре-


ждение:
if(sel=="")
alert("Вы не выделили текст!");

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


та. Для этого указываем, что будет обрабатываться та часть, которую охваты-
вает выделение:

let rng=sel.getRangeAt(0);

Дальше создаем новый текстовый узел, в который помещаем только тек-


стовое содержимое выделения:

let cr=document.createTextNode(sel.toString());

Удаляем выделенный фрагмент:

sel.deleteFromDocument();

и вставляем на это место чистый текст:

rng.insertNode(cr);

В самом конце записываем новые данные в массив промежуточных эта-


пов редактирования:

inter();

16.5. Простое редактирование текста


Чтобы задействовать кнопки простого редактирования, сначала создадим
два массива:
— с id кнопок

let mid=
["bol", "ital", "under", "strik", "sup", "sub"];

— с перечнем элементов, которые создают эти кнопки

let mco=["b", "i", "u", "s", "sup", "sub"];

169
Обратите внимание, что в первом массиве порядковый номер кнопки с
id=«bol» соответствует порядковому номеру создаваемого этой кнопкой эле-
мента b во втором массиве. И так для всех остальных кнопок.
Поскольку кнопок много, для сокращения кода станем регистрировать
обработчики в цикле. Первым делом создадим массив кнопок, ориентируясь на
то, что все они (и только они) принадлежат к классу but:

let m=document.querySelectorAll(".but");

а затем зарегистрируем для всех один и тот же обработчик:

for(let i=0; i<m.length; i++)


document.getElementById(mid[i]).
addEventListener("click", emb.bind(null, mco[i]));

Как видите, в функцию emb передается в качестве аргумента имя тега из


массива mco.
Функция emb, которая вставляет теги в текст:

function emb(v)
{
ed.focus();

if(sel=="")
alert("Вы не выделили текст!");
else
{
let rng=sel.getRangeAt(0);
let elm=document.createElement(v);
rng.surroundContents(elm);
inter();
}
}

Здесь четыре знакомых нам фрагмента кода:


— установка фокуса в редактируемую область;
— проверка выделения;
— создание на основе выделения диапазона для обработки;
— запись данных в массив промежуточных этапов редактирования.
После указания диапазона создаем в документе методом createElement
новый парный тег

let elm=document.createElement(v);

и обрамляем этим тегом выделенный текст (методом surroundContents):

rng.surroundContents(elm);

170
Как видите, термин «простое редактирование» использован автором не
случайно. Действительно, такие действия элементарны: выделил текст, нажал
кнопку и получил искомый результат.

16.6. Изменение цвета букв или фона текста


Описание этого процесса начнем с функции изменения цвета:

function clr(b)
{
...
}

Что за аргумент получает функция, станет ясно по ходу изложения.


Функция имеет уже ставшие привычными инструкции:
— установка фокуса в редактируемую область;
— проверка выделения;
— создание диапазона для обработки;
— запись данных в массив промежуточных этапов редактирования;
— добавление парного тега к выделению.

Чтобы раскрасить текст, мы используем парный тег span:

let elm=document.createElement("span");

После его создания устанавливаем стиль тега, где код цвета определяется
значением из поля с идентификатором ccc:

elm.style=b+"color: "+
document.getElementById("ccc").value;

Если нужно выделить цветом буквы, то аргумент b имеет пустое значе-


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

document.getElementById("col").
addEventListener("click", clr.bind(null, ""));

При нажатии кнопки выбора цвета фона в функцию в качестве аргумента


передается строка background-:

document.getElementById("backgr").
addEventListener("click",
clr.bind(null, "background-"));

171
16.7. Добавление разметки маркированного списка
Важное замечание к этой функции: чтобы создать список, используйте
перевод на следующую строку, а не на следующий абзац (то есть нажимайте
комбинацию клавиш «Shift» + «Enter», а не просто «Enter»).
Регистрируем обработчик:

document.getElementById("ulli").
addEventListener("click", function()
{
...
});

Устанавливаем фокус.
Проверяем выделение. Если оно есть, присваиваем выделенный фрагмент
новой переменной:

let ulli=sel;

Разбиваем выделенный текст на отдельные строки, используя в качестве


разделителя символ перевода строки \n:

let masulli=ulli.toString().split("\n");

Добавляем к строкам парный тег li:

let sum="";
for(let i=0; i<masulli.length; i++)
sum+="<li>"+masulli[i]+"</li>";

Создаем элемент списка ul:


let rng=sel.getRangeAt(0);
let elm=document.createElement("ul");

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

ed.getElementsByTagName("UL").length

и одновременно создаем id для нового списка:

let len="sul"+ed.getElementsByTagName("UL").length;

Идентификатор состоит из символов sul и порядкового номера списка,


полученных на предыдущем шаге:

elm.id=len;

172
Если созданный элемент — первый, то у списка id будет sul0, если эле-
мент второй, то id списка будет sul1 и т. д.
Вставляем тег ul в окно редактирования:

rng.surroundContents(elm);

и помещаем в его «внутренности» элементы списка:

frames[0].document.getElementById(len).innerHTML=sum;

Последний этап — запись данных в массив промежуточных этапов редакти-


рования:

inter();

16.8. Вставка линии


С этого раздела начинается описание добавления элементов, которые
нуждаются в предварительных настройках. Поэтому сначала расскажем, какая
схема действий реализована в их функциях. Порядок операций следующий:
— получаем данные из полей формы соответствующей вкладки;
— проверяем, все ли критически важные параметры введены админи-
стратором;
— компонуем данные, полученные из формы.
Дальше возможны два варианта действий:
— если настройки элемента уникальны, создаем его в текущей функции,
помещаем элемент в документ и добавляем ему скомпонованные данные;
— если настройки элемента универсальны, передаем все его данные от-
дельной функции, которая «обслуживает» создание элементов, происходящее
по одному и тому же алгоритму.
Из этого вытекает, что запись данных в массив промежуточных этапов
редактирования происходит либо в текущей функции, либо во внешней.
А теперь перейдем к созданию линий.
Регистрируем обработчик:

document.getElementById("buthr").
addEventListener("click", function()
{
...
});

Первый шаг — получение данных из панели настройки:

let hr1_v=document.getElementById("hr1").value;
let hr2_v=document.getElementById("hr2").value;
let hr3_v=document.getElementById("hr3c").value;

173
На случай, если администратор заполнил не все поля, создаем две пустые
переменные:

let hr1="";
let hr2="";

Напомню, что на вкладке выбора цвета по умолчанию всегда установлен


черный, поэтому данное поле не бывает пустым.
Если толщина линии введена, записываем в переменную hr1 фрагмент
настройки стиля:
if(hr1_v!="")
hr1="height: "+hr1_v+"; ";

Если нет, то устанавливаем линии толщину 1 px:

else
hr1="height: 1px; ";

Проверяем, введено ли значение ширины линии. Если «да», то записыва-


ем во вторую переменную свойство width:

if(hr2_v!="")
hr2="width: "+hr2_v+"; ";

Компонуем строку с настройками стиля

let shr=hr1+hr2+'background: '+hr3_v+'; border: 0;';

Напомню, что по умолчанию все браузеры создают линиям тени. Этот


фрагмент кода убирает тень:

border: 0;

Теперь передаем все параметры функции place, которая вставляет в окно


редактирования некоторые элементы, в том числе линии:

place("hr", "", shr, "", "hr_d");

Здесь первый аргумент — тег вставляемого элемента, третий — настрой-


ки стилей, пятый — идентификатор закрываемой вкладки.
Как уже было сказано, функция place создает разные элементы, в том
числе такие, для которых требуется 4 (фреймы) или 5 (рисунки) аргументов.
Создание линии обходится только тремя, поэтому второй и четвертый аргумен-
ты пустые.
Функция place:

function place(x, y, z, w, q)
{

174
ed.focus();

let rng=sel.getRangeAt(0);
let elm=document.createElement(x);

if(y!="")
elm.src=y;
elm.style=z;

if(w!="")
{
elm.alt=w;
elm.title=w;
}
rng.surroundContents(elm);

inter();
clo(q);
}

Здесь второй аргумент — адрес фрейма или рисунка, четвертый — текст


альтернативной подписи к рисунку.
Получив данные из соответствующей функции, создаем необходимый
элемент:
let rng=sel.getRangeAt(0);
let elm=document.createElement(x);

Добавляем ему стили:


elm.style=z;

Если создается рисунок или фрейм, добавляем данному элементу адрес,


переданный вторым аргументом:
if(y!="")
elm.src=y;

Если создается рисунок, добавляем ему альтернативный текст и подсказ-


ку, переданные четвертым аргументом:
if(w!="")
{
elm.alt=w;
elm.title=w;
}

Помещаем созданный элемент в редактируемую область:


rng.surroundContents(elm);

записываем промежуточный результат:


inter();

175
и запускаем программу закрытия вкладки с настройками:
clo(q);

16.9. Вставка фрейма


Регистрируем обработчик:
document.getElementById("butifr").
addEventListener("click", function()
{
...
});

Получаем данные:
let ifr1_v=document.getElementById("ifr1").value;
...
let ifr7_v=document.getElementById("ifr7").value;

Проверяем, введен ли адрес страницы, загружаемой во фрейм, и, если ад-


министратор забыл это сделать, выводим предупреждение:
if(ifr1_v=="")
alert("Вы не указали адрес загружаемой страницы!");

Когда адрес введен, наступает следующий этап. Объявляем 5 пустых пе-


ременных:
let ifr2="";
let ifr3="";
let ifr4="";
let ifr5="";
let ifr7="";

и на их основе формируем значения свойств стиля:


if(ifr2_v!="")
ifr2="width: "+ifr2_v+"; ";

if(ifr3_v!="")
ifr3="height: "+ifr3_v+"; ";
if(ifr4_v!="no")
ifr4="float: "+ifr4_v+"; ";

if(ifr5_v!="")
ifr5="border: "+ifr5_v+" solid "+ifr6_v+";";
else
ifr5="border: 1px solid "+ifr6_v+";";

if(ifr7_v!="")
ifr7=" margin: "+ifr7_v+";";

Затем компонуем эти данные в одну строку:

176
let sifr=ifr2+ifr3+ifr4+ifr5+ifr7;

и запускаем функцию place с набором из четырех заполненных аргументов и


одного пустого:
place("iframe", ifr1_v, sifr, "", "ifr_d");

16.10. Вставка рисунка


Уже привычная регистрация обработчика:
document.getElementById("butima").
addEventListener("click", function()
{
...
});

Получаем данные из полей вкладки:


let ima1_v=document.getElementById("ima1").value;
...
let ima8_v=document.getElementById("ima8c").value;

Выясняем, введен ли хотя бы один адрес картинки (внешней или внут-


ренней). В случае забывчивости администратора делаем ему предупреждение:
if(ima1_v=="" && ima2_v=="")
alert("Вы не указали адрес рисунка!");

Проверяем, создана ли альтернативная подпись к рисунку (согласно тре-


бованиям Консорциума Всемирной паутины атрибут alt является обязательным
в теге изображения). В «отрицательном» случае опять выносим предупрежде-
ние:
if(ima3_v.length<2)
alert("Вы не ввели подпись к рисунку!");

Когда введен адрес внешнего рисунка, загружен будет он. Если введены
сразу два адреса — внешнего и внутреннего фото, — то приоритет отдается
внешнему. Если выбрана только внутренняя картинка, то в этом случае будет
загружена она:
if(ima1_v!="")
ima=ima1_v;
else
ima="../img/"+ima2_v;

Процедуры формирования переменных для записи свойств стилей уже


знакомы вам по двум предыдущим разделам. Вы можете подробно изучить их,
открыв файл edit.js.
Сформировав промежуточные переменные, сводим их в одну:
let sima=ima4+ima5+'width: '+ima6+'; border: '+
177
ima7+' solid '+ima8_v;

Обратите внимание: при наведении указателя мыши на рисунок курсор


будет принимать вид «руки», чтобы посетитель догадывался — на фото можно
кликнуть и посмотреть его в увеличенном формате. Такое поведение курсора
устанавливается в файле таблицы стилей сайта:
#content img {cursor: pointer;}

Как видно из приведенного фрагмента, это правило распространяется


только на изображения в слое content страницы. В рекламном и нижнем блоках
эффекта «руки» на рисунках не будет.
Последний этап — обращение к функции place:
place("img", ima, sima, ima3_v, "img_d");

На этот раз мы задействовали и четвертый аргумент функции, передав


через него альтернативную подпись к изображению.

16.11. Вставка таблицы


Создание таблицы выполняет одна из наиболее «массивных» функций —
из-за обилия настроек в ней 78 строк кода. Естественно, для экономии места ав-
тор не станет приводить ее целиком, а покажет только основные части. Полно-
стью код этой функции можно посмотреть в файле edit.js.
Обработчик клика на кнопке butab:
document.getElementById("butab").
addEventListener("click", function()
{
...
});

Данные настроек:
let tab1_v=document.getElementById("tab1").value;
...
let tab12_v=document.getElementById("tab12").value;

Обязательные параметры для таблицы — количество строк и столбцов.


Проверяем их наличие. Если администратор забыл указать эти числа или одно
из них, открываем диалоговое окно с предупреждением:
if(tab2_v=="" || tab3_v=="")
alert("Вы не указали количество
строк или столбцов!");

На основе полученных значений из вкладки формируем набор свойств


таблицы и объединяем их в одну строку:
let tbf=tab1+'border-collapse: collapse;
background: '+tab11_v+'; '+tab5+tab7+
'font-family: '+tab6_v+'; color: '+
178
tab8_v+';'+tab12;

Обратите внимание, мы свели границы ячеек:


border-collapse: collapse;

чтобы таблица имела единую внешнюю рамку.


Теперь надо сформировать сами ячейки. Делаем это в циклах:
let sum="";
for(let i=0; i<tab2_v; i++)
{
sum+='<tr>';
for(let t=0; t<tab3_v; t++)
sum+='<td style="border: '+tab9+
' solid '+tab10_v+';'+tab4+'">&nbsp;</td>';
sum+='</tr>';
}

Внешний цикл совершает количество проходов, равное количеству строк,


а внутренний — количеству столбцов. Переменная tab9 задает толщину рамки,
а tab10_v — цвет рамки для каждой ячейки. Переменная tab4 определяет внут-
ренний зазор между содержимым ячейки и ее границами. Пробел &nbsp; до-
бавлен в каждую ячейку специально. Если этого не сделать, таблица может
сжаться так, что в ячейки невозможно будет вставить курсор.
Теперь создадим таблицу и добавим ей стили:
let rng=sel.getRangeAt(0);
let elm=document.createElement("table");
elm.style=tbf;

Следующий шаг — присвоение таблице id (то же самое мы делали при


создании маркированного списка):
let len="tbv"+ed.getElementsByTagName("TABLE").length;
elm.id=len;

Помещаем таблицу в редактируемый слой:


rng.surroundContents(elm);

и вставляем в нее ячейки:


frames[0].document.getElementById(len).innerHTML=sum;

Завершается функция двумя инструкциями,


inter();
clo("tabl_d");

которые выполняют:
— запись данных в массив промежуточных этапов редактирования;
— закрытие вкладки с настройками таблицы.
179
16.12. Вставка символов
Символов очень много — 50 штук. Регистрировать для каждого из них
обработчик отдельной строкой — хлопотное и нерациональное занятие. Поэто-
му все события нажатия кнопок символов будем регистрировать в цикле.
Создадим массив кнопок, ориентируясь на то, что все они (и только они)
принадлежат к классу sym:
let q=document.querySelectorAll(".sym");

Зарегистрируем для всех кнопок один и тот же обработчик:


for(let i=0; i<q.length; i++)
{
let idc=q[i].id;
let smb=q[i].value;
document.getElementById(idc).
addEventListener("click", sign.bind(null, smb));
}

Здесь мы последовательно выясняем идентификатор очередной кнопки:


let idc=q[i].id;

получаем ее символ:
let smb=q[i].value;

и регистрируем обработчик для событий нажатия этой кнопки:


document.getElementById(idc).
addEventListener("click", sign.bind(null, smb));

Функция sign(s) работает очень просто. Создаем новый текстовый узел,


который содержит переданный в аргументе символ:
let elm=document.createTextNode(s);

и вставляем его в позицию курсора в окне редактирования:


let rng=sel.getRangeAt(0);
rng.insertNode(elm);

В конце выполняем уже привычные завершающие операции:


inter();
clo("copyr_d");

16.13. Добавление ссылки


В этом модуле сразу после регистрации обработчика
document.getElementById("butlink").

180
addEventListener("click", function()
{
...
});
выполняется проверка выделения с типичным предупреждением в случае его
отсутствия:
if(sel=="")
alert("Вы не выделили текст!");

Дальше все идет привычным образом. Сначала получаем данные:


let link1_v=document.getElementById("link1").value;
...
let link7_v=document.getElementById("link7").value;

Затем формируем стиль, а заодно определяемся, будет ли ссылка с подчер-


киванием:
if(link5_v=="no")
link5='text-decoration: none; color: '+link6_v+';';
else
link5='color: '+link6_v+';';

Следом выясняем, какой должен быть адрес ссылки — на внешний ре-


сурс, на внутреннюю страницу или на файл:
if(link2_v!="")
ares=link1_v+link2_v;
else if(link7_v==999)
ares="index.php?a="+link4_v;
else
ares="file/"+link7_v;

Создаем ссылку в документе:


let rng=sel.getRangeAt(0);
let lin=document.createElement("a");

Добавляем адрес и стили:


lin.href=ares;
lin.style=link5;

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


ссылка (если добавляется ссылка на файл — то в новой):
if((link2_v!="" && link3_v=="yes") || link7_v!=999)
lin.target="_blank";

«Оборачиваем» выделенный текст в ссылку:


rng.surroundContents(lin);

и завершаем процесс:

181
inter();
clo("link_d");

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

16.14. Добавление заголовка


С этого раздела начинается описание трех обработчиков, каждый из ко-
торых на финише запускает одну и ту же функцию — wrap:
function wrap(r, u, j)
{
ed.focus();

if(sel=="")
alert("Вы не выделили текст!");
else
{
let rng=sel.getRangeAt(0);
let elm=document.createElement(r);
elm.style=u;
rng.surroundContents(elm);

inter();
clo(j);
}
}

Функция получает три аргумента:


— название создаваемого элемента;
— настройки стиля элемента;
— идентификатор закрываемой вкладки.
Инструкции функции уже так хорошо нам знакомы, что разбирать их не
имеет смысла. Поэтому перейдем к добавлению заголовков.
Регистрируем обработчик:
document.getElementById("buth16").
addEventListener("click", function()
{
...
});

Получаем данные настроек:


let h161_v=document.getElementById("h161").value;
...
let h164_v=document.getElementById("h164c").value;

Формируем строку стилей:


let h163="";

if(h163_v!="")
182
h163=" font-size: "+h163_v+";";

let h16res='font-family: '+h162_v+';'+h163+


' color: '+h164_v+';';
И отправляем информацию функции wrap:
wrap(h161_v, h16res, "h16_d");

которая добавляет выделенному тексту один из тегов h1–h6 и свойства стиля.

16.15. Выравнивание абзаца


Как всегда, пишем оболочку для обработчика:

document.getElementById("butequa").
addEventListener("click", function()
{
...
});

На основе данных из вкладки:

let equa1_v=document.getElementById("equa1").value;
...
let equa5_v=document.getElementById("equa5").value;

формируем строку свойств абзаца:

let equa3="";
let equa5="";
if(equa3_v!="")
equa3=" font-size: "+equa3_v+";";
if(equa5_v!="")
equa5=" text-indent: "+equa5_v+";";
let equares='text-align: '+equa1_v+
'; font-family: '+equa2_v+';'+
equa3+' color: '+equa4_v+';'+equa5;

и отправляем необходимые параметры функции wrap:

wrap("p", equares, "equa_d");

16.16. Настройка шрифта


В этом модуле тоже нет ничего особенного — только неоднократно разо-
бранные инструкции. Так как код модуля довольно короткий, приведем его це-
ликом. Думаю, читатель без проблем поймет, что и как устроено в этой функ-
ции:
document.getElementById("butfont").
addEventListener("click", function()
183
{
let font1_v=
document.getElementById("font1").value;
let font2_v=
document.getElementById("font2").value;
let font3_v=
document.getElementById("font3c").value;
let font2="";

if(font2_v!="")
font2=" font-size: "+font2_v+";";
let fontres='font-family: '+font1_v+';'+font2+
' color: '+font3_v+';';
wrap("span", fontres, "font_d");
});

Мы подробно (точнее — почти подробно) разобрали устройство редакто-


ра. На этом описание нашей CMS закончено. Настало время подвести итоги.

184
17. ЗАКЛЮЧЕНИЕ
Мы закончили описание системы управления и разбор ее функций. По сути,
данная книга получилась в некотором роде документацией по изученному про-
граммному обеспечению. Но, конечно, история с CMS на этом не заканчивается.
Ведь это был только первый шаг.
Что же дальше? Варианты профессионального роста могут быть самыми
разными.
Можно, взяв за основу изученный материал, нарастить его функциональ-
ность, увеличить количество настраиваемых параметров и создаваемых страниц.
Можно перевести систему с хранения информации в текстовых файлах на
ее хранение в базе данных.
Можно значительно усложнить систему шифрования пароля и тем самым
повысить уровень безопасности CMS.
Можно, используя полученный опыт, вообще написать что-то новое, не-
обычное и нестандартное.
Путей дальнейшего развития много.
Но в любом случае автор желает вам удачи.

185
ОГЛАВЛЕНИЕ
1. Введение ................................................................................................ 3
1.1. О чем эта книга ................................................................................ 3
1.2. Особенности проекта ...................................................................... 4
1.3. Оформление сценариев ................................................................... 5
2. Прежде чем начать ............................................................................. 7
2.1. CMS .................................................................................................. 7
2.2. WYSIWYG ....................................................................................... 8
2.3. Атрибут contentEditable .................................................................. 8
2.4. Свойство designMode ...................................................................... 9
2.5. Метод getSelection ......................................................................... 10
2.6. Методы createElement и surroundContents ................................... 10
3. Среда разработки .............................................................................. 11
3.1. Выясняем разрядность ОС ........................................................... 12
3.2. Установка пакета Visual C++ ....................................................... 13
3.3. Установка сервера Apache 2.4 ...................................................... 15
3.4. Установка PHP 8............................................................................ 21
3.5. Установка редактора Notepad++ 8 ............................................... 24
3.6. Установка файлов zip-архива
на локальный хостинг .......................................................................... 28
3.7. Перенос проекта на
удаленный хостинг ............................................................................... 29
4. Тестирование программ .................................................................. 36
4.1. Тестирование сценариев в браузерах .......................................... 36
4.2. Тестирование сценариев в валидаторах ...................................... 37
4.3. Кодировка файлов ......................................................................... 40
5. Структура проекта ........................................................................... 41
5.1. Комплектующие системы ............................................................. 41
5.2. Сайт ................................................................................................ 42
5.3. CMS ................................................................................................ 42
6. Вход и главная страница CMS ....................................................... 45
6.1. Вход в систему .............................................................................. 45
6.2. Меню системы ............................................................................... 48

186
7. Замена пароля .................................................................................... 51
7.1. Страница замены пароля .............................................................. 51
7.2. Замена пароля ................................................................................ 52
8. Специальные настройки ................................................................. 57
8.1. Страница специальных настроек ................................................. 57
8.2. Добавление фото ........................................................................... 61
8.3. Просмотр фото............................................................................... 63
8.4. Удаление фото ............................................................................... 65
8.5. Запись e-mail администратора ..................................................... 66
9. Сайт ..................................................................................................... 69
9.1. Варианты компоновки страниц.................................................... 69
9.2. Файл index.php ............................................................................... 71
9.3. Таблица стилей .............................................................................. 77
9.4. Просмотр фото............................................................................... 80
9.5. Страница «Контакты» ................................................................... 83
9.6. Отправка сообщений..................................................................... 86
10. Конструктор сайта .......................................................................... 90
10.1. Страница конструктора .............................................................. 90
10.2. Блоки ............................................................................................ 91
10.3. Вкладки ........................................................................................ 93
10.4. Запись результата ........................................................................ 96
11. Добавление фото.............................................................................. 98
11.1. Страница добавления фото......................................................... 98
11.2. Добавление фото ......................................................................... 99
11.3. Просмотр фото........................................................................... 101
11.4. Удаление фото ........................................................................... 101
12. Добавление файлов ....................................................................... 103
12.1. Страница добавления файлов................................................... 103
12.2. Добавление файла ..................................................................... 105
12.3. Удаление файла ......................................................................... 106
13. Список страниц ............................................................................. 108
13.1. Страница со списком ................................................................ 108
13.2. Блок списка страниц ................................................................. 109
13.3. Удаление лишних страниц ....................................................... 111
13.4. Блок добавления новых страниц .............................................. 112
13.5. Добавление новой страницы .................................................... 113
187
14. Базовые компоненты редактора страниц ................................ 116
14.1. Компоновка редактора .............................................................. 116
14.2. WYSIWYG и текстовый режимы ............................................ 118
14.3. Главная панель .......................................................................... 120
14.4. Основные настройки страницы................................................ 124
14.5. Кнопки простого редактирования ........................................... 126
14.6. Блок настроек линий ................................................................. 127
14.7. Блок настроек таблиц ................................................................ 128
14.8. Блок настроек рисунков............................................................ 130
14.9. Блок настроек фреймов............................................................. 133
14.10. Блок выбора символов ............................................................ 134
14.11. Блок настроек ссылок ............................................................. 135
14.12. Блок настроек заголовков ....................................................... 138
14.13. Блок настроек выравнивания
текста в абзацах .................................................................................. 139
14.14. Блок настроек шрифтов .......................................................... 140
14.15. Блок настроек цвета текста .................................................... 141
14.16. Удаление форматирования и ссылок ..................................... 142
15. Вспомогательные функции ......................................................... 143
15.1. Регистрация «главного» обработчика ..................................... 143
15.2. Выбор редактируемого блока .................................................. 144
15.3. Взаимодействие текстового поля
и редактируемой области .................................................................. 148
15.4. Фиксация в памяти
выделенного фрагмента ..................................................................... 150
15.5. Открытие и закрытие
вкладок с настройками....................................................................... 150
15.6. Выбор рисунка ........................................................................... 152
15.7. Навигация по внесенным изменениям .................................... 154
15.8. Запись отредактированной страницы ...................................... 157
15.9. Перемещение панели ................................................................ 162
16. Редактор контента ........................................................................ 166
16.1. Основные данные страницы ..................................................... 166
16.2. Включение режима редактирования ....................................... 167
16.3. Копирование текста .................................................................. 167
16.4. Удаление форматирования и ссылок ....................................... 168
16.5. Простое редактирование текста ............................................... 169
16.6. Изменение цвета букв или фона текста................................... 171
188
16.7. Добавление разметки
маркированного списка ..................................................................... 172
16.8. Вставка линии ............................................................................ 173
16.9. Вставка фрейма ......................................................................... 176
16.10. Вставка рисунка....................................................................... 177
16.11. Вставка таблицы ...................................................................... 178
16.12. Вставка символов .................................................................... 180
16.13. Добавление ссылки ................................................................. 180
16.14. Добавление заголовка ............................................................. 182
16.15. Выравнивание абзаца .............................................................. 183
16.16. Настройка шрифта .................................................................. 183
17. Заключение .................................................................................... 185

189
Валерий Викторович ЯНЦЕВ
JAVASCRIPT И PHP. CONTENT MANAGEMENT SYSTEM
Учебное пособие

Зав. редакцией литературы


по информационным технологиям
и системам связи О. Е. Гайнутдинова
Ответственный редактор Н. С. Захарова
Подготовка макета Ю. А. Бачурина
Корректор Т. А. Кошелева
Выпускающий Е. С. Шумская
ЛР № 065466 от 21.10.97
Гигиенический сертификат 78.01.10.953.П.1028
от 14.04.2016 г., выдан ЦГСЭН в СПб
Издательство «ЛАНЬ»
[email protected]; www.lanbook.com
196105, СанктПетербург, пр. Юрия Гагарина, д.1, лит. А.
Тел.: (812) 3362509, 4129272.
Бесплатный звонок по России: 88007004071

Подписано в печать 18.08.22


Бумага офсетная. Гарнитура Школьная. Формат 70×100 1/16.
Печать офсетная/цифровая. Усл. п. л. 15,60. Тираж 30 экз.
Заказ № 116622.
Отпечатано в полном соответствии
с качеством предоставленного оригиналмакета
в АО «Т8 Издательские технологии»
109316, г. Москва, Волгоградский пр., д. 42, к. 5.

Вам также может понравиться