Фотогалерея своими руками с использованием Perl
 
ФОТОГАЛЕРЕЯ СВОИМИ РУКАМИ С ИСПОЛЬЗОВАНИЕМ PERL


ПРЕДИСЛОВИЕ

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

ПОСТАНОВКА ЗАДАЧИ:

Какой должна быть собственно фотогалерея? Она должна выводить маленькие иконки фотографий в таблице и постранично, должны быть подписи к фоторгафиям, а сами иконки (а может и подписи тоже) должны быть ссылками на большие фотографии - это внешняя сторона. А что должно быть во внутренней:

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

Теперь самое интересное, что для этого нужно:

собственно сервер, на котором мы будем все это обкатывать, с установленным Perl и MySQL (для тех кто не хочет работать с файловыми БД, я лично отношусь к их числу);

к Perl должны быть установленны модули: CGI (для передачи переменных окружениея), DBI и DBD::mysql (для работы с MySQL), Image::Magick (для работы с графикой).

СТРУКТУРА АДМИН ИНТЕРФЕЙСА:

Итак какую структуру данных мы можем обрисовать:

Какие действия будет производить наш админ-интерфейс:

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

ОПИСАНИЕ АДМИН-ИНТЕРФЕЙСА:

Для начала создадим таблице в базе MySQL их всего две:

  • таблица фотогалерей со следующими полями gallery_table:
    • id - идентификатор галереи (тип поля MEDIUMINT - AUTO_INCREMENT - PRIMARY KEY);
    • name - название, заголовок фотогалереи (тип поля VARCHAR до 250 символов);
    • desc - описание фотогалереи (тип поля VARCHAR до 250 символов);
    • template - шаблон вывода фотогалереи (тип поля TEXT);
    • x_icon - максимальная ширина иконки (тип поля VARCHAR до 5 символов);
    • y_icon - максимальная высота иконки (тип поля VARCHAR до 5 символов);
    • row_list - количество выводимых строк фотографий (тип поля VARCHAR до 5 символов);
    • column_list - количество выводимых колонок фотографий (тип поля VARCHAR до 5 символов);
  • таблица фотографий со следующими полями image_table:
    • id - идентификатор изображения (тип поля MEDIUMINT - AUTO_INCREMENT - PRIMARY KEY);
    • gallery - id номер фотогалереии к которой относится изображение (тип поля MEDIUMINT);
    • name - название изображения, оно же подпись к картинке (тип поля VARCHAR до 250 символов);
    • type_image - расширение (тип) изображения (тип поля VARCHAR до 4 символов);

Скрипт создания таблиц:

#!/usr/bin/perl
use DBI;
$name_base_mysql = "mybase"; # база данных MySQL
$host_base_mysql = "localhost"; # host MySQL
$port_base_mysql = "3306"; # порт MySQL
$user_base_mysql = "user"; # пользователь MySQL
$pass_base_mysql = "qwerty"; # пароль пользователя MySQL
$dbh = "DBI:mysql:$name_base_mysql:$host_base_mysql:$port_base_mysql";
$dbh = DBI->connect($dbh, $user_base_mysql, $pass_base_mysql);
$dbh -> do("CREATE TABLE IF NOT EXISTS image_table (
id MEDIUMINT NOT NULL AUTO_INCREMENT,
gallery MEDIUMINT NOT NULL,
name VARCHAR(250) NOT NULL,
type_image VARCHAR(50) NOT NULL,
PRIMARY KEY (id),
INDEX (gallery))");
$dbh -> do("CREATE TABLE IF NOT EXISTS gallery_table (
id MEDIUMINT NOT NULL AUTO_INCREMENT,
name VARCHAR(250) NOT NULL,
titling VARCHAR(250) NOT NULL,
template TEXT NOT NULL,
x_icon VARCHAR(5) NOT NULL,
y_icon VARCHAR(5) NOT NULL,
row_list VARCHAR(5) NOT NULL,
column_list VARCHAR(5) NOT NULL,
PRIMARY KEY (id))");
print "Content-type: text/html; charset=windows-1251\n\n";
print qq "Create table OK!";
exit;

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

Теперь перейдем непостредственно к нашему скрипту админ-интерфейса. Для этого нам понадобится четыре шаблона:

шаблон загрузки изображения (upload.htm):

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title>Загрузка фoтoграфии</title>
</head>
<body>
<table width="98%" border="0" align="center" cellpadding="4" cellspacing="1">
<tr>
<td bgcolor="#B0B0B0" colspan="2">
<font color="#f5f5f5"><b>Загрузка фотографии</b></font>
</td>
</tr>
<form name="form1" method="post" action="%script_url%" enctype="multipart/form-data">
<tr>
<td width="200" valign="top">Название фотографии:</td>
<td align="left">
<input class="input" name="fdb0" type="text" size="40" maxlength="250" value="">
</td>
</tr>
<tr>
<td width="200" valign="top">Галерея фотографии:</td>
<td align="left">
<select name="fdb1">
<!-- 1 --><option value="%id_category%" %selected%> %name_category%</option>
<!-- 1_insert -->
</select>
</td>
</tr>
<tr>
<td width="200" valign="top">Путь к файлу:</td>
<td align="left">
<input class="input" name="image" type="file" size="250">
</td>
</tr>
<tr>
<td bgcolor="#F5F5F5" width="200" valign="top"></td>
<td bgcolor="#F5F5F5" height="30" align="left">
<input type="hidden" name="mod" size="40" maxlength="256" value="%mod%">
<input type="hidden" name="edit" size="40" maxlength="256" value="%edit%">
<input type="hidden" name="doing" size="40" maxlength="256" value="afform">
<input class="input" type="submit" name="Submit" value="Загрузить">&nbsp;&nbsp;&nbsp;
<input class="input" type="reset" value="Очистить">
</td>
</tr>
</form>
</table>
</body>
</html>

шаблон редактирования (он же удаления) изображения (edit.htm):

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title>Форма редактирования (удаления) изображения</title>
</head>
<body>
<table width="98%" border="0" align="center" cellpadding="4" cellspacing="1">
<tr>
<td bgcolor="#B0B0B0" colspan="2">
<font color="#f5f5f5"><b>%doing% изображения</b></font>
</td>
</tr>
<form name="form1" method="post" action="%script_url%" enctype="text/html">
<tr>
<td width="200" valign="top">Название изображения:</td>
<td align="left">
<input class="input" name="fdb0" type="text" size="40" maxlength="250" value="%name%">
</td>
</tr>
<tr>
<td width="200" valign="top">Галерея изображения:</td>
<td align="left">
<select name="fdb1">
<!-- 1 --><option value="%id_category%" %selected%> %name_category%</option>
<!-- 1_insert -->
</select>
</td>
</tr>
<tr>
<td width="200" valign="top"></td>
<td align="left">
<a href="%link_image%" target="_blank"><img src="%link_icon%" border="0" align="middle" vspace="2" hspace="2"></a>
</td>
</tr>
<tr>
<td bgcolor="#F5F5F5" width="200" valign="top"></td>
<td bgcolor="#F5F5F5" height="30" align="left">
<input type="hidden" name="mod" size="40" maxlength="256" value="%mod%">
<input type="hidden" name="edit" size="40" maxlength="256" value="%edit%">
<input type="hidden" name="id" size="40" maxlength="256" value="%id%">
<input type="hidden" name="doing" size="40" maxlength="256" value="afform">
<input class="input" type="submit" name="Submit" value="%doing%">&nbsp;&nbsp;&nbsp;
<input class="input" type="reset" value="Очистить">
</td>
</tr>
</form>
</table>
</body>
</html>

шаблон редактирования (он же создания и удаления) галереи (gallery.htm):

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title>Форма добавления, редактирования и удаления фотогалереи</title>
</head>
<body>
<table width="98%" border="0" align="center" cellpadding="4" cellspacing="1">
<tr>
<td bgcolor="#B0B0B0" colspan="2">
<font color="#f5f5f5"><b>%doing% галереи</b></font>
</td>
</tr>
<form name="form1" method="post" action="%script_url%" enctype="text/html">
<tr>
<td width="200" valign="top">Название галереи:</td>
<td align="left">
<input class="input" name="fdb0" type="text" size="40" maxlength="250" value="%name%">
</td>
</tr>
<tr>
<td width="200" valign="top">Описание галереи:</td>
<td align="left">
<input class="input" name="fdb1" type="text" size="40" maxlength="250" value="%titling%">
</td>
</tr>
<tr>
<td width="200" valign="top">Количество строк:</td>
<td align="left">
<input class="input" name="fdb2" type="text" size="5" maxlength="5" value="%row%">
</td>
</tr>
<tr>
<td width="200" valign="top">Количество колонок:</td>
<td align="left">
<input class="input" name="fdb3" type="text" size="5" maxlength="5" value="%column%">
</td>
</tr>
<tr>
<td width="200" valign="top">Высота иконки:</td>
<td align="left">
<input class="input" name="fdb4" type="text" size="5" maxlength="5" value="%y_icon%"> (px)
</td>
</tr>
<tr>
<td width="200" valign="top">Ширина иконки:</td>
<td align="left">
<input class="input" name="fdb5" type="text" size="5" maxlength="5" value="%x_icon%"> (px)
</td>
</tr>
<tr>
<td width="200" valign="top">Шаблон вывода галереи:</td>
<td align="left">
<table width="100%" border="0" cellpadding="0" cellspacing="0" summary="">
<tr>
<td>
<textarea name="fdb6" cols="70" rows="20">%html%</textarea>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td bgcolor="#F5F5F5" width="200" valign="top"></td>
<td bgcolor="#F5F5F5" height="30" align="left">
<input type="hidden" name="mod" size="40" maxlength="256" value="%mod%">
<input type="hidden" name="edit" size="40" maxlength="256" value="%edit%">
<input type="hidden" name="id" size="40" maxlength="256" value="%id%">
<input type="hidden" name="doing" size="40" maxlength="256" value="afform">
<input class="input" type="submit" name="Submit" value="%doing%">
<input class="input" type="reset" value="Очистить">
</td>
</tr>
</form>
</table>
</body>
</html>

шаблон вывода списка фотографий (show.htm):

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title>Список изображений</title>
</head>
<body>
<table width="98%" border="0" align="center" cellpadding="4" cellspacing="1">
<tr>
<td bgcolor="#B0B0B0" colspan="2">
<font color="#f5f5f5"><b>Список фотогалерей</b></font>
</td>
</tr>
<tr>
<td width="50"></td>
<td align="left">
<table width="60%" border="0" align="left" cellpadding="0" cellspacing="0" summary="">
<tr>
<form action="%script_url%" method="get">
<td>
<input type="hidden" name="mod" value="category" enctype="text/html">
<input type="hidden" name="edit" value="new">
<input type="submit" value="Новая фотогалерея">
</td>
</form>
<form action="%script_url%" method="get">
<td>
<input type="hidden" name="mod" value="image" enctype="text/html">
<input type="hidden" name="edit" value="upload">
<input type="hidden" name="cat" value="%edit%">
<input type="submit" value="Загрузить изображение">
</td>
</form>
</tr>
</table>
</td>
</tr>
<tr>
<td bgcolor="#B0B0B0" colspan="2">
<font color="#f5f5f5"><b>Список фотографий</b></font>
</td>
</tr>
<tr>
<td align="left" colspan="2">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td>
<!-- 1 --><table width="100%" border="0" cellspacing="0" cellpadding="0">
<!-- 1 --><form action="%script_url%" method="get">
<!-- 1 --><tr bgcolor="#B0C4DE">
<!-- 1 --><td width="300" valign="middle">
<!-- 1 --><b> [ %name_category% </b>&nbsp;&nbsp;&nbsp;
<!-- 1 --><a href="%script_url%?edit=%id%">Раскрыть</a>&nbsp;&nbsp;&nbsp;
<!-- 1 --></td>
<!-- 1 --><td width="230" valign="middle">
<!-- 1 --><input name="mod" type="hidden" value="category">
<!-- 1 --><input name="edit" type="hidden" value="edit">
<!-- 1 --><input name="id" type="hidden" value="%id%">
<!-- 1 --><input class="input" name="" type="submit" value="Редактировать">
<!-- 1 --></td>
<!-- 1 --></form>
<!-- 1 --><form action="%script_url%" method="get">
<!-- 1 --><td valign="middle">
<!-- 1 --><input name="mod" type="hidden" value="category">
<!-- 1 --><input name="edit" type="hidden" value="delete">
<!-- 1 --><input name="id" type="hidden" value="%id%">
<!-- 1 --><input class="input" name="" type="submit" value="Удалить">
<!-- 1 --></td>
<!-- 1 --></tr>
<!-- 1 --></form>
<!-- 1 --></table>
<!-- 2 --><table width="100%" border="0" cellspacing="0" cellpadding="0">
<!-- 2 --><form action="%script_url%" method="get">
<!-- 2 --><tr>
<!-- 2 --><td width="300"> [ %name_image% ] </td>
<!-- 2 --><td width="120">
<!-- 2 --><input name="mod" type="hidden" value="image">
<!-- 2 --><input name="edit" type="hidden" value="edit">
<!-- 2 --><input name="id" type="hidden" value="%id%">
<!-- 2 --><input class="input" name="" type="submit" value="Редактировать">
<!-- 2 --></td>
<!-- 2 --></form>
<!-- 2 --><form action="%script_url%" method="get">
<!-- 2 --><td align="left" width="70">
<!-- 2 --><input name="mod" type="hidden" value="image">
<!-- 2 --><input name="edit" type="hidden" value="delete">
<!-- 2 --><input name="id" type="hidden" value="%id%">
<!-- 2 --><input class="input" name="" type="submit" value="Удалить">
<!-- 2 --></td>
<!-- 2 --><td align="center">
<!-- 2 --><a href="%link_image%" target="_blank">
<!-- 2 --><img src="%link_icon%" border="0" align="middle" vspace="2" hspace="2"></a>
<!-- 2 --></td>
<!-- 2 --></tr>
<!-- 2 --></form>
<!-- 2 --><tr><td colspan="4"><hr size="1" color="#F5F5F5"></td></tr>
<!-- 2 --></table>
<!-- 1_insert -->
</td>
</tr>
</table>
</body>
</html>

Теперь обсудим какие что же у нас жа шаблоны получились. Первые три обычные формы без особых сложностей, единственно параметр формы загрузки изображения enctype="multipart/form-data" так как мы должны передать не текст, а файл. Так же в первых двух шаблонах есть динамические внедрения (<!-- 1 --> и <!-- 1_insert -->), так как мы должны будем первоначально формировать список наших галерей для последующего их выбора. Вот с четвертым шаблоном нужно повозится. Во-первых мы должны сформировать и выдать список фотогалерей (<!-- 1 --> - одна строка списка), во-вторых сформировать и выдать список фотографий выбранной фотогалереи (<!-- 2 --> - одна строка списка).
Переменные и параметры которые мы бодем обрабатывать скриптом я, для удобства, выделил темно-желтым цветом. Выделим их в список и определим, что мы с ними предполагаем делать:

  • mod - параметр указывающий объект с которым мы непосредственно работаем: фотогалерея (category) или изображение (image). Значение подставляется скриптом вместо выражения %mod%;
  • edit - параметр указывающий производимое действие над объектом: загрузка (upload), создание (new), изменение (edit), удаление (delete). Значение подставляется скриптом вместо выражения %edit%. При работе со списком указывает фотогалерею (идентификатор), которую просматриваем. Значение подставляется скриптом вместо выражения %id%;
  • id - индентификатор объекта. Значение подставляется скриптом вместо выражения %id%;
  • fdb[0-6] - параметры передаваемые из формы и указывающиеся непосредственно пользователем. Значение подставляется скриптом вместо выражений: %name%, %titling%, %row%, %column%, %y_icon%, %x_icon%, %html%, %id_category%, берущихся из нашей базы данных;
  • image - фотография которую мы хотим загрузить;
  • doing - подтверждение операции над объектом;
  • %script_url% - URL нашего скрипта. Можно конечно указывать его и сразу, но при переносе скрипта на другой хост прийдется переправлять все шаблоны, что несколько неудобно;
  • остальные параметры заключенные %, проще будет объяснить в процессе кода.

Теперь можно заняться непосредственно скриптом. Начало стандартное:

#!/usr/bin/perl
use CGI;
use DBI;
use Image::Magick;
use strict;

Так же я рекомендовал ввести еще дополнительные параметры работы скрипта:

use CGI::Carp qw (fatalsToBrowser); # что бы ошибки скрипта выпадали в браузер
$CGI::POST_MAX = 262144; # что бы не было желания закачивать картинки в особо крупных размерах :-)

Устанавливаем начальные переменные:

use vars '$path_image', '$name_base_mysql', '$host_base_mysql', '$port_base_mysql', '$user_base_mysql', '$pass_base_mysql',
'$mod', '$edit', '$id', '$doing', '$cat', '$image', '@fdb', '@temp', '$dbh', '$sth', '$sql', '$tmp', '$image_url';
$path_image = "/home/site/mysite/html/gallery/"; # путь где будут располагаться фотографии
$script_url = "http://www.mysite.ru/cgi-bin/gallery/gallery.pl"; # url где располагается наш скрипт
$image_url = "http://www.mysite.ru/gallery/"; # url где располагаются наши фотографии
$name_base_mysql = "mybase"; # база данных MySQL
$host_base_mysql = "localhost"; # host MySQL
$port_base_mysql = "3306"; # порт MySQL
$user_base_mysql = "user"; # пользователь MySQL*
$pass_base_mysql = "qwerty"; # пароль пользователя MySQL

*Привилегии для пользователя MySQL можно установить только SELECT, INSERT, UPDATE, DELETE

Забираем "переменные окружения":

my ($query);
$query = new CGI;
$mod = $query -> param('mod');
$edit = $query -> param('edit');
$id = $query -> param('id');
$doing = $query -> param('doing');
$cat = $query -> param('cat');
$image = $query -> param('image');
$fdb[0] = $query -> param('fdb0');
$fdb[1] = $query -> param('fdb1');
$fdb[2] = $query -> param('fdb2');
$fdb[3] = $query -> param('fdb3');
$fdb[4] = $query -> param('fdb4');
$fdb[5] = $query -> param('fdb5');
$fdb[6] = $query -> param('fdb6');

Перенаправляем на процедуру в соответствии с переданными переменными $mod и $edit:

if ($mod eq 'category') {
if ($edit eq 'new') {&mod_category_new}
if ($edit eq 'edit') {&mod_category_edit}
if ($edit eq 'delete') {&mod_category_delete}
}
if ($mod eq 'image') {
if ($edit eq 'upload') {&mod_image_upload}
if ($edit eq 'edit') {&mod_image_edit}
if ($edit eq 'delete') {&mod_image_delete}
}
&mod_list_gallery;
exit;

В итоге получается, что скрипт вызывает определенную процедуру в соответствии с двумя переданными параметрами $mod и $edit - объект и производимые действия над объектом. Если же нас не удовлетворяет хоть один из параметров, то выходим в процедуру просмотра фотогалерей.

Рассмотрим и разберем в одтельности каждую процедуру:

процедура просмотра фотогалерей: sub mod_list_gallery {

Загружаем наш шаблон - show.htm:

my ($file);
$file='show.htm'; open(TMP,$file); @temp=<TMP>; close(TMP);

Вырезаем из него сроку списка галерей и строку списка фотографий:

my ($temp_line_1, $temp_line_2, $temp_list_1);
foreach (@temp) {
if (m/<!-- 1 -->/gi) {$_ =~s /<!-- 1 -->//gi; $temp_line_1 .= $_; $_ = '';}
elsif (m/<!-- 2 -->/gi) {$_ =~s /<!-- 2 -->//gi; $temp_line_2 .= $_; $_ = '';}
}

Берем данные о наших фотогалереях из базы данных MySQL:

$sql = "SELECT id, name FROM gallery_table ORDER BY name ASC";
$dbh = "DBI:mysql:$name_base_mysql:$host_base_mysql:$port_base_mysql";
$dbh = DBI -> connect($dbh, $user_base_mysql, $pass_base_mysql);
$sth = $dbh -> prepare($sql); $sth -> execute();
my ($kolvo_category, $i, $tmp, @category, $line_temp);
$i = 0;
$kolvo_category = $sth -> rows;
while ($tmp = $sth -> fetchrow_arrayref()) {$category[0][$i]=$tmp->[0];$category[1][$i]=$tmp->[1];$i++;}
$sth -> finish();
$dbh -> disconnect();

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

for ($i=0; $i < $kolvo_category; $i++) {
$line_temp = $temp_line_1;
$line_temp =~s /%name_category%/$category[1][$i]/gi;
$line_temp =~s /%id%/$category[0][$i]/gi;
$temp_list_1 .= $line_temp;

Если у нас выбрана, какая, либо фотогалерея для просмотра фотографий (параметр $edit):

if ($edit eq $category[0][$i]) {

Выбираем список фотографий из базы данных MySQL:

$sql = "SELECT id, name, type_image FROM image_table WHERE gallery = '$category[0][$i]' ORDER BY name ASC";
$dbh = "DBI:mysql:$name_base_mysql:$host_base_mysql:$port_base_mysql";
$dbh = DBI -> connect($dbh, $user_base_mysql, $pass_base_mysql);
$sth = $dbh -> prepare($sql); $sth -> execute();
my (@tv, $kolvo_foto, @photo, $ii)
$tv[0] = 0;
$kolvo_foto = $sth -> rows;
while ($tmp = $sth -> fetchrow_arrayref()) {
$photo[0][$tv[0]] = $tmp -> [0];
$photo[1][$tv[0]] = $tmp -> [1];
$photo[2][$tv[0]] = $tmp -> [2];
$tv[0]++;
}
$sth -> finish();
$dbh -> disconnect();

Формируем список из шаблона:

for ($ii=0;$ii<$kolvo_foto;$ii++) {
$line_temp = $temp_line_2;
$tv[2] = $path_image."i".$photo[0][$ii].".".$photo[2][$ii];
$tv[3] = $path_image.$photo[0][$ii].".".$photo[2][$ii];
#$A1][$ii]/gi;
$line_temp =~s /%id%/$photo[0][$ii]/gi;
$line_temp =~s /%link_icon%/$tv[2]/gi;
$line_temp =~s /%link_image%/$tv[3]/gi;
$temp_list_1 .= $line_temp;
}
}

Из вышеуказанного кода теперь можно определить, почему мы в таблице фотографий не указывали имени файла, а только расширение - именем файла фотографии у нас будет являться id номер, что предохранит нас от от повторов имен файлов, т.к. поле id ключевое и повторы в нем запрещены, а так же очень просто выставляется соответсвие иконки для определенной картинки, мы просто к имени файла приставляем "i", а остальное остается неизменным.

Теперь остается собрать наш шаблон воедино и выдать на экран:

foreach (@temp) {
$_=~s /<!-- 1_insert -->/$temp_list_1/gi;
$_ =~s /%script_url%/$script_url/gi;
$_ =~s /%edit%/$edit/gi;
}
print "Content-type: text/html; charset=windows-1251\n\n";
print qq "@temp";
exit;
}

Вот и все с первой процедурой закончили. Берем следующую, самую, на мой взгляд, интересную...

процедура загрузки фотографии: sub mod_image_upload {

Сначала проверяем подтверждение операции над объектом, в данном случае загружаемой картинкой:

if ($doing eq "afform") {

Проверяем имя файла на соответствие разрешенного типа (расширения):

my (@tv, $file)
$tv[0] = $image;
$tv[0] =~s /.*((png)|(gif)|(jpg))$/$1/gi;
if ($tv[0] eq '') {exit;} # Хотя конечно лучше поставить более толковую проверку, но я не буду здесь "раздувать" код

Вставляем данные полученные из формы и полученное расширение файла в MySQL:

$dbh = "DBI:mysql:$name_base_mysql:$host_base_mysql:$port_base_mysql";
$dbh = DBI -> connect($dbh, $user_base_mysql, $pass_base_mysql);
$dbh -> do("INSERT INTO image_table SET name = '$fdb[0]', gallery = '$fdb[1]', type_image = '$tv[0]' ");
$tv[1] = $dbh->{'mysql_insertid'}; # Выберем последний id номер, он будет именем нашего файла
$dbh -> disconnect();

Теперь сохраняем фотографию на сервере:

$file = $path_image.$tv[1].".".$tv[0];
open (IMG, ">$file"); binmode IMG; flock ($file, 2); print IMG while (<$image>); close (IMG); chmod 0644, $file;

Формируем иконку (уменьшенное изображение):

Для начала узнаем какого размера будет иконка:

$sql = "SELECT x_icon, y_icon FROM gallery_table WHERE id='$fdb[1]'";
$dbh = "DBI:mysql:$name_base_mysql:$host_base_mysql:$port_base_mysql";
$dbh = DBI -> connect($dbh, $user_base_mysql, $pass_base_mysql);
$sth = $dbh -> prepare($sql); $sth -> execute();
my ($ix, $iy);
while ($tmp = $sth -> fetchrow_arrayref()) {$ix = $tmp -> [0]; $iy = $tmp -> [1]}
$sth -> finish();
$dbh -> disconnect();

Теперь уменьшаем изображение:

$image = Image::Magick->new;
$image->Read("$file"); # $file - понятно что это только что загруженный файл

Разберемся с тем какой размер иконки должен быть:

my ($ox, $oy, $oc, $ic, $nx, $ny, $geo);
($ox,$oy)=$image->Get('columns','height');
if (($ox > $ix)||($oy > $iy)) {
$oc = $ox/$oy; $ic = $ix/$iy;
if ($oc < $ic) {$ny = $iy; $nx=int(($ox/$oy)*$iy);}
elsif ($oc > $ic) {$nx = $ix; $ny=int(($oy/$ox)*$ix);}
else {$nx = $ix; $ny = $iy;}}
else {$nx=$ox;$ny=$oy;}

Изменяем размер и сохраняем изображение:

$geo = 'geometry';
$image->Resize(geometry=>$geo, width=>$nx, height=>$ny);
$file = $path_image."i".$tv[1].".".$tv[0];
$image -> Write("$file");

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

$url = $script_url."?edit=".$fdb[1];
print "Location: $url\n\n";
}

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

Выгружаем шаблон:

$file = "upload.htm";
open(TMP, $file);
@temp = <TMP>;
close(TMP);

Вырезаем строку из SELECT для того, что бы сформировать списко галерей для выбора

foreach (@temp) {
if (m/<!-- 1 -->/gi) {$_ =~s /<!-- 1 -->//gi; $temp_line_1 .= $_; $_ = '';}
}

Берем данные о наших фотогалереях из базы данных MySQL*:

$sql = "SELECT id, name FROM gallery_table ORDER BY name ASC";
$dbh = "DBI:mysql:$name_base_mysql:$host_base_mysql:$port_base_mysql";
$dbh = DBI -> connect($dbh, $user_base_mysql, $pass_base_mysql);
$sth = $dbh -> prepare($sql); $sth -> execute();
my ($kolvo_category, $i, $tmp, @category, $line_temp);
$i = 0;
$kolvo_category = $sth -> rows;
while ($tmp = $sth -> fetchrow_arrayref()) {$category[0][$i]=$tmp->[0];$category[1][$i]=$tmp->[1];$i++;}
$sth -> finish();
$dbh -> disconnect();

*Кстати подобная операция, как, впрочем и следующая, будет повторятся практически везде, поэтому её можно вынести в отдельную процедуру, либо выполнять сразу в начале скрипта...

Формируем список фотогалерей:

for ($i = 0; $i < $kolvo_category; $i++) {
$line_temp = $temp_line_1;
if ($cat eq $category[0][$i]) {$line_temp =~s /%selected%/selected/gi;} # вот, кстати, зачем нам нужен был параметр cat - только для этого, что бы было удобно
else {$line_temp =~s /%selected%//gi;}
$line_temp =~s /%id_category%/$category[0][$i]/gi;
$line_temp =~s /%name_category%/$category[1][$i]/gi;
$temp_list_1 .= $line_temp;
}

Собираем шаблон, и выводим на экран:

foreach (@temp) {
$_=~s /<!-- 1_insert -->/$temp_list_1/gi;
$_ =~s /%script_url%/$script_url/gi;
$_ =~s /%mod%/$mod/gi;
$_ =~s /%edit%/$edit/gi;
}
print "Content-type: text/html; charset=windows-1251\n\n";
print qq "@temp";
exit;
}

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

  • если мы производим операцию над объектом, то есть производим upload изображения
    • получаем имя файла (переменная $image - по сути имеет два значения, имя файла и непосредственно файл; и в зависимости от того, как мы используем эту переменную, то получаем соответстующее её значение);
    • выбираем расширение файла, в нашем случае ((png)|(gif)|(jpg)) - разрешенные расширения файлов которые мы можем загружать;
    • вставляем данные об изображении в таблицу базы данных MySQL;
    • производми upload файла;
    • сразу же считываем только что загруженный файл в новый проект Image::Magick;
    • определяем размеры изображения, до которого мы должны его ужать;
    • сжимаем изображение и записываем в новый файл;
    • редиректим скрипт;
  • если же мы только хотим загрузить файл, то просто выводим обработанную форму и все.

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

процедура изменения и процедура удаления фотографии: sub mod_image_edit { sub mod_image_delete {

Данные процедуры отличаются лишь тем, что при переданном параметре doing - первая производит UPDATE таблицы MySQL, а вторая DELETE FROM. А изменение изображения сводится только к изменению названия, описания и галереи изображения, правда есть некоторые нюансы:

Сначала проверяем подтверждение операции над объектом, в данном случае - картинкой:

if ($doing eq "afform") {

Обновляем таблицу если мы редактируем изображение

$dbh = "DBI:mysql:$name_base_mysql:$host_base_mysql:$port_base_mysql";
$dbh = DBI -> connect($dbh, $user_base_mysql, $pass_base_mysql);
$dbh -> do("UPDATE image_table SET name = '$fdb[0]', gallery = '$fdb[1]' WHERE id = '$id'");
$dbh -> disconnect();

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

Эта операция полностью идентична опрерации изменения иконки зображения при upload, единственное прежде нужно взять данные об изображении (id номер и расширение (type-image)). После изменения иконки производим редирект в список фотогалерей

Удаляем запись из таблицы если мы удаляем изображение:

Сначала считываем данные о этом изображении, для того, что бы мы могли удалить соответственные файлы:

$sql = "SELECT id, type_image FROM image_table WHERE id = '$id'";
$dbh = "DBI:mysql:$name_base_mysql:$host_base_mysql:$port_base_mysql";
$dbh = DBI -> connect($dbh, $user_base_mysql, $pass_base_mysql);
$sth = $dbh -> prepare($sql); $sth -> execute();
my (@photo)
while ($tmp = $sth -> fetchrow_arrayref()) {
$photo[0][0] = $tmp -> [0];
$photo[1][0] = $tmp -> [1];
}
$sth -> finish();

Удаляем запись:

$dbh -> do("DELETE FROM image_table WHERE id = '$id'");
$dbh -> disconnect();
$file = $path_image."i".$photo[0][0].".".$photo[1][0]; unlink ($file);
$file = $path_image.$photo[0][0].".".$photo[1][0]; unlink ($file);

Опять же редирект...

}

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

$file="edit.htm";
open(TMP,$file);
@temp=<TMP>;
close(TMP);

foreach (@temp) {
if (m/<!-- 1 -->/gi) {$_ =~s /<!-- 1 -->//gi; $temp_line_1 .= $_; $_ = '';}
}

my ($kolvo_category, $i, $tmp, @category, $line_temp, @photo);
$sql = "SELECT id, name, gallery, image FROM image_table WHERE id = '$id'";
$dbh = "DBI:mysql:$name_base_mysql:$host_base_mysql:$port_base_mysql";
$dbh = DBI -> connect($dbh, $user_base_mysql, $pass_base_mysql);
$sth = $dbh -> prepare($sql); $sth -> execute();
while ($tmp = $sth -> fetchrow_arrayref()) {
$photo[0][0] = $tmp -> [0];
$photo[1][0] = $tmp -> [1];
$photo[2][0] = $tmp -> [2];
$photo[3][0] = $tmp -> [3];}
$sth -> finish();

$sql = "SELECT id, name FROM gallery_table ORDER BY name ASC";
$dbh = "DBI:mysql:$name_base_mysql:$host_base_mysql:$port_base_mysql";
$dbh = DBI -> connect($dbh, $user_base_mysql, $pass_base_mysql);
$sth = $dbh -> prepare($sql); $sth -> execute();
$i = 0;
$kolvo_category = $sth -> rows;
while ($tmp = $sth -> fetchrow_arrayref()) {$category[0][$i]=$tmp->[0];$category[1][$i]=$tmp->[1];$i++;}
$sth -> finish();
$dbh -> disconnect();

for ($i = 0; $i < $kolvo_category; $i++) {
$line_temp = $temp_line_1;
if ($photo[2][0] eq $category[0][$i]) {$line_temp =~s /%selected%/selected/gi;}
else {$line_temp =~s /%selected%//gi;}
$line_temp =~s /%id_category%/$category[0][$i]/gi;
$line_temp =~s /%name_category%/$category[1][$i]/gi;
$temp_list_1 .= $line_temp;
}

$tv[2] = $image_url."i".$photo[0][0].".".$photo[3][0];
$tv[3] = $image_url.$photo[0][0].".".$photo[3][0];

foreach (@temp) {
$_=~s /<!-- 1_insert -->/$temp_list_1/gi;
$_=~s /%name%/$photo[1][0]/gi;
$_=~s /%link_image%/$tv[3]/gi;
$_=~s /%link_icon%/$tv[2]/gi;
$_=~s /%doing%/Изменение || Удаление/gi; # В зависимости от действия
$_ =~s /%script_url%/$script_url/gi;
$_ =~s /%mod%/$mod/gi;
$_ =~s /%edit%/$edit/gi;
$_ =~s /%id%/$photo[0][0]/gi;
}
print "Content-type: text/html; charset=windows-1251\n\n";
print qq "@temp";
exit;

Вот в общем-то все процедуры работы с изображениями рассмотрели, теперь процедуры работы с фотогалереями:

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

  • в процессе создания мы ничего не проверяем толко данные переданные из формы;
  • в процессе редактирования мы проверяем изменились ли параметры x_icon и y_icon, и если да, то переписываем все иконки изображений в соответсвии с новыми параметрами;
  • в процессе удаления, мы проверяем есть ли в данной фотогалереи фотографии и если есть отказываем в удалении, чтобы не оставалось "безхозных" фотографий ;

Структура же процедур точно такая же как и у процедур работы с изображениями;

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

ОПИСАНИЕ СКРИПТА ПОКАЗА ФОТОГАЛЕРЕИ:

Сам шаблон фотогалереи у нас находится в таблице базы данных MySQL, а так как, у нас может быть несколько фотогалерей, то и шаблонов будет столько же, и при запуске нашего скрипта нужно будет указывать какую фотогалерею мы используем. То есть скрипт сам по себе по ссылке http://www.mysite.ru/cgi-bin/gallery.cgi - работать не будет, точнее будет, но покажет при этом пустую страницу, для этого ссылку нужно будет делать http://www.mysite.ru/cgi-bin/gallery.cgi?gal=1, где gal - id номер фотогалереи. Если же мы будем использовать только одну галерею, то данный параметр мы можем вписать прямо в скрипт, и тогда не будет никаких "заморочек".

Я предполагаю для себя такую структуру шаблона:

Что в HTML коде будет выглядеть как:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title>Моя фотогалерея</title>
</head>
<body>
<table width="98%" border="0" align="center" cellpadding="4" cellspacing="1">
<tr>
<td>
%name_category%
</td>
</tr>
<tr>
<td>
%title_category%
</td>
</tr>
<!-- 1_insert -->
<!-- 1 --><tr>
<!-- 1 --><td>
<!-- 1 -->Страницы &lt;&nbsp;
<!-- 2 --><a href=""></a>&nbsp;
<!-- 3 --><b></b>&nbsp;
<!-- 1 --><!-- 2_insert -->
<!-- 1 -->
&gt;
<!-- 1 --></td>
<!-- 1 --></tr>
<!-- 4_insert -->
<!-- 4 -->
<tr>
<!-- 4 --><!-- 5_insert -->
<!-- 5 -->
<td>
<!-- 5 --><a href="%link_image%" target="_blank"><img src="%link_icon%" alt="%name_image%"></a><br>
<!-- 5 -->%name_image%
<!-- 5 --></td>
<!-- 4 --></tr>
<!-- 1_insert -->
</table>
</body>
</html>

В блоках «заголовок» и «окончание» используются следующие переменные:

  • %name_category% - Название категории;
  • %title_category% - Описание категории;

В блоке «фотография» используются следующие переменные:

  • %link_image% - Ссылка на фотографию;
  • %link_icon% - Ссылка на уменьшенное изображение;
  • %name_image% - Название фотографии;

В блоке «список страниц» используются следующие переменные:

  • - Ссылка на предыдущую/следующую страницу;
Теперь перейдем скрипту, естественно он должен быть компактен и работать в один проход, то есть процедуры мы не используем:

Начало стандартное:

#!/usr/bin/perl
use CGI;
use DBI;
use DBD::mysql;
use strict;
my ($query, $gallery, $page, $dbh, $sth, $sql, $tmp, @cat, $mi, $i, $max_img, @img, @temp, $tl1, $tl2, $tl3, $tl4, $tl5, $np, $ii, $tl, $link, $list2, $n_row, $n_col, $list4, $list5, $iii, $url_img, $l_img, $l_ico);

Устанавливаем начальные переменные:

$url_img = "http://www.mysite.ru/gallery/";

Получаем переменные окружения:

$query = new CGI;
$gallery = $query -> param('gal');
$page = $query -> param('p');
$gallery = 0 if $gallery eq ''; # MySQL уходит в "штопор" если указывается пустое значение в условии запроса
$page = 1 if $page eq '';

Выбираем данные нашей фотогалереи:

$dbh = "DBI:mysql:mybase:localhost:3306";
$dbh = DBI->connect($dbh,'user','qwerty');
$sql = "SELECT name, titling, template, row_list, column_list FROM gallery_table WHERE id='$gallery'";
$sth = $dbh -> prepare($sql); $sth -> execute();
while ($tmp = $sth -> fetchrow_arrayref()) {
$cat[0] = $tmp->[0];
$cat[1] = $tmp->[1];
$cat[2] = $tmp->[2];
$cat[3] = $tmp->[3];
$cat[4] = $tmp->[4]
}
$sth -> finish();

Обрабатываем полученные данные:

$mi = $cat[3]*$cat[4]; # количество фотографий на странице
$i = ($page-1)*$mi; # "стартовая точка" для LIMIT
@temp = split(/\n/,$cat[2]); # переводим шаблон в массив, так как его обработка ведется построчно

Выбираем количество фотографий фотогалереи:

$sql = "SELECT id FROM image_table WHERE category='$gallery'";
$sth = $dbh -> prepare($sql); $sth -> execute();
$max_img = $sth->rows;$sth->finish();

Выбираем список фотографий фотогалереи:

$sql = "SELECT id,name,image FROM image_table WHERE category='$gallery' LIMIT $i,$mi";
$sth = $dbh -> prepare($sql); $sth -> execute(); $i = 0;
while ($tmp=$sth->fetchrow_arrayref()) {
$img[0][$i] = $tmp->[0];
$img[1][$i] = $tmp->[1];
$img[2][$i] = $tmp->[2];
$i++}
$sth -> finish();$dbh -> disconnect();

Вырезаем из шаблона блоки:

foreach (@temp) {
if (/<!-- 1 -->/) {$_=~s/<!-- 1 -->//g; $tl1 .= $_; $_=''}
if (/<!-- 2 -->/) {$_=~s/<!-- 2 -->//g; $tl2 .= $_; $_=''}
if (/<!-- 3 -->/) {$_=~s/<!-- 3 -->//g; $tl3 .= $_; $_=''}
if (/<!-- 4 -->/) {$_=~s/<!-- 4 -->//g; $tl4 .= $_; $_=''}
if (/<!-- 5 -->/) {$_=~s/<!-- 5 -->//g; $tl5 .= $_; $_=''}}

Формируем блок количества страниц:

if ($mi eq 0) {$mi = 1} # проверка что-бы не было деления на 0
$np = int(($max_img - 1)/$mi)+1; # количество страниц
if ($np eq 1) {$tl1 = ''} # если всего одна страница то данный блок не выводим, точнее обнуляем
else {
for ($ii = 1; $ii < ($np + 1); $ii++) {
if ($ii eq ($page)) {$tl = $tl3} else {$tl = $tl2} # текущая/нетекущая страница
$link = "?gal=".$gallery."&p=".$ii;
#$A #$A $list2 .= $tl;
}
$tl1 =~s/<!-- 2_insert -->/$list2/gi;
}

Получаем количество строк:

$n_row = int(($i-1)/$cat[4])+1;

Формируем строки:

for ($ii = 0; $ii < $n_row; $ii++) {
if ($ii eq ($n_row-1)) {$n_col = $i - $ii*$cat[4]} else {$n_col = $cat[4]} # получаем количество ячеек в строке
$list5 = '';

Формируем ячейки:

for ($iii = 0; $iii < $n_col; $iii++) {
$id = $ii*$cat[4] + $iii;
$l_img = $url_img.$img[0][$id].".".$img[2][$id];
$l_ico = $url_img."i".$img[0][$id].".".$img[2][$id];
$tl = $tl5;
$tl =~s/%link_image%/$l_img/gi;
$tl =~s/%link_icon%/$l_ico/gi;
$tl =~s/%name_image%/$img[1][$id]/gi;
$list5 .= $tl;
}

Вставляем сформированные ячейки в строку:

$tl=$tl4;
$tl=~s/<!-- 5_insert -->/$list5/gi;
$list4 .=$tl;
}

Последняя обработка шаблона: вставляем сформированные строки и список страниц:

foreach (@temp) {
$_ =~s/<!-- 1_insert -->/$tl1/gi;
$_ =~s/<!-- 4_insert -->/$list4/gi;
$_ =~s/%name_category%/$cat[0]/gi;
$_ =~s/%title_category%/$cat[1]/gi;}

Выводим на экран:

print "Content-type: text/html; charset=windows-1251\n\n";print qq "@temp";exit;

и все... нет, конечно, еще нужно поработать с шаблономи и привести их в соответствующий вид, но это уже дело техники...

Томулевич Сергей (Phoinix) http://www.asit.ru/

 
Автор: Томулевич Сергей
 
Оригинал статьи: http://woweb.ru/publ/58-1-0-385