Php: выводим список картинок-миниатюр, у которых в атрибутах src указываем путь к маленькому изображению (такие изображения-миниатюры хранятся в папке files/th/0/). Каждая картинка (html элемент IMG) обёрнута в ссылку – html-элемент A с атрибутом href="path_to_bigPhoto" (путь к большому изображению, которое хранится в папке files/0/). Весь список помещаем в контейнер div (например) с уникальным идентификатором, содержащим наименование функции javascript. Функция php, вычисляющая уникальность идентификатора для каждого такого "виджета", копит эти идентификаторы в массиве Page::$config['markers'], а потом в конце страницы мы пишем javascript, который, во-первых, перечисляет массив всех "видждетов", потом по очереди вызывает функции, собранные в этом массиве (Catalog, Form, YMap...):
dict.markers = {"catalog":[{"num_id":1,"subj":"catalog","real":false}]}
initMarkers();
Все javascript-обработчики для Каталога, Формы или других задач хранятся в объектах и пишутся в javascript-коде с большой буквы; на сервере же все они пишутся с маленькой буквы и список в результате генерируется почему-то тоже с маленьких букв. Поэтому функция последовательного вызова обработчиков (initMarkers) должна преобразовывать наименования вызываемых объектов с помощью пользовательской функции capitalize.
Javascript: функция инициализации Каталога init() ищет в переданном по идентификатору элементе div картинки с условной меткой (в нашем случае это class="thumb"). Картинки заносятся в список. Этот список может служить основой для всяческих "лент" и "слайдеров". Весь код занимает 90 строк:
var Catalog = {
img_list: [],
micro_list: [],
kolhoz: true,
tN: 'img',
cN: 'thumb',
init: function (id, subj) {
var box = gi(id);
if (box) {
this.img_index = 0
box.img_list = [];
box.Big = this.getBig(box); //для Большой Картинки
//this.box.micro_list = []; //будущая микронавигация
//box.micro = ce('div');
//cc(box.micro, 'micro')
box.img_index = 0;
setE(box, this.tN, {className:this.cN},
{onclick:Catalog.showBig, box:box},
'inList', 'Catalog')
this.addLister(box.Big)
}
},
inList: function (el){
Catalog.img_list[Catalog.img_index] = el
//Для слияния разных контейнеров с фото
el.box.img_list[el.box.img_index] = el
el.c_num = Catalog.img_index
//el.micer = el.box.micro.insertBefore(ce('span'),
el.box.micro.firstChild)
Catalog.img_index += 1
el.box.img_index += 1
Catalog.addEditLink(el);
},
addEditLink: function (el){
el.item = findParent(el, 'div', 'item');
var carr = el.className.split(/\s/);
if (Editor.editable) {
ac(buildEl2('span', {sql:['_edit', 'file', carr[1]], className:'_edit2',
onclick:Editor.preBuildForm, el:el.parentNode}, 'Редактировать file ' + carr[1]), el.item)
cc(el.item, '_edit')
}
},
addLister: function (Big){
var pre = {prev:'prevI', next:'nextI'};
if (!Big.prev) {
for (var id in pre) {
Big[id] = ac(buildEl2('div', {className:pre[id], onclick: Catalog.showNext}), Big);
}
}
},
getBig: function (box){
return (box.Big)
? box.Big
: ac(buildEl2('div', {className:'bigImgBox disnone'}));
},
getBigI: function (Big){
if (!Big.BigI) {
var tC = ac(buildEl2('div', {className:'tCell'}), Big);
Big.BigI = ac(buildEl2('img', {className:'bigImg'}), tC);
}
return Big.BigI;
},
showBig: function (e, o){
prevent(e);
o = o || this; //img (thumb)
var Big = o.box.Big;
Catalog.getBigI(Big).src = o.parentNode.href;
Hider.show(Big);
Big.prev.img = o;
Big.next.img = o;
window.scrollTo(0,0)
},
showNext: function (e, o){
o = o || this
var img = o.img, idx = img.c_num, img2, box = img.box, img_list = box.img_list;
idx = (hc(o.className, 'nextI')) ? idx – 1 : idx + 1
img2 = img_list[idx]
//cc(img.micer, null, 'curr') //будущая микронавигация
//cc(img2.micer, 'curr')
if (img2) {
Catalog.showBig(null, img2)
box.Big.prev.img = img2
box.Big.next.img = img2
}
}
}
При щелчке по картинке создаётся (или используется раньше созданный) DIV 800 х 600 пикселов (можно другой размер), в него помещается элемент IMG с src, взятым из атрибута href ссылки, в которую обёрнута каждая миниатюра.
Картинок много, и хотелось бы их листать, "не отходя от витрины". Добавляем к контейнеру большого изображения стрелки вправо и влево (next, prev). К стрелкам присоединяем javascript-функцию showNext, которая ищет в списке картинок следующую картинку (по индексу текущей картинки +- 1) и отображает в большом окне эту новую картинку.
Список картинок хранится как свойство текущего контейнера. Если на странице несколько таких контейнеров (несколько разных групп фото), они не пересекаются. Но у нас зарезервировано свойство kolhoz: можно указывать его true и в этом случае хранить список картинок как свойство общего объекта Catalog (при листании будут показываться все фото из разных групп страницы подряд). Это дольше описывать, чем реализовать (2-3 строчки кода).
Зарезервировано ещё несколько строк для микронавигации. Часто можно увидеть на сайтах под большой картинкой такие кружочки (или квадратики), символизирующие список всех фото. Это тоже несложно добавить – больше кода, наверное, будет в CSS.
Кстати, о CSS: общий css сайта пока не превышает 8 килобайт, и Каталогу там посвящено примерно 30 строк. В основном, это колдовство с Большой Картинкой и Стрелками вокруг неё (вправо-влево).
(рис 4.1)
(рис 4.2)
(рис 4.3)
(рис 4.4)
(рис 4.5)
(рис 4.6) Ещё раз напомним, что сайт состоит из элементов HTML, в которых мы показываем пользователю данные, извлечённые из БД на сервере. Данные хранятся в ячейках mysql-таблиц. Для доступа к данным со страницы HTML надо послать на сервер фоновый HTTP-запрос, содержащий наименование mysql-таблицы, номер строки (id) и наименование поля (не всегда: если наименования поля нет, форма будет создаваться для всех полей, хранимых в данной строке таблицы).
Везде, где на html-странице выводятся mysql-данные, мы создаём ссылки для редакторского доступа к этим данным. То же и для фото: к каждой миниатюре в списке изображений на станице мы прикрепляем ссылку для редактирования соответствующей строки в таблице Файлы. Ссылки всегда создаются только на стороне клиента (Javascript); они, разумеется, создаются только в том случае, если от Php поступает соответствующий сигнал – в конце страницы написано Editor.editable = true;
Но что если у вас на сайте в шапке размещён "слайдер", картинки в котором мелькают так быстро, что не успеешь кликнуть по ссылке редактирования? И что если вы захотели на время скрыть страницу (но не удалять насовсем)? Как получить доступ к таким неотображаемым объектам? Надо создавать специальные списки для всех mysql-таблиц. Чтобы не загромождать текущую страницу такими списками (или даже просто ссылками на них), поместим все эти "общие" ссылки на страницу входа в Редактор – edit.com. На этой странице ведь сейчас ничего нет, кроме формы авторизации (либо надписи "Пользователь: admin").
Как, кстати, теперь можно попасть на страницу авторизации? Раньше, в версии 06 (http://hichtig.ru/06/) мы делали для режима DEBUG верхнее меню, где, в частности, была ссылка на страницу редактирования. Теперь мы от такого меню отказались. Поэтому надо либо набирать адрес edit.com руками, либо можно нажать сочетание клавиш Ctrl + Alt + e.
Так вот, на странице edit.com мы создали список ссылок (сейчас их три: Страницы, Файлы, Сообщения) для доступа к любой строке любой таблицы Mysql. На самом деле это не ссылки, а эмуляция "кнопок" – элементы SPAN, при щелчке по которым создаётся "выпадающий" список и элемент input, при вводе в который букв или цифр выпадающий список фильтруется (запросами вида select * from file where title like '%море%', которые формируются на сервере при обращении к URL вида js_list.com?table=fileid=3).
При щелчке по элементу списка значение вставляется в поле input и генерируется форма редактирования найденной строки (в текущей таблице mysql). Можно не щёлкать по списку, если знаете точно номер строки – просто ввести этот номер в поле и нажать красную стрелку справа от поля, тогда тоже сгенерируется форма с данными, соответствующими введённому номеру (id).
Вы помните, что все javascript-обработчики у нас начинают работу при получении специального сигнала от Php. По какому сигналу javascript в браузере начинает генерировать кнопки редактирования элементов? (это вопрос для домашнего задания)
(рис 4.7)
(рис 4.8)
(рис 4.9)
(рис 4.10)
(рис 4.11)
(рис 4.12) В версии 05 (http://hichtig.ru/05/) мы добавили нашей CMSphp-функцию _headers(), хотя до этого всё вроде бы прекрасно "работало само": веб-сервер Apache отправлял нужные заголовки ответа. Правда, мы тоже отправляли "свои" заголовки – в двух случаях:
Location: " с невидимым кодом ответа 302 – "временно перемещён").Необходимость отправлять "свои" заголовки возникла прежде всего в связи с фоновыми запросами к серверу (вида js_list.com?table=fileid=3), которые должны возвращать вовсе не html, а javascript. А веб-сервер по умолчанию все ответы, организованные с участием Php, сопровождает заголовком "Content-type: text/html...". Поэтому php-функция js_list обязана отправить заголовок Content-type: text/javascript... (у нас это делает не она сама, а дополнительная функция js_send). Важно не забывать и о кодировке; полностью этот заголовок должен выглядеть так:
Content-Type: text/javascript; charset=UTF-8
Сейчас по умолчанию на всех линукс-серверах и так используется кодировка UTF-8. Но вдруг ваш веб-сервер работает под Windows? Поэтому заголовок Content-type мы отправляем из php всегда, для любого типа содержимого (и javascript, и html).
Обычная вежливость также заставляет нас отправлять заголовок 304 Not Modified, если страница с прошлого раза не изменилась – чтобы браузер не скачивал её заново, а мог брать из своего кэша; и чтобы наш веб-сервер лишний раз не напрягался, генерируя страницу заново.
Насчёт "прошлого раза" здесь должен позаботиться сам браузер (или другой клиент) – если он раньше уже открывал эту страницу, он отправит нам заголовок If_Modified_Since с датой создания страницы. А для этого, в свою очередь, мы должны отправлять браузеру даты создания страниц – в заголовках вида Last-Modified: ....
Мы так и делаем: дата создания страницы хранится у нас в поле ctime ("creation time"). Сейчас эту дату можно считать верной лишь приближённо, так как на странице, например, мог появиться более поздний комментарий (и при этом содержимое страницы изменится – кэш браузера устареет). Это значит, нужно добавить специальную функцию, которая при изменении каждой сущности, связанной с данной страницей (Комментарии, Файлы), будет обновлять время создания страницы – поле ctime. Сделаем эту функцию позже (или вы сами попробуйте сделать в качестве домашнего задания).
ЧПУ расшифровывается как "человекопонятный УРЛ", где аббревиатура УРЛ (Universal Resource Locator) используется в значении "адрес страницы". Внешнее, формальное отличие заключается в том, что в ЧПУ мы не используем параметры запроса (вида ?id=1), а пишем в адресе просто "1": localhost/1. Или "localhost/1.html", с расширением.
Можно однако применить и более содержательные адреса – писать их транслитом или использовать перевод на английский: "kontakty.html" или "contacts.html". Мы будем использовать транслит, потому что он требует меньше ресурсов при автоматической генерации (и человеческих, и машинных). Но редактор всегда может своей рукой написать вместо транслита английские слова (или латинские). Все пробелы будут заменены на подчёркивания.
У вас должен возникнуть вопрос: как запрос адреса contacts.html заставит работать программу, вызываемую из файла index.php? Если сказать коротко, это делается с помощью перенаправления запросов в файле .htaccess. Мы не будем здесь рассматривать синтаксис mod_rewrite сервера Apache, в нашем учебном сайте файл .htaccess совсем небольшой, с достаточно очевидными настройками. Мы описывали выполняемые им задачи в Главе 19, приведём здесь его код полностью:
AddDefaultCharset UTF-8
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.nichtig\.ru$
RewriteRule ^(.*)$ http://nichtig.ru/$1 [L,R=301]
RewriteRule main-1.html$ http://nichtig.ru/ [L,R=301]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-l
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule .* index.php [L]
Options -Indexes
Для использования длинных адресов нам понадобится ещё одно поле в таблице page – назовём его url. Заполнять его – не такая тривиальная задача. После сохранения страницы надо будет вызывать функцию set_page_url, проверять в этой функции, что написал редактор в поле url (и если отправил пустое поле, заполнять его транслитом из поля title), затем изменять запись в таблице page.
Заполнять поле url приходится именно после сохранения страницы, потому что в него мы дописываем в конец значение поля id (через дефис): contacts-4.html. Это решает несколько задач:
id);javascript можно получать идентификаторы страниц;id и внутри php будем использовать именно его – короче и удобнее.Теперь, когда мы "договорились" о системе адресов, можно организовать их распознание на сервере и выдачу соответствующих данных. Создадим для этого целый отдельный класс – Route. В нём одна основная, "рабочая" функция response() и три вспомогательных.
contacts-3.html или write_wr.com; определяем сущность по расширению – связь сущностей с расширениями в массиве Config::$private['subjects'].command, вызываем через основную функцию класса Command command_wr ту функцию, которая указана в имени (без расширения) – write_wr, get_data_js, load_sql... Разумеется, мы вызываем только те функции, которые тут же и перечислены (в функции Route::response), а для неизвестных даём ответ 404.page, ищем запись в таблице сложным способом: если имя оканчивается на дефис-число, ищем по id, если числа в конце имени нет, ищем по полю url (это поле также является уникальным индексом) – вдруг кто-то не захотел, чтобы в конце имени были цифры.Например, я не захотел – при создании новой страницы: там создаётся имя "new_page", ему уникальные цифры в конце не нужны принципиально, потому что в системе в данный момент времени может быть только одна "новая страница".
Или, например, мы устанавливаем нашу новую CMS на сайт со старыми данными, а там уже есть все готовые адреса страниц, и их менять нельзя - тогда там тоже не будет чисел в конце адресов.
Page и заполняем полученными из БД данными массив Page::$data['row'].Page::$tpl(), где значение $tpl берём из соответствующего поля в полученных данных ("tpl").page, для command разрешаем – для вызова пользовательских функций класса Ab с параметрами. Пользовательских – в том смысле, что какой-то другой программист, не автор нашей CMS, позже может написать какую-то свою функцию для обработки данных на сайте, и ему не надо будет её "регистрировать", изменяя код нашей базовой функции Route::response. Эту новую функцию надо будет писать в файле Abadon.php, в классе Ab.
(рис 4.13)
(рис 4.14)
(рис 4.15)
(рис 4.16)
(рис 4.17)
(рис 4.18) В папке files у нас будет склад файлов, которые редактор загрузил на сервер. Потом из этого склада можно файлы брать и использовать на сайте для разных задач (например, для создания Фотогалереи). Файлы можно искать по имени (Дом.jpg), можно просматривать каждую картинку – искать "по содержанию". Второй способ явно медленней. Но что делать, если вы загрузили 1000 картинок с наименованиями вида DSC3424.jpg?
Я считаю, что всё равно надо связывать каждую картинку с какими-то ключевыми словами – так искать будет гораздо удобнее и быстрее. Поэтому система хранения у нас будет организована так: все имена при загрузке файлов превращаются в последовательные числа (1.jpg, 2.jpg, 3.jpg...); старые имена загружаемых файлов сохраняются в БД в таблице file, в поле title; это поле потом можно изменить – записать туда более содержательный текст, чем первоначальный DSC4545.
В таблице file есть ещё несколько полей: например, descr, в которое можно записывать более длинный текст (можно использовать HTML), или поле prop1, в которое можно записывать всё, что угодно (например, адрес ссылки для баннера). Только надо договориться с программистом, чтобы он использовал нужные вам поля в соответствующих шаблонах (Фотогалереи, Каталога товаров...). А, поскольку программист сейчас – это вы, договориться будет несложно.
На странице edit.com (которую можно открыть, нажав клавиши Ctrl + Alt + e), в меню "Файлы" вы вводите текст, который будет искаться в таблице file, по полям title, descr, id – так вы можете быстро найти нужный файл по ключевым словам, которые вы же туда и записали.
В этом месте мы вводим существенное ограничение на использование нашей CMS: загружаемые файлы надо обрабатывать на сервере – уменьшать их размер, и мы, ради простоты кода, будем использовать внешнюю программу – ImageMagick. Тогда изменять размеры можно будет одной командой:
<?php $command = 'convert ' . $dest[0] . ' -resize ' . $dim[0] . 'x' . $dim[1] . '\> -quality 80 ' . $dest[1]; system($command);
Здесь $dest[0] и $dest[1], соответственно, – исходный файл и уменьшенный файл; $dim[0] и $dim[1] – требуемые размеры изображения, которые мы храним в файле config.php (и получаем при работе из Config::$public['img_size']).
Для выполнения команды в операционной системе сервера должен быть установлена система обработки изображений ImageMagick (именно её вызывает команда convert) и у Php должен быть доступ к выполнению команд системы (такой доступ бывает не на каждом хостинге).
Команда системы convert хороша тем, что она очень компактна, в двух её значках – "\>" – спрятана логика, для которой на php понадобится несколько строк кода. Эти значки предписывают конвертеру не менять размеры изображения, если они не больше требуемых. Впрочем, на php можно работать с изображениями более гибко; но это не предмет нашего курса (мы используем в нашей учебной CMS только изменение размеров картинок).
***
Файлы загружаются на сервер группой (можно выбрать на компьютере сразу несколько файлов). На самом деле они загружаются по одному, с помощью XmlHttpRequest (а остальные ждут своей очереди). Мы отображаем процесс загрузки в правом верхнем углу страницы – печатаем имя каждого загружаемого файла.
На сервере для каждого файла сначала создаётся запись в таблице file, затем мы узнаём id новой записи с помощью mysql-функции LAST_INSERT_ID() и перемещаем загруженный файл в нужную папку с нужным именем (id + расширение файла). Мы допускаем, что в одной папке будет храниться до 1000 файлов, а следующие будем складывать в другую папку (по номерам: 0, 1, 2...). Вы можете найти в других CMS разные хитрые способы случайного распределения файлов по разным папкам, но для планируемых нами объёмов (несколько тысяч файлов) сложная система не нужна, и мы по возможности стараемся соблюдать принцип KISS (англ. keep it simple, stupid).
Всё это делает функция Command::upload_file(), которая, в свою очередь, вызывается из функции Command::onwrite_file() (как бы по событию "после записи в таблицу file") в файле Command.php.
При загрузке группы файлов можно сразу указать (выбрать из выпадающего списка) страницу, к которой загружаемые файлы будут привязаны (потом эту страницу можно поменять в форме редактирования каждого файла). Тогда на выбранной странице легком можно будет отображать список изображений, выбрав их запросом "
Мы, видимо, не будем делать специальную форму обратной связи для текущей версии CMS, так как материал сайта к этому не располагает: сайт сделан у нас в форме блога с комментариями, на каждой странице посетители могут писать что угодно, так что ещё одна кнопка "свяжитесь с нами" смотрелась бы странно.
Но, чтобы каждый комментарий или вопрос действительно был "обратной связью", автор сайта должен своевременно получать информацию о новых сообщениях от посетителей. Для этого надо отправлять автору по почте уведомления о новых сообщениях (так же, как это мы делали бы для формы "обратной связи").
В структуру кода "записывающих" функций у нас заложена работа "по событиям": если в файле Command.php мы создадим функцию onwrite_msg, она "автоматически" будет вызываться после записи в БД любого комментария или вопроса.
Вот и напишем в функции Command::onwrite_msg(), чтобы она отправляла на адрес автора сайта текст из полученных данных $_POST['set']['text_q']:
<?php
mail('onerror@mail.ru', 'Новое сообщение на сайте', $_POST['set']['text_q']);
На самом деле, конечно, отправка организована несколько сложнее. В таком виде письмо может уйти не только автору – умелый пользователь легко сможет отправить его ещё на 100 адресов (либо, что более вероятно, письмо никуда вообще не уйдёт из-за синтаксической ошибки).
Дело в том, что протокол отправки писем очень чувствителен к переносам строки – коду . А мы просто будем удалять сочетание \r\n из тела письма:
<?php
$msg = preg_replace('/\r/', "\n", $_POST['set']['text_q']);
mail('onerror@mail.ru', 'Новое сообщение на сайте', $msg);
Ну, и добавится ещё одно ограничение для нашей CMS: для отправки почтовых уведомлений мы используем php функцию mail(), которая требует наличия сервера sendmail. Это значит: веб-сервер должен быть на линуксе, и владелец хостинга должен разрешить использование функции mail(). Хостинг, удовлетворяющий таким условиям, найти несложно, но и обратная ситуация встречается достаточно часто. Поэтому надо просто обращать внимание на такие детали. Или сразу привыкать пользоваться виртуальным сервером (VPS), который вы можете настраивать по своему усмотрению.
Конечно, можно и совсем обойтись без функции /). На самом деле можно даже библиотеку не использовать, а самому написать (получится килобайт 10-30), но тогда надо будет сначала выучить упомянутую выше спецификацию (RFC 5321). Так что выбора, по большому счёту, у вас и нет (вы же не осилите?..).
Чтобы при отладке программы (на домашнем компьютере) не сыпались ошибки, добавим в конфигруацию пункт mailer, значения которого могут быть "mail", "smtp" (для сторонних библиотек) или "null" – тогда почту отправлять вовсе не будем пытаться. В какой именно раздел файла конфигурации добавим новый пункт, обсудим в следующей главе.
Все настройки хранятся у нас в файлах exe/config.php и exe/dns.php (mysql). Хранить настройки в отдельных файлах удобно: это повышает читаемость (да и прочность) кода. К тому же можно привести массив в config.php к однородному виду (сделать всё в два уровня), и тогда можно будет править параметры через http (то есть прямо на сайте). Практика, однако, показывает, что заказчики сайтов не пользуются такой возможностью – всегда обращаются к разработчику для изменения любых параметров конфигурации. Поэтому мы не будем усложнять нашу маленькую CMS в эту сторону – вся конфигурация будет правиться только в файле.
Почему параметр из предыдущей главы (о почтовых уведомлениях) нельзя хранить в файле config.php? Можно, но не совсем правильно. У нас прослеживается три типа параметров:
CMS,Например, Mysql – совершенно отдельная система, она не входит в CMS (и даже не входит в систему Apache – Php); поэтому мы выделили эти настройки в отдельный файл dns.php. То же и с доступом к командам ОС (convert, например) – мы должны записать в dns.php, есть ли у нашей CMS такая возможность, и разветвлять рабочий код соответственно.
Есть ли на нашем хостинге (сервере) возможность отправлять сообщения через sendmail, тоже не имеет прямого отношения к CMS, и этот параметр мы тоже поместим в dns.php.
***
Интересен вопрос о второй группе параметров – "для всей CMS". У нас почти все параметры именно такие. Более того, 95% всего рабочего кода у нас не относится к конкретному сайту (ведь именно к этому мы и стемились, начиная писать CMS). И пора переходить на следующую ступень – выделить код "ядра" в отдельную папку, стоящую выше конкретных сайтов (и сразу много сайтов смогут использовать общий код этого ядра). Ядро у нас получилось так себе – не ядро, а ядрышко, зёрнышко, всего 60 килобайт. Так и назовём его в соответствии с размером – не "kernel", а "Kern", Kern.sys (расширение – для надёжности, чтоб не путать эту папку с другими "просто папками").
В папку "бибилиотеки" Kern.sys поместим файлы Command.php, common.php, config.php, dns.php, functions.php, а на сайтах, в папках exe, останутся файлы Site.php, users.php, Abadon.php. Строго говоря, для "персональных" параметров сайта надо добавить ещё файл site_config.php, и перекрывать его параметрами значения из общего файла config.php. Но пока в этом нужды нет, а когда (и если) возникнет – сделаем.
Эти изменения требует переезда на следующую версию – 08 http://nichtig.ru/08/. Возникает сразу пара небольших проблем:
zip-архив "библиотеки", которая теперь находится не в папке сайта.Первую проблему решаем, добавив в наш "контекстный коммандер" Abadon.php следующую функцию:
<?php
class Ab {
static function lfile () {
$f = SITE . '/files';
if (!file_exists($f)) symlink(SITE . '/../07/files', $f);
}
//...
Функция вызывается так: http://nichtig.ru/08/addon.com?lfile (нужен авторизованный пользователь со статусом 2).
Вторую задачу решить сложнее из-за запутанной логики скачивания архивов. Там javascript отправляет на сервер запрос downzip.com с параметром type, который описан прямо в функции Command::downzip(). Мы добавили сейчас параметр "8", который потребует совершенно отдельной обработки – и в коде ниже придётся добавить самый "верхний" if: если параметр равен 8, пакуем рекурсивно папку Kern.sys (функцией work_zip):
<?php
class Command {
//...
static function downzip () {
//...
if ($type == $rtypes['lib']) {
work_zip($Zip, LIB, LIB);
}
//...
Ну, и последняя деталь. Вы видите в примере константу LIB, которой не было в предыдущей версии. Мы не хотим связывать себя конкретными обязательствами по месту расположения нашего Kern.sys, поэтому создаём (прямо в файле index.php) функцию lib_find, которая будет искать свою "библиотеку" начиная с папки самого сайта (да, можно помещать Kern.sys хоть внутри сайта):
<?php
define('SITE', realpath(dirname(__FILE__)));
//...
define('LIB', lib_find(EXE, 'Kern.sys'));
//...
function lib_find($path, $lib_name) {
$res = null;
DIRECTORY_SEPARATOR == '/' || $path = str_replace('\\', '/', $path);
$path_arr = explode('/', $path);
while(array_pop($path_arr)) {
$test = implode('/', $path_arr) . '/' . $lib_name;
if (file_exists($test)) {
$res = $test;
break;
}
}
return $res;
}
Остаётся пока вопрос, как все эти пути будут работать под Windows. Протестируйте это, если у вас есть Windows. Если возникнут проблемы, пишите комментарии. Впрочем, и так известно, что возникнут. Поэтому заменим сразу строчку определения константы SITE:
<?php
define('SITE', str_replace('\\', '/', realpath(dirname(__FILE__))));
Вот теперь проверяйте (это домашнее задание). Проверять надо так:
Второе задание: создайте систему редактирования файла Kern.sys/config.php
Написать код рабочей CMS и описать её работу – разные задачи. И вторая отнюдь не проще. Начнём с инструкции по установке.
Инструкция по установке CMS "Шутка" (назовём её так):
http.Kern.sys на один-два уровня выше (чтобы она была недоступна по http).exe/users.php пользователя со статусом "2". Это можно сделать, например, с помощью команды консоли в вашем каталоге exe: "./add_user.sh my-name my-password 2" (где вместо my-name и my-password вы должны написать своё имя и пароль). Или допишите элемент массива руками (вычислив md5 для пароля в другой программе).Kern.sys/dns.php – впишите туда правильные имя пользователя, пароль и БД для подключения к Mysql.http-адрес, по которому находится ваш новый сайт. Нажмите Ctrl + Alt + e для перехода на страницу авторизации (или введите в адресную строку адрес edit.com и перейдите по нему). Авторизуйтесь с именем и паролем пользователя, которого вы создали (со статусом "2").mysql" в списке красных ссылок справа, убедитесь по отчёту, что таблицы созданы.exe/Site.php и начинайте создавать шаблоны (php, выводящий mysql-данные в окружении HTML-тэгов в нужных местах страницы) для нового сайта.site.css правила для отображения HTML-элементов нового сайта.Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.