vorst.ru - Статьи о задачах возникающих при создании сайта и их решении
Статьи помеченные gallery

Комментарии

Как выводить комментарии с отступами?

На сайте оставили комментарий, вы ответили. Оппонент что-то еще уточнил, вы опять ответили. Цепочка из нескольких вопросов/ответов, которую нужно представить в виде лестницы.

  • Подскажите, как организовать комментарии на сайте?
    • Есть варианты. Каковы ваши требования?
      • Необходимо, чтобы юзер мог оставить комментарий, который будет размещен после модерации.
  • Добрый день! У меня такая же проблема.
    • Можно использовать следующий алгоритм ... [Ответить]

В каждом мини-диалоге участвуют двое. Если кто-то еще оставляет комментарий, то это новый мини-диалог. Вклиниться в мини-диалог стороннему комментатору нельзя.


Старт

Модуль для управления блогом

Новости, сообщения, описания - это все статьи, а статьи - это блог. Блог является важной составляющей любого сайта.

Блог настолько важен, что самая популярная CMS на планете WordPress - это блог.

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


Виджет

Как написать виджет для WordPress

Иногда производители предоставляют возможность покупателю самостоятельно выбрать на сайте комплектацию товара. В этом случае, при разработке сайта, нужно предусмотреть возможность показа текущей цены на видном месте (demo).

В WordPress для подобных задач используется область виджетов. Как писать виджет в WordPress подробно написано в кодексе. Но всегда есть нюансы.


Short-code

как написать short-code для WordPress

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

Должна быть возможность легко размещать такой вопросник в статье или на странице блога WordPress. Об этом была речь в предыдущем посте.

Для подобных задач в WordPress есть механизм short-code. При создании сайта или в процессе развития сайта в страницу или пост вставляется специальный код. Если есть зарегистрированные коды, страница проходит проверку. Если код обнаружен, то вызывается обработчик. Результатом работы обработчика является html код, который вставляется на место short-code.

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


Калькулятор

Плагин для WordPress подсчета цены по компонентам

Любой товар состоит из элементов. Довольно часто производитель, поставив задачу написать электронный магазин, позволяет выбирать комплектацию заранее, до производства. Выбирая комплектующие есть возможность сэкономить или наоборот, согласиться с утверждением "Я этого достоин(а)" и заказать по полной.

Например можно представить пиццерию, где в меню указана стоимость составляющих - лепешка, сыр трех сортов, помидоры двух сортов, колбаса 5 видов. Комбинируете вы сами и постоянно видите, как ваш выбор отражается на цене.

Но это отвлеченный пример, а был конкретный - Demo, Plugin.


Email

Как отправить письмо с сайта, чтобы оно не попало в спам

При создании сайта почти всегда предусматривают форму обратной связи.

Допустим у вас на сайте есть форма обратной связи и посетитель заполнил в ней поле Name и поле Email. Логично отправить сообщение с сайта от имени посетителя на адрес администратора сайта или, например, адрес отдела маркетинга.

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


Правила

rbac - выполнять действие или нет

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

Если вы Админ, то у вас есть общее разрешение create. Оно дает право добавлять данные к любым моделям. Если вы Автор, то такое разрешение давать не стоит. Автор не может, например, добавить новую рубрику.

Если нельзя дать общее разрешение, то нужно создать и выдать частное разрешение. Чтобы позволить Автору добавлять статьи, нужно создать разрешение createPost. Оно должно проверяться следующим, в случае неудачи при проверке разрешения create.

$auth->addChild($createPost, $create);

Чтобы Админ мог добавить статью, нужно, чтобы он имел разрешение на все, на что имеет разрешение Автор.

$auth->addChild($admin, $author);

Все отлично и логично. Четкая иерархия разрешений и ролей. Но, например, вы Автор и пытаетесь редактировать чужую статью. Действие не желательное. Как проверить, что статья не ваша? Или как проверить, что этот комментарий не к вашей статье и вам не стоит на него отвечать?


Разрешения

rbac - разрешения на доступ, роли для групп пользователей

Разработка бекенда сайта часто подразумевает ограничение прав при редактировании контента. Ролевая модель имеет иерархию. Сначала прав не много, потом чуть больше, наконец доступно все. Права (или разрешения) - это константы, которые связаны с конкретной ролью. Если за ролью закреплено разрешение, то действие может быть выполнено.

Проверяется разрешение просто, например:

if(Yii::$app->user->can('update', ['model' => $model])) {
  // code for model updating
}

То есть может быть выполнен некий код, если за ролью текущего Пользователя закреплено разрешение update. Если представить, что с каждой ролью связан массив текстовых констант - "create", "update" и прочее, то правило проверки становится очевидным.

Но при такой организации данных нельзя учесть иерархию разрешений. Нужна древовидная структура. Строится такое дерево с помощью менеджера авторизации.

Определим те разрешения, что были упомянуты в предыдущей статье.


Настройка RBAC

rbac - подключение ролевого доступа

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

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


Сортировка

Сортировка строк мышкой

Василий оставил комментарий в конце января

Напишите, пожалуйста, статью, как массово загружать изображения через ajax с возможностью сортировки перетаскиванием.

Речь о расширении yii2-byone-uploader, которое может загружать, сжимать, обрезать картинки для любой модели. Я писал о нем уже пару раз.

На тот момент этой функциональности не было. Собирался я долго. Но вот, наконец добавил требуемое. Теперь спешу написать несколько слов о процессе. Демо (при редактировании).


Кеширование

ускорение (оптимизация) работы сайта с помощью кеширования

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

Если текст поста меняется редко, желательно выполнять работу по подготовке страниц в "первый" просмотр и затем выводить уже готовые страницы.


Недвижимость

плагин для агентств недвижимости

Привлекательность WordPress в большом количестве тем оформления. Иллюзия легкости, с которой можно поменять тему, не дает покоя разработчикам. Сейчас на WordPress работают даже магазины.

Чтобы разобраться, как это возможно, я решил написать плагин для агенств недвижимости. Расширять структуру данных не хотелось - это уже будет не WordPress. Решил сохранять JSON-определения объектов недвижимости в поле post_content и преобразовывать эти определения "на лету" - Demo, GitHub


amoCRM

Как получить данные из amoCRM

Заказчик попросил вывести данные о сделках в таблицу в формате CSV. Данные должны быть максимально полными.

В том числе, необходимо вывести все возможные этапы сделки и даты, когда сделка переходила с этапа на этап. В amoCRM предусмотрена возможность определять этапы для каждой воронки продаж. Необходимо было, чтобы учитывались все этапы, всех воронок.

Ну и прочее - кто, когда, с кем, контакты клиента и, наконец, все комментарии к сделке.


Событие

пример события

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

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


Без перегрузки

Динамическая загрузка контента

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


Вспомнить все

Если читатель хочет вспомнить, что просмотрено и прочитано

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


Что к файлу

как добавить атрибут к загруженному файлу

Иногда важно определить дополнительную информацию для загружаемого файла. Например это может быть описание. В расширении yii2-byone-uploader возможность добавить описание к файлу уже предусмотрена.

Кроме описания может быть добавлено и любое другое свойство. В предыдущем примере использовалось свойство when типа radio с двумя значениями - "до" и "после". Как добавить новое свойство?


Загрузка фото

делаем демо

Чтобы показать, как работает загрузка изображений, нужно разрешить загружать изображения на свой сайт. Но, в этом случае, дисковое пространство может быть быстро исчерпано и нужно как-то удалять оставленный посетителями "мусор".

Например, можно устроить, что-то вроде надписи в столовой - "Уберите, пожалуйста, за собой посуду" (демо).


Pop-up форма

редактирование и валидация модальной формы

Бывает, что количество полей в таблице не слишком велико и редактировать в модальном окне проще и нагляднее. Что необходимо, чтобы организовать стандартный набор операций - CReate, Update, Delete?


Доступ к Google

редактирование таблиц Google из приложения

В предыдущей статье уже была ссылка на работающий пример.

Следующим шагом стало выделение процесса аутентификации на сервисе Google в отдельный класс. А это, в свою очередь, позволило сделать расширение, немного упрощающее решение задачи.



Поиск



Комментарии

Как выводить комментарии с отступами?

На сайте оставили комментарий, вы ответили. Оппонент что-то еще уточнил, вы опять ответили. Цепочка из нескольких вопросов/ответов, которую нужно представить в виде лестницы.

  • Подскажите, как организовать комментарии на сайте?
    • Есть варианты. Каковы ваши требования?
      • Необходимо, чтобы юзер мог оставить комментарий, который будет размещен после модерации.
  • Добрый день! У меня такая же проблема.
    • Можно использовать следующий алгоритм ... [Ответить]

В каждом мини-диалоге участвуют двое. Если кто-то еще оставляет комментарий, то это новый мини-диалог. Вклиниться в мини-диалог стороннему комментатору нельзя.


    Поделиться

Нужно, чтобы человек мог оставить отзыв, который можно было бы разместить, после модерации. Допустим, что комментарии могут оставлять только авторизованные пользователи и только во frontend.

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

У пользователя, оставившего комментарий, должен отобразиться ваш ответ во frontend и кнопка [Ответить].

При упомянутом ограничении, а именно - в диалоге участвуют только двое: администратор и комментатор, структура данных для хранения комментариев примерно следующая.

id integer
post_id integer
user_id integer
content text
status integer
last boolean
thread char(32)
created_at integer

Нить диалога

Каждый новый комментарий получает уникальное значение - код нити (thread). Ответный комментарий получает то же значение thread, что и начальный комментарий. Если отсортировать все комментарии с одинаковым значением thread по дате, то получим цепочку диалога.

content thread
Подскажите, как организовать комментарии на сайте? a1
Есть варианты. Каковы ваши требования? a1
Необходимо, чтобы юзер мог оставить комментарий, который будет размещен после модерации. a1
Добрый день! У меня такая же проблема. b2
Можно использовать следующий алгоритм ... b2

Вывод комментариев в backend

Вывод комментариев уступами можно выполнить с помощью стандартного Grid, если сохранить текущее значение thread и использовать его при выводе следующего комментария. Для сохранения используем внутреннюю переменную params класса yii\base\View.

<?= GridView::widget([
  'dataProvider' => $dataProvider,
  'filterModel' => $searchModel,
  'layout' => "{items}\n{summary}\n{pager}",
  'columns' => [
  ...
  [
    'attribute' => 'content',
    'value' => function($data) {
      // declare params for indention in a row,
      // using View class internal variable - params
      if(!isset($this->params['thread']))
        $this->params['thread'] = false;
      if($this->params['thread'] == $data->thread) 
        $this->params['indention'] .= '--';
      else {
        $this->params['thread'] = $data->thread;
        $this->params['indention'] = '';
      }
      return $this->params['indention'] . ' ' . $data->content;
    }
  ],
  ...

Вывод комментариев во frontend

Верстка во frontend, как правило, более изощренная и часто основана на bootstrap media object. Начинается вывод с frontend/views/post/view.php в котором где-то, после текста поста, присутствует следующий код:

<?php echo $this->render('_comments', [
  'post' => $model,
  'comments' => $model->comments,
]); ?>

Комментарии выводятся один за другим. Если у комментариев одинаковый thread, то нужно сделать отступ и напечатать текущий комментарий. И так далее, пока не изменится thread. Рекурсивно подобный процесс представить легче.

Рекурсия

Определим основной цикл вывода frontend/views/post/_comments.php. Как и в backend промежуточные переменные сохраняются во внутренней переменной params класса yii\base\View.

<?php
function close_thread($level) {
  while($level > 1) {
    $level--;
    echo '</div>';
  }
}
?>
<?php $i = 0; $this->params['comments'] = $comments; ?>
<ul class="media-list">
  <?php while($i < count($comments)): ?>
    <li class="media">    
    <?php echo $this->render('_comment', [
      'i' => $i, 
      'thread' => $comments[$i]->thread, 
      'level' => 0]); 
      $i = $this->params['comment_i'];
    ?>
    </li> <!-- .media / -->
  <?php endwhile; ?>
</ul>

При выводе текущего комментария frontend/views/post/_comment.php новый блок (отступ) выводится пока thread тот же, что и у предыдущего комментария. Если же thread изменился, то блоки закрываются.

<?php $comment = $this->params['comments'][$i]; ?>
<div class="media-left">
  <i class="fa fa-2x fa-user"></i>
</div>
<div class="media-body">
  <div class="media-content">
    <h4 class="media-heading"><?= $comment->author->name ?></h4>
    <div class="alert alert-info" role="alert"><?= $comment->content ?></div>
    <?php if($comment->canBeAnswered()): ?>
      <div class="text-right">
        <a href="#comment-area" data-comment-thread="<?= $comment->thread ?>" class="btn btn-info reply-btn">
          <?= \Yii::t('app', 'Reply') ?>
        </a>
      </div>
    <?php endif; ?>
  </div>
<?php 
$i++;  $this->params['comment_i'] = $i; 
if($i < count($this->params['comments'])) {
  $comment = $this->params['comments'][$i];
  if($thread == $comment->thread) {
    $level++;
    echo '<div class="media-body">';
      echo $this->render('_comment', 
        ['i' => $i, 'thread' => $thread, 'level' => $level]);
  } else
    close_thread($level);
} else {
  close_thread($level);
}
?>

Право на ответ

Метод canBeAnswered() модели Comment вызывается во frontend при определении права текущего пользователя отвечать на последний комментарий.

Пользователь имеет право ответить на комментарий, если:

  • Пользователь авторизован;
  • Пользователь является инициатором мини-диалога;
  • Это последний комментарий в мини-диалоге;
  • И последний комментарий принадлежит модератору.


public function canBeAnswered() {
  $countInThread = Comment::find()
    ->where(['thread' => $this->thread, 'user_id' => \Yii::$app->user->id])
    ->count();
  return !\Yii::$app->user->isGuest &&
    $this->last && // this is a last comment in a thread
    \Yii::$app->user->identity->group == User::GROUP_COMMENTATOR && // you are a commentator
    $this->user_id != \Yii::$app->user->id && // last comment not yours
    $countInThread > 0; // you begin this thread
}

Как это работает во frontend, можно посмотреть на сайте. О backend подробнее в расширении sergmoro1/yii2-blog-tools.

Заключение

Комментарии в расширении sergmoro1/yii2-blog-tools могут оставлять только зарегистрированные пользователи. Комментарии должны модерироваться администратором сайта. Диалог возможен только между пользователем и администратором сайта.

Оставить комментарий

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