Гостевая книга на Битриксе на стандартных компонентах

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

Руководствуясь приведённой выше мыслью я решил сделать гостевую книгу на стандартных компонентах и API.

Необходимо: модуль "Формы", копипаст моего кода, доработка моего кода :)
Пример гостевой: http://nwcinema.ru/guest/

На странице гостевой будет располагаться форма добавления реплики (вопроса, жалобы и т.п.), а также лента вопросов-ответов с постраничной навигацией.

Форма добавления будет стандартной, лента - через API.

  1. Создаём форму:
    1. Сервис -> Веб-формы -> Настройка форм -> Создать
    2. Имя, описание и т.п. - на ваш вкус
    3. Шаблон я использовал свой (необходимо было реализовать возможность "неотображения" вопроса в ленте).
      Вот код:
      1. <?=$FORM->ShowFormDescription("")?> <?=$FORM->ShowFormNote()?><?=$FORM->ShowFormErrors()?>
      2. <br />
      3.  
      4. <table style="padding-bottom: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; border-collapse: collapse; " border="0" cellspacing="0" cellpadding="0" width="100%">
      5. <tbody>
      6. <tr> <td style="padding-bottom: 15px; padding-right: 20px; " valign="top" width="15%" align="right"><?=$FORM->ShowInputCaption("NAME","")?></td> <td><?=$FORM->ShowInput('NAME')?> </td> </tr>
      7.  
      8. <tr> <td style="padding-bottom: 15px; padding-right: 20px; " valign="top" align="right"><?=$FORM->ShowInputCaption("CITY","")?></td> <td><?=$FORM->ShowInput('CITY')?></td> </tr>
      9.  
      10. <tr> <td style="padding-bottom: 15px; padding-right: 20px; " valign="top" align="right"><?=$FORM->ShowInputCaption("EMAIL","")?></td> <td><?=$FORM->ShowInput('EMAIL')?></td> </tr>
      11.  
      12. <tr> <td style="padding-bottom: 15px; padding-right: 20px; " valign="top" align="right"><?=$FORM->ShowInputCaption("MESS","")?></td> <td><?=$FORM->ShowInput('MESS')?> </td> </tr>
      13.  
      14. <tr><td style="padding-bottom: 15px; padding-right: 20px; " valign="top" align="right"><?=$FORM->ShowInput('PRIVATE')?></td><td valign="top"><?=$FORM->ShowInputCaption("PRIVATE","")?> </td></tr>
      15.  
      16. <tr><td style="padding-bottom: 15px; padding-right: 20px; " valign="top" align="right">Введите текст на картинке<?=$FORM->ShowRequired()?></td><td valign="top"><?=$FORM->ShowCaptchaImage()?>
      17. <br />
      18. <?=$FORM->ShowCaptchaField()?></td></tr>
      19. </tbody>
      20. </table>
      21.  
      22. <br />
      23. <?=$FORM->ShowSubmitButton("","")?>
  2. Добавляем вопросы на форму:

    Сообщение - textarea, "Я не хочу..." - checkbox, остальные - text

  3. Добавляем статус формы
  4. Создаём страничку с гостевой (в моём случае - /guest/index.php), вот код:
    1. <?
    2. require($_SERVER["DOCUMENT_ROOT"]."/bitrix/header.php");
    3. $APPLICATION->SetTitle("Жалобная книга");
    4. ?>
    5. <p>
    6. <?php /* Вставляем нашу формочку через компонент, не забываем поправить "WEB_FORM_ID", поставив свой ID формы */ ?>
    7. <?$APPLICATION->IncludeComponent("bitrix:form.result.new", "comments", Array(
    8. "WEB_FORM_ID" => "1",
    9. "IGNORE_CUSTOM_TEMPLATE" => "N",
    10. "USE_EXTENDED_ERRORS" => "N",
    11. "SEF_MODE" => "N",
    12. "SEF_FOLDER" => "/guest/",
    13. "CACHE_TYPE" => "N",
    14. "CACHE_TIME" => "3600",
    15. "LIST_URL" => "",
    16. "EDIT_URL" => "",
    17. "SUCCESS_URL" => "",
    18. "CHAIN_ITEM_TEXT" => "",
    19. "CHAIN_ITEM_LINK" => "",
    20. "VARIABLE_ALIASES" => array(
    21. "WEB_FORM_ID" => "WEB_FORM_ID",
    22. "RESULT_ID" => "RESULT_ID",
    23. )
    24. )
    25. );?></p>
    26.  
    27. <?php
    28. $f = array( );
    29.  
    30. if ( CModule::IncludeModule("form") ) {
    31.  
    32. /* Получаем ленту сообщений, единичка - ID нашей формы */
    33. CForm::GetResultAnswerArray(
    34. 1,
    35. $f,
    36. $f,
    37. $answers,
    38. array(
    39.  
    40. )
    41. );
    42.  
    43. if ( count( $answers ) ) {
    44. $rez = array( );
    45. foreach ( $answers as $id => $f ) {
    46. $x = CFormResult::GetByID( $id );
    47. if ( $x && ($x = $x->GetNext()) && ($d = date_parse( $x['DATE_CREATE'] )) ) {
    48.  
    49. $x = array( 'DATE_CREATE' => date(
    50. 'd.m.Y H:i:s',
    51. (mktime(
    52. $d['hour'],
    53. $d['minute'],
    54. $d['second'],
    55. $d['month'],
    56. $d['day'],
    57. $d['year']
    58. ) + 7*60*60)
    59. ) );
    60.  
    61. foreach ( $f as $s => $v ) {
    62. $v = $v[0];
    63.  
    64. $x[ $s ] = ( $v['VARNAME'] == 'PRIVATE' ? $v['VALUE'] : $v["USER_TEXT"] );
    65. }
    66.  
    67. if ( ! $x['PRIVATE'] )
    68. $rez[ $id ] = $x;
    69. }
    70. }
    71.  
    72. /* Сортировать будем DESC по ID */
    73. krsort( $rez );
    74.  
    75. $p = $_GET['p'] > 0 ? $_GET['p'] : 1;
    76. $onP = 10; // количество вопросов в ленте на странице
    77. $elz = count ( $rez );
    78.  
    79. if ( $elz > $onP ) {
    80. $rez = array_slice( $rez, ( $p > 1 ? ($p * $onP - $onP) : 0 ), $onP, 1 );
    81. }
    82.  
    83. /* Выводим ленту */
    84. echo '<h2>Лента сообщений</h2>';
    85. foreach ( $rez as $v ) {
    86. echo '<p><b>'.$v["NAME"].'</b>'
    87. . ( $v['CITY'] ? ' (' . $v["CITY"] . ')' : '' ) . '<br />';
    88. echo '<span style="color:#aaa">'.$v["DATE_CREATE"].'</span><br />';
    89. echo
    90. str_replace(
    91. array(
    92. '<r>',
    93. '</r>'
    94. ),
    95. array(
    96. '<p class="reply">',
    97. '</p>'
    98. ),
    99. $v["MESS"]
    100. )
    101. . '<div class="line_1" style="margin: 5px 0px 0px 0px; padding: 1px;"></div></p>';
    102. }
    103.  
    104. /* Выводим постраничную навигацию */
    105. if ( $elz > $onP ) {
    106. $pages = ceil( $elz / $onP );
    107. $rez = array();
    108. foreach ( range( 1, $pages ) as $v ) {
    109. $rez[] = $p == $v
    110. ? "<b>$v</b>"
    111. : "<a href='?p=$v'>$v</a>";
    112. }
    113. echo '<p><font class="text"> ' . ($p * $onP - $onP + 1) . ' - ' . ( $p * $onP > $elz ? $elz : $p * $onP ) . ' из ' . $elz . '<br></font>
    114. <font class="text">
    115. Страницы: ' . join( ' ', $rez ) . '
    116. </font></p>';
    117. }
    118. }
    119.  
    120. }
    121. ?>
    122. <?require($_SERVER["DOCUMENT_ROOT"]."/bitrix/footer.php");?>
    В коде есть комментарии, не забудьте отредактировать под себя.

  5. Ключевой момент - ответы на вопросы
    1. Заходим в результаты формы
    2. Заходим в редактирование вопроса, на который нужно дать ответ. В тегах <r><r> Вводим ответ

  6. Добавляем css-правило и радуемся жизни :)
    1. p.reply {
    2. color: #C2097F;
    3. /* font-family: arial;
    4. font-size: 0.8em; */
    5. margin:10px 0 10px 30px;
    6. }

Часы, да чтобы тикали :)

Иногда необходимо вывести дату и время яваскриптом, например так:

Задача тривиальная, но, всё же, вот код:
  1. <script language="JavaScript"><!--
  2. document.write ((function(d){
  3. return(
  4. ( d.getDate() > 9 ? d.getDate() : '0' + d.getDate() ) + ' ' +
  5. ['Января','Февраля','Марта','Апреля','Мая','Июня','Июля','Августа','Сентября','Октября','Ноября','Декабря'][d.getMonth()] +
  6. ' ' + d.getFullYear() + ' г. ' +
  7. ( d.getHours() > 9 ? d.getHours() : '0' + d.getHours() ) + ':' + ( d.getMinutes() > 9 ? d.getMinutes() : '0' + d.getMinutes() )
  8. );
  9. })(new Date()));
  10. --></script>

Чтобы тикали:
  1. <div id="beautyTimeThisTag">here will be datetime</div>
  2. <script language="JavaScript"><!--
  3. setInterval( function() {
  4. document.getElementById( 'beautyTimeThisTag' ).innerHTML = ((function(d){
  5. return(
  6. ( d.getDate() > 9 ? d.getDate() : '0' + d.getDate() ) + ' ' +
  7. ['Января','Февраля','Марта','Апреля','Мая','Июня','Июля','Августа','Сентября','Октября','Ноября','Декабря'][d.getMonth()] +
  8. ' ' + d.getFullYear() + ' г. ' +
  9. ( d.getHours() > 9 ? d.getHours() : '0' + d.getHours() ) + ':' + ( d.getMinutes() > 9 ? d.getMinutes() : '0' + d.getMinutes() )
  10. );
  11. })(new Date()));
  12. }, 1000 );
  13. --></script>

Ресайз картинок на лету (для Битрикса и не только)

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

Так вот, неоднократно мне понадобилось обжимать картинки на Битрикс-сайтах.
Встроенный CFile::ShowImage() всего-навсего задаёт width и height у картинок, что, в большинстве случаев, неприемлемо. Так и был придуман этот незаурядный способ ресайза картинок на лету.

Итак, по порядку:
  1. Создаём папку resized в корневой директории сайта (mkdir resized)
  2. Создаём resize.php в корне, со следующим содержимым:
    1. <?php
    2. if ( $_GET['f'] && ($_GET['x'] && $_GET['x'] <= 1440) ) {
    3. createThumb( '.' . base64_decode( $_GET['f'] ), ('./resized/' . $_GET['f'] . '>' .$_GET['x']), $_GET['x'] );
    4. }
    5. function createThumb($resource, $dest, $x_scale) {
    6. $check = GetImageSize ( $resource );
    7. $y_scale = floor ( $x_scale / ( $check [0] / $check [1] ) );
    8. $im_dest = imagecreatetruecolor ( $x_scale, $y_scale );
    9. if ($check [2] == IMAGETYPE_JPEG) {
    10. $im_src = ImageCreateFromJPEG ( $resource );
    11. } elseif ($check [2] == IMAGETYPE_GIF) {
    12. $im_src = ImageCreateFromGIF ( $resource );
    13. } elseif ($check [2] == IMAGETYPE_PNG) {
    14. $im_src = imagecreatefrompng ( $resource );
    15. }
    16. if ($im_src && ImageCopyResampled ( $im_dest, $im_src, 0, 0, 0, 0, $x_scale, $y_scale, $check [0], $check [1] )) {
    17. if ($check [2] == IMAGETYPE_JPEG) {
    18. header('Content-type: image/jpeg');
    19. return ImageJPEG ( $im_dest, $dest, 99 ) && ImageJPEG ( $im_dest, null, 99 );
    20. } elseif ($check [2] == IMAGETYPE_GIF) {
    21. header('Content-type: image/gif');
    22. return ImageGIF ( $im_dest, $dest, 99 ) && ImageGIF ( $im_dest, null, 99 );
    23. } elseif ($check [2] == IMAGETYPE_PNG) {
    24. header('Content-type: image/png');
    25. return imagepng ( $im_dest, $dest, 99 ) && imagepng ( $im_dest, null, 99 );
    26. }
    27. }
    28. return 0;
    29. }
    30. ?>
  3. Редактируем .htaccess (или создаём, если его нет):
    1. <IfModule mod_rewrite.c>
    2. RewriteEngine On
    3. RewriteCond %{REQUEST_FILENAME} !-f
    4. RewriteRule ^resized/([a-zA-Z0-9=]+)>([0-9]{1,3})$ /resize.php?f=$1&x=$2 [QSA,L]
    5. </IfModule>
  4. Добавляем хелпер в /bitrix/header.php (или другой файл, который инклудится везде):
    1. <?php
    2. // Bitrix line
    3. require_once($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog.php");
    4. // Our helper
    5. function resizeHelper( $path, $x, $addi = '' ) {
    6. $path = '/resized/'
    7. . base64_encode(
    8. ((string)(float)$path === (string)$path)
    9. ? CFile::GetPath( $path )
    10. : $path
    11. )
    12. . '>' . $x;
    13. return '<img src="' . $path .'" width="' . $x . '"' . $addi . '/>';
    14. }
    15. ?>
  5. Вот и всё :)

Параметры resizeHelper():
  1. $path - путь к картинке или её Битрикс ID (что обычно и возвращается из инфоблока);
  2. $x - необходимая ширина картинки (высота вычисляется автоматически, пропорционально ширине);
  3. $addi - дополнительные параметры тега <img/>, которые будут просто вставлены (например, style="border:1px solid #666;padding:2px;margin-right:15px").

В папке /resized сохраняются картинки с новыми размерами (для каждого размера картинки - отдельный файл) - при повторном запросе, будет возвращена уже уменьшенная картинка (для этого-то мы и редактировали .htaccess).

Пример использования:
  1. <?php
  2. CModule::IncludeModule("iblock");
  3. // Bitrix API GetList()
  4. $x = CIBlockElement::GetList(
  5. array(
  6. 'RAND' => 'ASC',
  7. ),
  8. array(
  9. 'IBLOCK_ID'=> 8,
  10. '>=DATE_ACTIVE_TO' => ConvertTimeStamp(
  11. mktime(
  12. 0, 0, 0,
  13. date( 'n' ),
  14. date( 'j' )
  15. ),
  16. 'FULL'
  17. ),
  18. '!PROPERTY_IMAGE_LEFT_VALUE' => 'false',
  19. //'ID' => 69
  20. ),
  21. false,
  22. array(
  23. //'nTopCount' => 1
  24. ),
  25. array(
  26. 'IBLOCK_ID',
  27. 'PROPERTY_image_left',
  28. 'NAME',
  29. 'PREVIEW_TEXT',
  30. 'DETAIL_PAGE_URL',
  31. 'PREVIEW_PICTURE'
  32. )
  33. );
  34. // Iterate over it
  35. while ( $x && ($s = $x->GetNext()) ) {
  36. if ( ($i = $s['PROPERTY_IMAGE_LEFT_VALUE']) or ($i = $s['PREVIEW_PICTURE']) ) {
  37. // Below is out helper: put image $i (int or str), 330px wide, with border-styling (style="")
  38. ?>
  39. <div><a href="<?=$s['DETAIL_PAGE_URL']?>"><?=resizeHelper($i, 330, 'style="border: 5px solid #fff;"')?></a></div>
  40. <div class="info">
  41. <h2><?=$s['NAME']?></h2>
  42. <p>
  43. <?=$s['PREVIEW_TEXT']?> <br><br>
  44. <a class="more_1" href="<?=$s['DETAIL_PAGE_URL']?>">подробнее</a>
  45. </p>
  46. </div>
  47. <?
  48. break;
  49. } else {
  50. continue;
  51. }
  52. }
  53. ?>