Практикум по разработке системы управления контентом (CMS)

Редактирование на странице и CSS

Показывать лекцию целиком

Редактирование HTML-элементов прямо на странице

Каждый фрагмент данных, извлекаемый нами из БД, заключается потом в HTML-тэг: текст мы помещаем внутри DIV, а картинку (путь к файлу изображения) внутри тэга IMG в атрибут SRC.

Для "прямого" редактирования того, что мы видим на сайте, нам надо решить последовательно четыре задачи:

1. На стороне Php назначить элементам однозначные идентификаторы (чтобы потом Javascript мог работать с ними на стороне клиента). Решаем сразу – через атрибут class: "_edit table id {field_name} {старый_класс}", то есть сохраняем класс, который был задуман дизайнером, отодвигая его в конец группы, а на первые четыре места ставим метку редактирования (_edit), наименование таблицы, номер id в таблице и наименование поля, которое будем править.

2. Дать пользователю возможность вызвать форму редактирования для любого элемента с данными. Для этого сделаем две вещи:

  • кнопочку со словом "Редактировать", которая будет всплывать (становиться видимой) при наведении мыши на редактируемый элемент,
  • и например, будем ещё открывать форму по щелчку мышкой на элементе с доп. клавишами: Ctrl + Alt + Click.
  • Это всё будем делать на стороне клиента (Javascript).

    3. По щелчку на кнопке "Редактировать" надо генерировать HTML-форму с единственным полем ввода; посылать "фоновый" запрос на сервер (что-то вроде XmlHttpRequest, в просторечии называемый ajax'ом), извлекать этим запросом содержание текущего поля и вставлять это содержание в форму.

  • Форма должна быть на переднем плане; остальной текст страницы затенён; должна быть возможность закрыть форму – для этого делаем крестик справа вверху.
  • 4. При отправке формы (submit) по щелчку на кнопке "Сохранить", в фоновом же режиме (ajax) посылать POST-запрос на сервер для сохранения данных; получать в ответ от сервера сохранённые данные и помещать их в редактируемый HTML-элемент (обновлять его).

  • POST-запрос на сервере должна принимать специальная функция (которой у нас пока нет); она должна уметь посылать разные ответы: сообщение об ошибке или об успехе операции, чтобы редактор видел, что происходит на сервере.
  • Ответ серверной функции, обрабатывающей POST-запрос, мы должны будем куда-то помещать на странице (чтобы пользователь видел этот ответ).
  • Это не всё. Нужны кнопки для создания новой страницы и удаления текущей страницы. И, как бы мы ни старались сэкономить за счёт "прямого редактирования полей", всё равно понадобится общая большая форма для редактирования всех полей страницы – потому что там будут скрытые от пользователя поля (например, поле для сортировки, определяющее очерёдность страниц в списках). Эти общие для всей страницы кнопки будем помещать на фиксированном месте вверху страницы – они будут всплывать при наведении мыши на страницу.

    Ещё нужен будет сигнал, передаваемый от Php к Javascript: этот сигнал будет сообщать о том, что страницу можно редактировать, и, следовательно, к элементам надо подрисовывать соответствующие всплывающие кнопочки. Потому что в обычном режиме, для неавторизованных посетителей, кнопочки всплывать не должны.

    Если сказать коротко, в результате у нас получится 4 файла (которые мы с этой версии 05 http://nichtig.ru/05/ должны будем теперь помещать в отдельные папки на сайте): размером 6 килобайт. Разницу как раз и составляет код CMS – системы редактирования сайта (без CMS код сайта в 5-6 раз меньше).

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

    CSS

    Мы упомянули в списке выше файл site.css как новый, но мы уже давно незаметно использовали его. Например, ссылки в меню (в предыдущей версии – index4-3.php) выровнены по центру – потому что в файле site.css в строке 40 (приблизительно) мы описали класс: .menu1 {text-align:center;}, а в коде php поместили список ссылок в элемент <div class="menu1">. А вся страница расположена в центре экрана, потому что мы именно так и описали её класс: div.page {width:900px; margin:0 auto;}.

    В новой же версии (05) для CSS работы прибавилось: скрывать и показывать ссылки редактирования, раскрасить эти ссылки поярче; показывать в центре страницы форму редактирования, затенив остальные элементы. Меню слева от текста страницы – с помощью свойства float:rigth;. В общем, много чего. Всё это занимает сейчас 3 килобайта (около 100 строк кода). Вы должны удивиться, как это мало (учитывая, что в этот код входит оформление визуального редактора HTML).

    В дальнейшем мы не будем упоминать CSS (если именно на нём не основана логика работы); просто смотрите в файл site.css внимательно: хорошее знание этой области очень помогает в создании сайта.

    Обратите внимание на стиль работы – как именно связываются javascript и CSS при динамическом изменении страницы: мы никогда не пишем "element.style.color='red'", но всегда javascript оперирует только готовыми CSS-классами:

    element.className = 'redColor'

    Хотя на самом деле и это не совсем так: CSS-классы HTML-элементам добавляет специальная функция cc ("change class"), которая не трогает существующий набор "слов" – классов элемента, а добавляет к нему что-то новое (или удаляет одно из слов).

    Иногда мы используем CSS-3, не стремясь к кроссбраузерности, – это учебные страницы, и не очень принципиально, если в каком-нибудь Интернет Эксплорере-9 вы не увидите у какого-нибудь элемента прозрачную гладкую тень (тень будет квадратной и чёрной, или её вовсе не будет). Но вы должны понимать, что на реальном сайте обеспечить правильную работу всех планируемых "украшений" будет несколько сложнее - это особенности работы html-верстальщика (мы не вдаёмся здесь в подробности этой работы).

    Редактирование WYSIWYG (копируем текст из Word'а)

    Обычно при словах "визуальный редактор" мы сразу вспоминаем многомегабайтные чудовища, название которых как бы в насмешку начинается с "tiny...". Но мы-то пытаемся сделать настоящее tiny. Поэтому начнём с простого: если человек скопирует текст из Ворда и вставит в поле textarea, то при сохранении всё форматирование пропадёт. Ладно ещё пропадёт жирность, но ведь не будет ни абзацев, ни таблиц, ни списков... сплошной массив текста. Самое большее, что мы можем сделать в этом случае для сохранения структуры, – при выводе на сайте отображать разрывы строк с помощью nl2br.

    Пара килобайт javascript может довольно существенно улучшить положение. Во-первых, добавим в форму редактирования элемент div с атрибутом contenteditable и сделаем его основным для редактирования. При любых изменениях текста в нём, будем передавать его innerHTML в скрытое по умолчанию поле textarea.

    Во-вторых, добавим сразу кнопку "Код HTML", которая по желанию пользователя будет отображать скрытое поле (чтоб можно было всё контролировать).

    В-третьих, у нас проблема на сервере: не будем же мы, в самом деле, разрешать какие угодно тэги – пусть не намеренная диверсия, но пользователи склонны по глупости затаскивать на сайт чужой вредоносный код (в том числе javascript). Либо форматирование может измениться непредсказуемо, мало ли всякого мусора скрыто в невидимых тэгах!

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

    В-четвёртых, у нас проблема с настройками полей: в одном поле (например, html1) нам нужен html, в другом же (например, title) он категорически опасен. В идеале надо составлять список всех полей всех таблиц и добавлять в конфигурацию описания (тем более что там не только об html-е вопросы будут). Именно так и делаем: добавляем в файл config.php раздел 'fields' и записываем туда, в частности, что поле html1 в таблице mypages имеет тип "21", что перед выводом в браузер заставит Php а) вывести поле (не пропускать) и б) разрешить редактирование (и отображение) html-кода.

    В функции validate_html записано, в частности, правило: если тип поля "больше нуля", пропускать html (пропускать только то, что мы перечислили явно в файле config.php).

    Ещё мы по особому обрабатываем при выводе на сайте поля, начинающиеся с букв "img" – создаём элемент img и присваиваем ему src равный значению поля (приблизительно – так как надо ещё путь к папке с картинками указать).

    Теперь мы можем копировать текст из других программ (rtf, html) и вставлять в поле редактирования. В основном форматирование не испортится (сохранятся таблицы, разделение на абзацы, заголовки и подзаголовки...). Но часть непосредственного оформления будет уничтожена – например, переданного через атрибут style или через устаревшие тэги font. Это часть идеологии нашей CMS: всё оформление мы стараемся делать через внешний файл CSS, описывая в нём классы, – тогда страницы сайта будут более-менее единообразными, в одном стиле.

    Для описанных изменений создадим "субверсию" 05.1 http://nichtig.ru/05.1/ - она отличается от версии 05 только размером файла с именем admin и паролем admin.

    Редактирование WYSIWYG (форматируем HTML кнопочками)

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

    Первое, что надо сделать, – описать класс для таблицы с рамочками в site.css. Назовём этот класс "border". Ну, и класс для заголовка таблицы тоже понадобится (tHead). Затем надо преодолеть соблазн оформить ВСЕ таблицы на сайте по умолчанию классом border (может, оно на данном этапе и правильно, но всё равно ведь понадобятся разное оформление для разных типов таблиц).

    Второе – как-то указать, отметить таблицу в нашем contenteditable, чтобы при щелчке по кнопке "Рамки таблицы" рамки появлялись именно у конкретной таблицы. Надо как-то "лоцировать" место, где мы находимся в редактируемом тексте. По событиям мыши и клавиатуры (то есть при любой активности пользователя, связанной с редактируемым текстом) будем запускать специально написанную нами для этого javascript функцию Editor.locate и сохранять значение текущего html-элемента в какой-нибудь переменной. А при щелчке по кнопкам форматирования уже использовать значение этой переменной (а не что попало).

    Дальше – ясно: ищем вверх по DOM-дереву (если не знаете, что такое DOM, почитайте спецификации W3C) элемент "Таблица" (но не выше нашего редактируемого элемента "contenteditable" – с помощью функции findParent) и назначаем ему класс border. Точнее, делаем кнопку переключателем: если класс уже назначен таблице, он удаляется. Благо, такая функция переключения классов CSS уже у нас в запасе имеется (называется tc – от "ToggleClass"):

    function tc(el, cl1, cl2) {
    	cl2 = cl2 || null;
    	if (hc(el.className, cl1)) cc(el, cl2, cl1)
    	else cc(el, cl1, cl2)
    }
    

    Дальше по аналогии можно добавить несколько кнопок типа h3, ol, ul...

    Javascript в нашей CMS работает без использования какого-либо фреймворка. Но есть несколько функций, сокращающих синтаксис. Например, последовательность функций ac(ce('div')), вызванная в таком виде, вернёт элемент div, присоединённый к элементу body. В ней можно указать второй параметр, и тогда вновь созданный элемент будет присоединяться к указанному в этом параметре элементу-контейнеру. А если делать то же на встроенных функциях (native) код будет выглядеть так:

    document.body.appendChild(document.createElement('div'))
    

    Это не фреймворк, мы просто делаем код короче и логически проще. А также используем несколько очень нужных в работе, но отсутствующих в javascript функций типа cc (changeClass), hc (haseClass), buildElement... Вы тоже можете их использовать, и при желании понять, как они работают (наблюдая за их поведением в работе сайта).

    У нас нет сейчас цели глубоко изучить (Дмитрий Котеров), и закончите сайтом http://javascript.ru/ (Илья Кантор). Сейчас уже трудно найти офлайн-справочник по javascript Юрия Лукача, иначе он был бы первым в списке (хоть и устарел во многом, потому что браузеры сильно изменились).

    Перенос сайта и подключение к mysql

    Обычно код сайта меняется на "домашней" машине, а потом изменения переносятся на сервер. И несколько неудобно каждый раз при копировании файла index7.php (например) потом руками менять в нём данные для подключения к mysql (на сервере ведь один пользователь, а дома – другой).

    Поэтому мы всё дальше уходим от мечты – сделать сайт "в одном файле php". Выносим данные для подключения в файл dns.php. Это будет маленький файл с массивом настроек внутри (mysql пользователь, пароль, бд...), его надо будет исключить из синхронизации при автоматическом обновлении файлов сайта из внешнего источника.

    Заодно в этом файле будет метка, указывающая на доступ к командной оболочке shell (не на всех хостингах это возможно – ну, или вдруг где-то у нас проскользнёт сервер под windows, например). Если метка положительная, мы будем обрабатывать (например) картинки сразу convert'ом, без использования "изобразительных" функций php.

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

    1. В функции response в массив $valid мы должны добавить новые функции: dump, load_sql, rm_page, create_page. Последние две – это долг за прошлую версию (05): там уже были сделаны кнопочки на javascript "Удалить страницу" и "Добавить страницу", но они не будут работать, пока мы не создадим и не разрешим соответствующие функции в php-коде.

    2. Все эти "пишущие" функции будут или менять содержимое сайта или выдавать его полностью пользователю (выгрузка sql). Это значит, в начале каждой такой функции мы должны проверять авторизацию пользователя и отказываться выполнять функции, если пользователь не авторизован. Пользователь у нас пока один, а позже нужно будет ещё и разделить права на выполнение этих функций для пользователей с разными статусами. Создадим для этого функцию _us().

    3. Часть функций потребует достаточно сложной работы с mysql, поэтому придётся добавить соответствующие функции внутри нашего класса Mysql: во-первых функцию insert_id, которая будет возвращать id последней вставленной в таблицу записи (чтобы можно было перенаправить пользователя на вновь созданную страницу – по её идентификатору).

    Затем нужна будет функция dump_table, выгружающая таблицу в sql-файл (точнее в файл sql.gz). Это будет целая группа функций, обеспечивающая выполнение следующих задач: основная функция проверяет всякие входящие параметры (например, можно выгружать не целую таблицу, а часть), создаёт sql для выборки данных, добавляет с помощью дополнительной функции add_create_table текст запроса на создание таблицы и, наконец, вызывает основную функцию выгрузки sqldump_long_sql. Вставка "_long_" в названии функции говорит о том, что можно выгружать таблицы очень большого размера (больше, чем php может удержать в оперативной памяти): php выбирает из БД данные небольшими фрагментами (около 300 килобайт) и дописывает их в файл sql.gz, каждый раз подставляя в начало фрагмента заголовок запроса вида "insert into mypages (id, title, html1...) values ".

    Затем нужны будут две функции для загрузки в БД из созданного нами файла sql.gz: основная – handle2bd (читает sql из файла и копит его в памяти), и дополнительная – flush_sql (вызывается каждый раз, когда основная функция обнаруживает в строке sql разделитель запросов – точку с запятой и перенос строки \n).

    4. Функция "Добавить страницу" должна не просто выполнить sql-запрос на вставку строки (insert into mypages...) с какими-то дефолтными значениями; желательно обеспечить какую-то минимальную защиту от вставки большого количества пустых (полупустых) страниц. Для этого мы добавляем в таблицу mypages поле mark – "метка" со значением по умолчанию "-8". А отображать в меню будем только страницы с меткой "1" и больше (положительными числами).

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

    Метка "-8" заменяется на метку "1" при любом обновлении строки таблицы с помощью функции write (при успешном обновлении там вызывается функция onwrite_mypages). То есть если редактор изменил вручную какое-либо поле на странице, мы считаем, что страница перестала быть "новой".

    5. Функция rm_page, конечно, намного проще – просто выполняем запрос на удаление. Удаление безвозвратное, поэтому javascript перед вызовом php-функции переспрашивает редактора, уверен ли он в своём желании удалить страницу (защита от случайного нажатия кнопки).

    6. Чем сложнее становится код, тем больше ему (точнее, программисту) требуется "обслуживающих" функций. Сейчас, при отладке функции dump, нам понадобилось посмотреть, как передаётся массив параметров от dump к Mysql::dump_table. Но функция перенаправляет нас на загрузку файла, и браузер не отображает страницу с ошибкой работы функции – мы не можем воспользоваться обычным print>_r. Поэтому мы создаём лог-файл и туда выгружаем искомый массив:

    log_w(var_export($param, true))
    

    Лог-файл создаётся функцией log_w, которую мы специально для этого же и написали.

    7. Ещё нам понадобилось написать функцию для создания директории. Функция, собственно, уже есть в Phpmkdir. Но нам а) не надо создавать директорию, если она уже есть (надо проверять существование файла), б) если есть файл (а не папка) с таким именем (вдруг, случайно), его надо удалить. И только потом создавать папку вместо одноимённого файла. Заодно подправляем права (на 0755). То есть несколько более сложная логика, чем просто создать папку:

    function mkdir_safe( $dir, $force ) {
    	$ret = false;
    	if (file_exists($dir)) {
    		if (is_dir($dir)) $ret = $dir;
    		else if ($force) unlink($dir);
    	}
    	if (!$ret  (mkdir($dir, 0755, true))) $ret = $dir;
    	if ($ret) chmod($dir, 0755);
    	return  $ret;
    }
    

    Вот 4 файла очередной версии (06): index.php, core.php, site.js, site.css – код вырос до ~43 килобайт (в предыдущей версии было ~36).

    Архитектура CMS, новые задачи

    Кажется, мы могли бы работать с предельно простой системой разделения кода по файлам: один файл php, один js и один css. Это возможно – если один раз написать весь код и забыть (а потом всё будет работать само, без вмешательства программиста).

    Потребность разделять код на отдельные файлы возникает из-за развития, изменения кода. CMS (вообще любая программа) или развивается, или умирает. У нас программа совсем новая, она пока развивается чрезмерно интенсивно; тем важнее сделать применение изменений, перенос их между версиями более удобным.

    Мы уже сделали незаметный шаг – стали помещать версии в отдельные папки (05, 06), потому что потребовалась активная работа с файлами js, css. Мы отделили также два файла настроек: core.php, dns.php (для подключения mysql). Дальнейшее разделение php-кода связано с двумя направлениями:

  • "абстрактное" упорядочивание (например, по функциональности – выделить все "пишущие" функции в отдельный файл);
  • разделение по частоте изменений: теоретически некоторый общий код (ядро CMS) должен переноситься неизменно от сайта к сайту (мы стремимся именно к этому, стремимся создать и отладить такой код); и всегда на новом сайте будет совершенно другое оформление, структура страниц – эти функции ("шаблонные") как раз и надо в первую очередь обособить, отделить от ядра.
  • Добавим также префиксы таблицам mysql, так как таблицы будут сильно меняться по структуре (и эти изменения могут повредить работе предыдущих версий).

    Мы наметили структурное, "архитектурное" направление изменений. Теперь перечислим практические задачи развития CMS, стоящие на очереди.

  • Управление файлами (загрузкой, удалением, привязкой к страницам сайта).
  • Создание фотогалереи (вывод списков изображений на страницах сайта).
  • Добавление возможности комментариев на сайте и управление комментариями.
  • Изменение системы навигации на ЧПУ ("человеко-понятные УРЛ") – вместо адресов страниц вида "index.php?id=1" использовать что-то вроде "main-1.html", "contacts-2.html".
  • Разделение прав пользователей (admin, например, может только просматривать возможности CMS без права изменений данных, author – править тексты, editor – загружать и менять изображения...).
  • Добавление быстрой обратной связи – всплывающей формы типа "Заказ обратного звонка", при отправке которой владелец сайта будет получать уведомление на e-mail.
  • Заголовки http (кодировка, кэширование). Валидация http-кэша (установка времени изменения страницы с учётом всех выводимых на странице сущностей – комментариев, фото...).
  • После реализации этих изменений общий код сайта (php, js, css) вырастет в два-три раза (будет примерно 100 килобайт).

    И так получается, что многие из этих изменений выгодней делать "оптом", а не по отдельности. Скажем, нам понадобится 2-3 новых таблицы mysql. Лучше сразу продумать их структуру, написать запросы на создание таблиц и выполнить их за один раз. Их можно выполнить теперь, кстати, без phpMyAdmin: у нас есть кнопка "Загрузить sql", можно нажать на неё, выбрать файл с написанным заранее sql-кодом и отправить его на сервер – он там выполнится, и мы увидим отчёты о выполнении (сколько строк затронуто, были ли ошибки).

    Или, скажем, нам понадобятся таблицы для хранения а) комментариев и б) обращений через форму обратной связи. Мы можем продумать структуру и понять, что здесь будет достаточно одной таблицы, но в ней должно быть поле "статус", в котором цифра будет указывать на назначение записи в таблице (например, цифра "1" – комментарий, а цифра "2" – "обратный звонок").

    И лучше начать с ЧПУ (чтобы потом меньше переделывать), так как система адресации на сайте используется во всех остальных подсистемах.

    CMS: разделение кода по файлам

    После реализации большинства задач предыдущей главы получилось примерно вот что: http://nichtig.ru/07/ (можно авторизоваться с данными admin, admin и скачать zip и sql этой версии себе на компьютер).

    Код php между разными файлами распределился так:

  • exe/common.php – классы Config, Route, Mysql, Norm, Page. Класс Config очень простой – он хранит 4 массива: public, private, fields, subjects. Идея такая: public выгружается в javascript для использования на стороне клиента; private используется только в Php, fields описывает поля разных таблиц Mysql (эта информация потом используется для создания HTML-форм и для валидации данных); subjects описывает разные сущности, используемые в работе CMS.

    А сущности, кстати, такие: page, file, msg, command. Для первых трёх создаются соответствующие таблицы для хранения данных, сущность command не имеет хранимых данных, она воплощена только в php-коде – это набор функций для управления содержимым сайта.

  • exe/Command.php – класс Command. Содержит функции для записи данных, вызываемые всегда через "обёртку" write_wr() (вместо "конструктора"); функции для организации выгрузки и загрузки sql и файлов всего сайта.
  • exe/config.php – просто массив; загружается в соотв. разделы класса Config при инициализации сайта (функцией _init(), вызываемой из Route::response()).
  • index.php – единственный php-файл в корне сайта, содержит минимальные важные настройки: debug mode (надо ли включать отладку и на каком уровне отлаживать, как много информации о работе программы выводить); префикс mysql-таблиц TPREFIX (позволяет в одной БД хранить данные разных версий сайта) и некоторые другие константы (посмотрите сами). Подключает явно и "поимённо" (через "include") остальные php-файлы CMS.
  • exe/functions.php – просто функции, которые сложно отнести к какому-либо конкретному классу. Или они "слабо" связаны с кодом класса и вынесены в общую кучу, чтобы лучше была видна логика внутри класса (чтобы в самом классе было меньше "отвлекающего от сути" кода).
  • exe/Site.php – "наследник" класса Page, на каждом сайте может меняться полностью, так как именно этот класс отвечает за отображение, за внешний вид информации на сайте. Может быть совершенно пустым, тогда для вывода информации в HTML используется "по умолчанию" шаблон simple_tpl из класса Page.
  • exe/Abadon.php – аналог Site.php для административных команд, содержит класс Ab – наследник класса Command. Имя класса отличается от имени файла для шутки (мы не соблюдаем PSR в нашей маленькой CMS).
  • exe/users.php – массив с описанием пользователей (редакторов и администраторов); решили хранить его так, а не в Mysql, чтобы надёжнее можно было авторизоваться (удобно, когда таблиц mysql ещё нет и их надо создать загрузкой sql-файла с помощью встроенных функций нашей CMS).
  • exe/dns.php – массив с информацией для подключения Mysql (пользователь, пароль, кодировка...).
  • Код javascript и CSS не разделяется по разным файлам и содержится в соответствующих единственных файлах: site.js, site.css.

    Файл .htaccess в корне сайта содержит достаточно стандартные инструкции: перенаправляет все запросы (если не существует соответствующий реальный файл на сервере) на файл index.php; запрещает вариант сайта, начинающийся с www. (перенаправляет такие запросы на "обычный" адрес); запрещает сканирование директорий (options -Indexes).

    Файл .htaccess в папках img, files, source запрещает запросы HTTP к файлам php.

    Файл .htaccess в папке exe запрещает любые запросы HTTP в эту папку.

    Эту часть защиты можно было сделать проще: запретить вообще любые запросы к файлам php (кроме index.php) на всём сайте. Это на ваш вкус. Это защита от довольно редкого случая, когда кому-то удастся загрузить на ваш сайт свой файл php и с помощью этого файла выполнять там любые команды. Случай редкий, потому что даже если ОН украдёт ваш пароль от CMS, php-файл он загрузить на сайт незаметно не сможет:

  • мы явно перечислили расширения файлов, которые можно загружать в параметре 'upload_ext' в файле config.php;
  • наша CMS построена с расчётом на то, что ни редактору, ни программисту после установки не надо будет пользоваться загрузкой по (s)ftp – все файлы можно загружать через функцию CMS "Загрузить файл sql или zip". Вы можете так настроить права пользователей, чтобы даже через http никто не мог бы загрузить zip (а загружались бы только файлы картинок). Zip (с файлами программы) в общем-то уже и не нужно загружать, когда процесс отладки закончился и сайт начал работать стабильно.
  • Вернуться к учебному плану