Drupal в рунете

Подписка на Лента Drupal в рунете
Drupal (Друпал) — голландское "druppel" («капля») в английском произношении. Drupal — платформа для создания сайтов и управления ими (CMF). Посредством данной системы можно строить сайты различного назначения и сложности, для управления созданным на Drupal сайтом не требуется владение вебвёрсткой и знание программирования. Drupal создан международной командой разработчиков, как программное обеспечение с открытыми исходными текстами, защищенными общественной лицензией GNU. Международный сайт проекта — drupal.org, а наш сайт предназначен для общения и координации работ русскоговорящих участников проекта. Сообщества проекта Drupal доступны также на других языках.
URL-адрес: https://drupal.ru
Обновлено: 2 часа 29 мин. назад

Множественные уязвимости в contib модулях Drupal 7.x

ср, 13/07/2016 - 01:40

Команда безопасности выпустила уведомление DRUPAL-PSA-2016-001.

Это предупреждение о том, что завтра в 16:00 по UTC времени (19:00 по Московсокму времени) будет уведомление о уязвимостях вызова PHP кода в contrib модулях.

Судя по тому, что модули, на которые намекают, установлены на 1,000 до 10,000 сайтах, модули не очень популярные.

Рекомендуем завтра по получению уведомления проверить свои сайты и если необходимо - обновится.

Версия Drupal: Drupal 7.* Форумы: Безопасность4 Спасибо
Категории: Друпалургия

Создание своей темы в backdrop cms

вс, 10/07/2016 - 13:52

​​​​Темы в backdrop создаются почти точно так же как и в drupal 7, за исключением некоторых приятных моментов.

Разработчики backdrop создали модуль layout, который генерирует шаблоны страниц и размножать шаблоны page.tpl.php в папке templates темы для кастомизации не обязательно.

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

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

Стоп!

А где же тема?

Создаём тему

Назовём её light

Создаём директорию light

В директории light создаём файл light.info следующего содержания

name = Light
description = The started theme for Backdrop CMS.
version = BACKDROP_VERSION
type = theme
backdrop = 1.x
stylesheets[all][] = css/style.css
scripts[] = js/script.js

project = backdrop
version = 1
timestamp = 100000000

Почти как в drupal 7 ))

Ой, вы не увидели регионы как это было  в семерке.

Здесь это в принципе и не нужно.

Выше было упоминание за layout. Это его заслуга.

Далее как обычно в семерке вы создаете папки css, js, images, templates

В папке css располагаете файлы css

В папке js файлы js

В папке templates шаблоны.

Вам могут пригодиться шаблоны node.tpl.php и comment.tpl.php. Можно скопировать их из ядра backdrop.

Если нужно различное html отображение для тизера и полной ноды, то в node.tpl.php можно прописать проверку

<?php if($teaser): ?>

разметка тизера ноды

<?php endif; ?>

 <?php if(!$teaser): ?>

разметка полной ноды

<?php endif; ?>

Добавление кастомных блоков в регионы в backdrop реализованы немного иначе. Страницы отображения блоков здесь нет. Они отображаются в настройках layout

Вы расставляете блоки в макете как вам нужно, добавляете нужную html разметку в макете через добавление custom block и останется только прописать стили css.

Благодаря плагину в браузере firefox firebug

писать стили будет удобно.

Вы можете пойти другим путём и выбрать одну из тем, предоставляемых backdrop и изменять её

Но со своей темой будет всё же проще.

Всё это поможет реализовать вам свой уникальный дизайн ваших веб-страничек, созданных с помощью программного обеспечения Backdrop CMS

Оригинал заметки http://mihail.space/post/sozdanie-temy-v-backdrop-cms

Версия Drupal: Backdrop 1.4.x Форумы: Дизайн и вёрстка4 Спасибо
Категории: Друпалургия

Вышел Drupal 7.50 (следующий после 7.44) и Drupal 8.1.3

вс, 10/07/2016 - 06:02

7 июля прошло обновление Drupal 7 на 7.50
а 15 Июня были обновления Drupal 7.44 и Drupal 8.1.3

Обновления Drupal 7.44 и Drupal 8.1.3 были обновлениями по безопасности - смотрите https://www.drupal.org/SA-CORE-2016-002

Теперь немного подробнее про Drupal 7.50
Самое заметное - это прыжок с 7.44 на 7.50.
Говорят, это сделано, чтоб показать важность и обьем изменений сделаных в релизе.

Коротко о новом:
- administer fields права доступа, для управления полями. Теперь не нужно быть администратором для управления полями.
- зашита от clickjacking. По сути защита от того, чтоб ваш сайт не мог работать в iframe на другом домене.
- поддержка emojis. Теперь можно вставлять и сохранять emojis.
- PHP 7 совместимость. Но я бы хотел предупредить что большая часть contrib модулей и тем для Drupal 7 или будут работать с ошибками или порождать WSOD. Если решитесь пробовать, не забудьте про бекапы. Они пригодятся.
- Улучшения производительности в случае если Drupal пробует найти файл которого больше нет в системе.
- изменения в robots.txt для разрешения поисковикам индексировать css и картинки.

Детальнее по релизу читайте в CHANGELOG

Недавно я писал про новых разработчиков: Fabian Franz (Fabianx) и Stefan Ruijsenaars (stefan.r)

Как видно, выбор был сделан верным и ребята стараются.
Fabian Franz (Fabianx) проявил интерес к недавним изменениям в BackdropCMS а именно ускорения работы тестов ядра.

В issue 2759197 идет портирование моего патча 1366 - который позволил в 2 раза ускорить тесты ядра BackdropCMS.

К сожалению в релиз 7.50 изменения не вошли но ожидается что они будут в 7.60.

Версия Drupal: Drupal 7.* 4 Спасибо
Категории: Друпалургия

Новые со-разработчики ядра Drupal 7 - Fabianx и stefan.r

пт, 24/06/2016 - 06:43

Сегодня, 23 Июня, Дрис обьявил о том, что 2 разработчика: Fabian Franz (Fabianx) и Stefan Ruijsenaars (stefan.r) были назначены ответственными за поддержку Drupal 7.

Ссылка на оригинал: https://groups.drupal.org/node/512271

Вольный перевод:

Ранее в этом году, я объявил, что ищу дополнительных разработчиков Drupal 7 для того, чтобы помочь неутомимому David Rothstein .

Большое спасибо всем, кто откликнулся на призыв: были старательные и квалифицированные заявки!

Я выбрал Fabian Franz (Fabianx) и Stefan Ruijsenaars (stefan.r) , которых David тоже предлагал.

Недавно я попросил их потвердить, своё согласие стать co-maintainers Drupal 7 , и я рад Вам сообщить, что они оба восторженно согласились!
Фабиан будет сопровождать Framework Manager Drupal 7, а Стефан будет менеджером версии Drupal 7, а так же Дэвид будет продолжать играть свою роль в курировании Framework Manager.

Фабиан у нас - из Германии, он старший инженер производства и технический руководитель на Tag1 Consulting.
Он всегда готов к вызовам, и является частью сообщества Drupal уже более 6 лет. Он уже был ответственнен за подсистему темизации, и он сделал инновационную работу с точки зрения масштабируемости и высокой производительности для Drupal (BigPipe!). Он вообще увлечён Open Source, с момента рождения GNU, которая, так случилось, была основана в день его рождения.

Стефан родом из Нидерландов, он фрилансер, и в настоящее время работает с бельгийскими правительственными клиентами. Он является частью сообщества Drupal с момента своего первого Drupalcon в Szeged в 2008 году, а недавно стал членом команды безопасности Drupal.

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

Пожалуйста, поприветствуем Fabian Franz (Fabianx) и Stefan Ruijsenaars (stefan.r) в рядах основных коммиттеров команды Drupal 7! (И если вам случится быть в Drupal Developer Days Милан, вы можете пожать руку Стефана лично! :-))

Dries Buytaert

PS: спасибо   andypost и  Director-cemetery за подготовку материала к публикации.

2 Спасибо
Категории: Друпалургия

Очередная уязвимость Друпал

пт, 17/06/2016 - 14:27

В ядре Друпала обнаружили очередную уязвимость - https://www.drupal.org/SA-CORE-2016-002 - любопытно, что она есть как в семерке, так и в восьмерке (я-то думал, что восьмерку переписали полностью), обновляйте в общем:)

2 Спасибо
Категории: Друпалургия

Видеоподкасты Drupalim и гости

чт, 09/06/2016 - 22:27

Всем привет! Мы с ребятами записываем в прямых эфирах мысли вслух. И эти мысли порой бывают интересные. Многие из вас уже видели наши выпуски здесь https://www.youtube.com/user/DrupalBY/videos или здесь http://podcasts.dru.io/.

И сегодня я пришёл не просто поприветствовать вас и похвалиться каналом. В следующий четверг в 17:30 (или около того) мы планируем следующий эфир. Это очень важно для нас выходить регулярно. Но даже не это главная новость. Новость в том, что мы приглашаем каждый раз Друпалера (желательно со стажем и с большой буквы). И 16 июня с нами будет GOR, Gor Martsen.

Гор обещал рассказать про его работу над ядром и новыми фичами. А так же пообещал мне ответить на многие вопросы, которые накопились к нему от друпал-комьюнити и от меня лично. И именно по этому я сегодня и здесь.

  • Если у вас есть вопросы к Егору, то задавайте их здесь в каментах или сообщите их мне лично в скайп(ptchom).
  • Если вы хотите побеседовать с нами в прямом эфире, то велкам в личку скайпа (ptchom).
  • Если у вас есть замечания, предложения и умные мысли, а я уверен, что есть, то всё недовольство и довольство принимается здесь, в личке, в каментах к видику и просто в любом друпал-чате.

Спасибо!

Ключевые слова: подкасты 7 Спасибо
Категории: Друпалургия

Интеграция Drupal с amoCRM. Часть 2

вт, 05/04/2016 - 19:22

Друзья, всем привет!

Сегодня расскажем о втором этапе нашего сотрудничества с amoCRM.

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

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

Наша цель - подружить маркетинг и продажи и помочь выстроить полноценную воронку от первого клика клиента до сделки. Каждый предприниматель должен знать стоимость лидов, затраты на привлечение нового клиента и самые эффективные источники клиентов.

Кратко пройдемся по функциональности модуля Widget, который состоит из 5 подмодулей:

1. Страница (pages) для статического отображения контента. Позволяет создавать виджеты на базе Drupal.
2. Trello. Интеграция amoCRM и трелло через Drupal. Позволяет создавать и отслеживать изменения на досках и в карточках.
3. Slack. Можно создать канал для сделки, где будут обсуждаться все актуальные вопросы по заключению контракта. Тут также можно присваивать сообщениям статусы, которые настраиваются в Drupal.
4. С 2GIS целых 2 вида интеграций! 1. Делает поиск в базе 2GIS по телефону или e-mail и подгружает информацию по клиенту в карточку. 2. Формирует список компаний по ключевым словами в Drupal и создает контакты и задачи в amoCRM для найденных компаний.
Для использования этих модулей необходимо приобрести ключ API справочных данных 2ГИС.
5. Mail. Позволяет отправлять письмо прямо из карточки контакта, сделки, компании. Планируется добавить возможность подставлять шаблоны типовых писем.

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

К тому же уже давно в открытом доступе ряд других модулей:

----
Спасибо за внимание и до новых встреч! Напоследок вопрос: а какие инструменты автоматизации у себя в компании используете вы? Отписываетесь в комментариях, будет интересно обсудить :)
Связаться с нами можно по почте hello@drupaljedi.com или через сайт.

Первая часть про интеграцию Drupal и amoCRM.

Тип материала: Предлагаю решениеВерсия Drupal: Drupal 7.*Ключевые слова: amoCRMCRMвиджетыМодули и темы: amoCRMamoCRM APIamoCRM Form Форумы: Разработка модулей4 Спасибо
Категории: Друпалургия

ubercart.ru требуется участие

чт, 18/02/2016 - 00:59

Разработчики и компании что занимаются созданием магазинов на основе ubercart.ru


Требуется ваше участие и взаимная организация по запуску сайта ubercart.ru

Ваши обсуждения и предложения жду тут: https://github.com/DrupalRu/ubercart.ru/issues

Чат для общения и самоорганизации тут: http://gitter.im/DrupalRu/ubercart.ru

Начальное обсуждение можно начать в этой ноде.

Из того, что я вижу что надо будет сделать на сайте:

  1. промо страница с описанием возможностей
  2. демо страница для попробовать и увидеть возможности
  3. перечень компаний и фрилансеров что участвовали в разработке ubercart.ru
  4. сборки от компаний и фрилансеров с указанием автора для заказа дополнительной доработки
  5. документация хелпы, для того чтоб клиент мог сам решать какие то проблемы (настройки, включение опций и тп)
  6. список рабочих сайтов с ubercart и отображением разработчика
1 Спасибо
Категории: Друпалургия

Drupal 8. Entity, мы с тобой одной крови

вт, 16/02/2016 - 03:29

– Все объекты Drupal 8 - Entity.
– Но ведь материалы, пользователи, таксономия, комментарии – они же уже были Entity еще в 7-ке.
– Все - значит все.

Как так?

Поля, блоки, меню, стили изображений, роли, вьюхи, фиды, языки, форматы… Стоп! Как это всё можно одной гребёнкой, ведь это совсем разные вещи. А дело в том, что Entity теперь тоже не так прост. Т.е. не так конкретен. Т.е. настолько абстрактен (а потому и вездесущ), что теперь еще сложнее сказать, что это. Но Барт Финстра (xano), который вроде как в этом понимает, говорит следующее:
Entites are self-contained units of complex data
Сущности – автономные (независимые) составляющие данных
Сущности – сути вещей

Ближе к телу

Entity делится на две категории: Контент и Конфигурацию (19 стр.). А описание возможностей идет через интерфейсы, которых море. Вот, например, как определяется нода (отсюда):

А вот общая структура, где эту ноду еще попробуй найди (нащелкал в PhpStorm Diagrams)

Главные выводы:

  • теперь не во всякий Entity можно пихать поля;
  • обычно контент хранится в базе, а конфиги в файлах;
  • если делаешь контент, то оберегай его от конфигов, их реально больше;
  • без кода грустно.
У вас есть Entity? Дайте два

Примеров реализации собственных Entity валом. Даже в  Examples есть content_entity_example и config_entity_example. По аналогии сделаем еще один, который будет такой же бесполезный, но короче. Встречайте ego.


<?php

namespace Drupal\ego\Entity;

use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;

/**
 * Defines the EgoContent entity.
 *
 * @ContentEntityType(
 * id = "ego_content",
 * label = @Translation("Ego Content entity"),
 * base_table = "ego",
 * entity_keys = {
 * "id" = "id",
 * "label" = "name",
 * "uuid" = "uuid"
 * },
 * )
 */
class EgoContent extends ContentEntityBase {

  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {

    $fields['id'] = BaseFieldDefinition::create('integer')
      ->setLabel(t('ID'))
      ->setDescription(t('Ego ID'));
    $fields['uuid'] = BaseFieldDefinition::create('uuid')
      ->setLabel(t('UUID'))
      ->setDescription(t('Ego UUID'));
    $fields['name'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Name'))
      ->setDescription(t('Ego Name'))
      ->setSettings(array(
        'max_length' => 100,
      ));

    return $fields;
  }
}?> CRUD макдак

Распиаренный CRUD проверим с помощью не менее распиаренной системы тестирования /admin/config/development/testing
<?php

<?php
namespace Drupal\ego\Tests\Entity;

use Drupal\ego\Entity\EgoContent;
use Drupal\examples\Tests\ExamplesTestBase;

/**
 * Tests of the Ego Content
 *
 * @group ego
 */
class EgoContentTest extends ExamplesTestBase{
  public static $modules = array('ego', 'block');

  public function testEgoContent() {

    $storage = \Drupal::entityTypeManager()->getStorage('ego_content');

    // C - create
    $ego = EgoContent::create();
    $this->assertNull($ego->id());
    $this->assertTrue($ego->isNew());
    $ego->save();
    $this->assertNotNull($ego->id());
    $this->assertFalse($ego->isNew());
    $ego2 = $storage->create(array('name'=>"GOD"));
    $this->assertEqual($ego2->name->value, "GOD");
    $ego2->save();

    // R - read
    $id = $ego->id();
    $ego3 = $storage->load($id);
    $ego4 = EgoContent::load($id);
    $this->assertEqual($ego->uuid->value, $ego3->uuid->value);
    $this->assertEqual($ego->uuid->value, $ego4->uuid->value);

    // U - update
    $ego->name = "Lalala";
    $this->assertEqual($ego->name->value, "Lalala");
    $ego->save();

    // D - delete
    $id = $ego->id();
    $id2 = $ego2->id();
    $ids = array($id, $id2);
    $ego->delete();
    $storage->delete(array($ego2));
    $result = $storage->loadMultiple($ids);
    $this->assertTrue(empty($result));

  }
}?>

Хотя, 30 с. на выполнение многовато, но в зеленый красит исправно.

Да, всё примитивно, но это не обучалка по созданию крутых Entity, а развлекательная статья. И дай бог, чтобы ты её и так доскролил. А что поинтересней можно глянуть здесь:

Поля

Сказать, что Entity повлияло на филды – ничего не сказать. Теперь их просто не узнать - вычислительные свойства, методы, все как в лучших домах.. Но о домах чуть позже.

<?php
// Drupal 7
$node->body[$langcode][0]['value'];

// Drupal 8
$node->body->value;
$node->tags[2]->target_id;
$ru_node = $node->getTranslation('ru');
$ru_node->language() == 'ru';
$ru_node->title->value = "Прывет!";
$node->field_link->url;
$node->field->getPropertyDefinitions();
$node->hasField('super_field');
$entity = $node->field_name->getEntity();
$node->body->getType();?>

Клёво! Что? В 7-ке есть Entity Metadata Wrapper, который тоже так умеет? Ладно, но вот сейчас будет точно бомба.

EntityFieldQuery (7) vs (8) EntityQuery

Задача: среди опубликованных материалов product и movies выбрать те, у которых в body есть слово discount, и отсортировать по убыванию.

Drupal 7:

<?php
$query = new EntityFieldQuery();
$query 
  ->entityCondition('entity_type', 'node')
  ->entityCondition('bundle', array('product', 'movies')) 
  ->propertyCondition('status', 1)
  ->fieldCondition('body', 'value', 'discount', 'CONTAINS')
  ->propertyOrderBy('created', 'DESC'); 

$result = $query->execute(); 
if (!empty($result['node'])) { 
  $nodes = node_load_multiple(array_keys($result['node']));
}
?>

Drupal 8:

<?php
$storage = \Drupal::entityTypeManager()->getStorage('node');
$query = $storage->getQuery();
$query
  ->Condition('type', array('product', 'movies'))
  ->Condition('status', 1)
  ->Condition('body', 'value', 'discount', 'CONTAINS')
  ->OrderBy('created', 'DESC');

$result = $query->execute();
$nodes = $storage->loadMultiple($result);
?>

Один Condition работает за троих (field, property, entity Condition)?! В чем секрет его успеха? Просто у него хороший менеджер. А работает он только за одного (угадай кого).
Еще пример conditions:

<?php
$ids = \Drupal::entityQuery('node')
          ->condition('title', 'About', 'STARTS_WITH')
          ->condition('created', 637200000, '>')
          ->execute();
?>

Пример AND и OR группировки условий:

<?php
$query = \Drupal::entityQuery('node')
  ->condition('status', 1)
  ->condition('changed', REQUEST_TIME, '<');

$group = $query->orConditionGroup()
  ->condition('title', 'cat', 'CONTAINS')
  ->condition('field_tags.entity.name', 'cats');

$nids = $query->condition($group)->execute();?>

Пример агрегации:

<?php
$query = Drupal::entityQueryAggregate('node');
$result = $query
  ->groupBy('type')
  ->aggregate('nid', 'COUNT')
  ->execute();?>

Пример выборки из блоков:

<?php
$ids = \Drupal::entityQuery('block')
  ->condition('plugin', 'aggregator_feed_block')
  ->condition('settings.feed', array_keys($entities))
  ->execute();
?> Эй, где мои field_tables?!

Теперь филды еще и лежат не сами по себе, а исключительно с типом entity для которого созданы. Поэтому поля, созданные в одних entity уже не получится использовать в других (но между bundle-ами одного entity пока можно). По этому поводу краткий перевод статьи Франческо Плацело (plach). Только перевод этой статьи изначально и предполагался, но потом меня понесло :)

***********************************

Ну да, теперь поля прикреплены типо к entity. Благодаря этому не будет лишней суеты, когда в запросе есть условие на несколько полей. А то, что теперь их нельзя использовать для разных entity, как по мне, даже хорошо. Вечно накалывался, когда пользовался этим.
Более того, Entity сейчас вообще хранятся как попало. Что-то в базе, что-то в файлах, что-то опять в базе, но сериализованно в blob. И это сделано специально для того, чтобы все пользовались Entity Query API. Эта классная штука, учитывающая кучу нюансов, в том числе кэширование и, главное, реализацию хранилища. Например, если это SQL, то entity reference связи превратятся в JOIN-ы. Короче, только отсутствие у хранилища реализации Entity Query API спасет вас от жесткого баттхерта, если вы сделаете запрос напрямую. И то, при этом нужно ограничиться только получением id, а остальное уже через специальные методы загрузки.

Ладно, в общем-то я хотел рассказать о схеме хранения.

Везде расставлены слухачи событий изменения, добавления или удаления полей к entity. Так что, если при этом не задеваются никакие данные, процесс обновления сработает быстро и четко (иначе уж сами разруливайте).
Вот 4 основных модели организации таблиц:

1. Простой entity:
| entity_id | uuid | bundle_name | label | … |

2. С поддержкой мультиязычности (Translatable):

| entity_id | uuid | bundle_name | langcode |
| entity_id | bundle_name | langcode | default_langcode | label | … |

3. С поддержкой редакций (Revisionable):

| entity_id | revision_id | uuid | bundle_name | label | … |
| entity_id | revision_id | label | revision_timestamp | revision_uid | revision_log | … |

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

| entity_id | revision_id | uuid | bundle_name | langcode |
| entity_id | revision_id | bundle_name | langcode | default_langcode | label | … |
| entity_id | revision_id | langcode | revision_timestamp | revision_uid | revision_log |
| entity_id | revision_id | langcode | default_langcode | label | … |

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

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

Типичное решение - денормализация данных. Т.е. добавим к таблице User еще два поля, для хранения количества записей и заголовка последней из них.

<?php
function active_users_entity_base_field_info(EntityTypeInterface $entity_type) {
  $fields = [];
  if ($entity_type->id() == 'user') {
    $fields['last_created_node'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel('Last created node')
      ->setRevisionable(TRUE)
      ->setSetting('target_type', 'node')
      ->setSetting('handler', 'default');
    $fields['node_count'] = BaseFieldDefinition::create('integer')
      ->setLabel('Number of created nodes')
      ->setRevisionable(TRUE)
      ->setDefaultValue(0);
  }
  return $fields;
}
?>

Поддержка редакций указана из-за того, что она есть у User, а значит и все её поля должны это делать. А вот если её не будет, то и флаг редакции поля просто проигнорируется.

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

<?php
public function onNodeCreated(NodeInterface $node) {
  $user = $node->getOwner();
  $user->last_created_node = $node;
  $user->node_count = $this->getNodeCount($user);
  $user->save();
}

protected function getNodeCount(UserInterface $user) {
  $result = $this->nodeStorage->getAggregateQuery()
    ->aggregate('nid', 'COUNT')
    ->condition('uid', $user->id())
    ->execute();

  return $result[0]['nid_count'];
}

public function onNodeDeleted(NodeInterface $node) {
  $user = $node->getOwner();
  // ох уж этот итальянец, все предусмотрел!
  if ($user->last_created_node->target_id == $node->id()) {
    $user->last_created_node = $this->getLastCreatedNode($user);
  }
  $user->node_count = $this->getNodeCount($user);
  $user->save();
}

protected function getLastCreatedNode(UserInterface $user) {
  $result = $this->nodeStorage->getQuery()
    ->condition('uid', $user->id())
    ->sort('created', 'DESC')
    ->range(0, 1)
    ->execute();

  return reset($result);
}
?>

Отличненько, теперь еще подкинем метод получения id активных пользователей.

<?php
public function getActiveUsers() {
  $ids = $this->userStorage->getQuery()
    ->condition('status', 1)
    ->condition('node_count', 0, '>')
    ->condition('last_created_node.entity.status', 1)
    ->sort('login', 'DESC')
    ->execute();

  return User::loadMultiple($ids);
}?>

Заметили, как Query API разобрался с зависимостями между таблицами User и Node и даже не вспотел?

Осталось только воспользоваться всем этим. У меня в друпал-кругах уже определенная репутация, поэтому пришлось делать через контроллер:

<?php
public function view() {
  $rows = [];
  foreach ($this->manager->getActiveUsers() as $user) {
    $rows[]['data'] = [
      String::checkPlain($user->label()),
      intval($user->node_count->value),
      String::checkPlain($user->last_created_node->entity->label()),
    ];
  }

  return [
    '#theme' => 'table',
    '#header' => [$this->t('User'), $this->t('Node count'), $this->t('Last created node')],
    '#rows' => $rows,
  ];
}
?> Какой смысл в друпале без общих филдов, я ухожу

Постой (я с тобой)! Если использование общего поля для разных entity является принципиальным, то можно это устроить с помощью трюка с псевдо-полями:

<?php
use Drupal\node\Entity\NodeType;
use \Drupal\Core\Entity\EntityInterface;
use \Drupal\Core\Entity\Display\EntityViewDisplayInterface;

/**
 * Implements hook_entity_extra_field_info().
 */
function my_module_entity_extra_field_info() {
  $extra = array();

  foreach (NodeType::loadMultiple() as $bundle) {
    $extra['node'][$bundle->Id()]['display']['my_own_pseudo_field'] = array(
      'label' => t('My own field'),
      'description' => t('This is my own pseudo-field'),
      'weight' => 100,
      'visible' => TRUE,
    );
  }

  return $extra;
}

/**
 * Implements hook_ENTITY_TYPE_view().
 */
function my_module_node_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode, $langcode) {
  if ($display->getComponent('my_own_pseudo_field')) {
    $build['my_own_pseudo_field'] = [
      '#type' => 'markup',
      '#markup' => 'This is my custom content',
    ];
  }
}?>

Версия Drupal: Drupal 8.*Ключевые слова: Entity 8 Спасибо
Категории: Друпалургия

Импорт данных в словарь таксономии из xls файла. Модуль для Drupal 7.

сб, 13/02/2016 - 00:48

Иногда возникает потребность в импорте большого объема данных на сайт. Как раз для этих целей был создан модуль по импорту терминов таксономии из xls файла для Drupal 7.

Источник - http://lib365.ru/drupal-7/16

Перейдем к запуску и настройке модуля.

  • Скачиваем архив и распаковываем в /sites/all/modules.
  • Библиотеку phpexcel (в корне модуля) перемещаем в sites/all/libraries.
  • Включаем модуль через админку сайта и настраиваем права доступа.
  • Переходим на страницу импорта данных /admin/config/services/tix.
  • Выбираем словарь таксономии и прикрепляем файл - он должен быть в формате .xls (Excel 2003). Структура файла должна быть следующая:
  • Первая строка (id, tag, count, desc) - это название наших столбцов, ниже - данные. Можете посмотреть пример файла в корне модуля (file-import-example.xls).

  • Если все правильно сделаем, должно получиться следующее:
  • Заметим, что если поле в термине таксономии обязательно, то это автоматически отображается на данной форме. Также, мы можем не указывать поле для импорта выбрав опцию "- Нет -".
    Если поставить галочку напротив "Очистить словарь", то все термины данного словаря будут удалены, а потом выполнится импорт, который учитывает имеющиеся термины.
    Проще говоря, если у нас уже есть термин, наличие которого определяется по имени, то мы выполняем его обновление. Если термина с таким именем не существует - добавляем его.

  • Выполняем импорт:

После импорта нас редиректит на страницу списка терминов для того словаря, по которому он выполнялся:

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

Тип материала: Предлагаю решениеВерсия Drupal: Drupal 7.*Ключевые слова: importCatalog taxonomyxls Форумы: Программирование5 Спасибо
Категории: Друпалургия

Уведомление о новых постах через Telegram Channel на Drupal 7

пт, 12/02/2016 - 21:00

Подписка по почте и rss - это неплохие вещи, но моему извращенному вкусу захотелось, чтобы извещение о новых постах приходили в групповой чат телеграмма.
Для этого был написан модуль, который использует telegram bot api для отправки новых уведомлений в наш чат. Модуль получился довольно простой. Буду рад адекватным предложениям по допилу :)

Ссылка на источник - http://lib365.ru/drupal-7/3

1. Создаем Telegram Bot

Прежде всего нам требуется создать Telegram Bot. Можно почитать как это делается на официальном сайте или посмотреть статью на русском языке от Blizzy. Вкратце опишу процесс регистрации нового бота:

  • Вводим в поиске Telegram @BotFather нажимаем кнопку "Start"
  • Выполняем команду /newbot и задаем имя бота
  • Вводим и отправляем наш "username" (обязательно в конце должно присутствовать слово "bot")
  • Если все выполнено правильно, Telegram вернет "token to access". Запомните его, он нам пригодится.

2. Создаем Telegram Chat

Переходим в desctop версию Telegram и создаем новый channel:

Заполняем данные:

Переходим в настройки чата и добавляем нашего созданного бота в админы:

На этом подготовительные процедуры закончены. Переходим непосредственно к модулю.

3. Telegram Chat модуль для Drupal 7
Модуль состоит из таких файлов:

telegram_item.info - описание
telegram_item.install - очистка таблицы от переменных модуля
telegram_item.module - основная логика работы
telegram_item_config.inc - страница администрирования
telegram_item.forms.inc - дополнительные элементы формы
C telegram_item.info, telegram_item.install, telegram_item_config.inc в принципе все понятно и это банальные вещи. (Если все-же возникли вопросы - пишите).

Разберем telegram_item.module:

<?php/**
 * Implements hook_module_implements_alter().
 * Перемещаем hook_insert нашего модуля в самый низ (после модуля pathauto)
 * Это нужно, чтобы получить alias для url ноды через модуль pathauto
 */
function telegram_item_module_implements_alter(&$implementations, $hook) {
  $module = basename(__FILE__, '.module');
  if(!empty($implementations[$module])){
    $group = $implementations[$module];
    unset($implementations[$module]);
    $implementations[$module] = $group;
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 * Присоединяем наши элементы формы к форме ноды
 */
function telegram_item_form_node_form_alter(&$form, &$form_state) {
  if (empty($form['#node']->type)) {
    return;
  }
  // проверяем, имеет ли пользователь право на публикацию в Telegram
  $user_access = user_access('telegram send message');

  if ($user_access) {
    // дополняем форму добавления/редактирования ноды
    form_load_include($form_state, 'forms.inc', 'telegram_item');
    __telegram_item_process_node_form($form);
  }
}

/**
 * Implements hook_node_insert().
 */
function telegram_item_node_insert($node) {
  __telegram_item_process_node($node);
}

/**
 * Implements hook_node_update().
 */
function telegram_item_node_update($node) {
  __telegram_item_process_node($node);
}

/**
 * Функция отправки сообщения в Telegram channel
 */
function __telegram_item_process_node($node) {
  $apikey = variable_get('telegram_item_apikey');
  $channel = variable_get('telegram_item_channel_name');
  $user_access = user_access('telegram send message');
  
  // проверяем, стоит ли отметка публикации ноды в Telegram
  $post_node = isset($node->telegram_item_post_this_node) ? $node->telegram_item_post_this_node : FALSE;

  if ($apikey && $channel && $user_access && $post_node) {
    global $base_url;
    // добавляем заголовок
    $message = $node->title . "\r\n";

    // добавляем url ноды
    $message .= $base_url . '/' . drupal_get_path_alias('node/' . $node->nid);

    $message = urlencode($message);
    __telegram_item_send_message($message, $apikey, $channel);
  }
}

function __telegram_item_send_message($message, $apikey, $channel) {
  // составляем запрос по всем правилам Telegram api
  $query = "https://api.telegram.org/bot" . $apikey .
    "/sendMessage?disable_web_page_preview=true&chat_id=" . $channel .
    "&text=" . $message;

  // выполняем наш запрос через curl
  $curl = curl_init();
  curl_setopt($curl, CURLOPT_URL, $query);
  curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
  curl_setopt($curl, CURLOPT_HEADER, 0);
  curl_exec($curl);
  curl_close($curl);
}?>

Как видите, все довольно банально. Самый смак в функции "__telegram_item_send_message($message, $apikey, $channel)".
Рассмотрим файл telegram_item.forms.inc, который дополняет нашу форму добавления/редактирования ноды:

<?php

/**
 * Добавляем чекбокс, который будет указывать стоит ли отсылать сообщение
 */
function __telegram_item_process_node_form(&$form) {
  // добавляем новый fieldset и задаем ему вес
  $form['telegram_item'] = array(
    '#type'   => 'fieldset',
    '#title'  => t('Telegram item'),
    '#group'  => 'additional_settings',
    '#weight' => -11,
  );

  $form['telegram_item']['telegram_item_post_this_node'] = array(
    '#type'          => 'checkbox',
    '#title'         => t('Send to Telegram'),
    '#description'   => t('After you submit this node it will be send to Telegram.'),
  );

  $apikey = variable_get('telegram_item_apikey');
  $channel = variable_get('telegram_item_channel_name');

  // отключаем элемент управления, если мы не настроили telegram в админке
  if (empty($apikey) && empty($channel)) {
    $form['telegram_item']['telegram_item_post_this_node']['#disabled']    = TRUE;
    $form['telegram_item']['telegram_item_post_this_node']['#value']       = 0;
    $form['telegram_item']['telegram_item_post_this_node']['#description'] =
      t("You can't Telegram nodes until apikey or channel is empty.");
  }
}
?>

4. Telegram Chat настройка и тестирование модуля
Устанавливаем наш модуль, включаем его в админке и переходим на страницу редактирования прав доступа /admin/people/permissions. Тут выставляем права на публикацию новости в Telegram и администрированию модуля:

Далее, переходим в страницу конфигурирования модуля /admin/config/services/telegram-item, вводим Bot apikey, Channel name, и сохраняемся. Я подвязал уведомление на свою группу.

Переходим в добавление какого-либо материала и публикуем новость, предварительно отметив галочку "Send to Telegram":

Смотрим результат в Telegram:

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

Тип материала: Предлагаю решениеВерсия Drupal: Drupal 7.*Ключевые слова: telegram_bot_apitelegram Форумы: Программирование5 Спасибо
Категории: Друпалургия

Что нового в Backdrop 1.3

ср, 10/02/2016 - 00:43

15 января 2016 года был анонсирован 4 релиз Backdrop CMS - 1.3.0.
Этот релиз так же отмечает 1 годовщину от первого официального релиза Backdrop CMS.

Новое в UI&UX

Этот релиз полон изменений завязанных на UI&UX - более 32 новых возможностей.

Некоторые из возможностей:

Новые упрощенные пользовательские блоки


В интерфейсе управления отображением страницы можно добавить пользовательский блок через "Добавить блок" -> "Добавить пользовательский блок".
Backdrop CMS теперь поддерживает блоки, существующие только для определенного Layout или же могут быть использованы для разных Layout.
Информация о данном типе блока хранится в JSON файле. Что позволяет переносить блоки при деплое как файлы, без необходимости вносить изменения в базу.

Упрощенное создание типа материала и словаря


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

Иконка уведомления


В предыдущих версиях Backdrop CMS, если сайт нуждался в обновлении или были критические проблемы, уведомление о необходимости обновится или проблемах занимало значительную часть экрана в административной части сайта. Это несло 2 проблемы: сообщение не появлялось на обычных страницах и было неудобно пользоваться административной частью. Для решения этих проблем была добавлена иконка в административный toolbar вверху экрана.

Улучшено Управление пользователями

1.3.0 релиз содержит в себе большое количество улучшений управления пользователями. Теперь можно настроить использовать имейл для авторизации на сайте в дополнение к логину или вместо. Валидация формы авторизации стала более информативной.
Пункт меню, ранее называвшийся People был переименован в User accounts. Настройку генерации пути для пользовательского профайла можно сделать сразу же в настройках пользователей. И что б избежать загруженности страницы настроек, имейлы регистрации, авторизация, пароли находятся каждый на своей странице.

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

В теме Sven основные закладки теперь полностью адаптивные к мобильным экранам. Очень приятная реализация.

И много других новых возможностей:
  • Настройки комментариев упрощены в форме типа материала.
  • Короткие и длинные текстовые поля могут использовать виджеты отображения друг друга.
  • Поля с большим значением количества элементов можно настроить на лимит более чем 10.
  • Административный toolbar был обновлен новыми названиями, позволяющие делать более эффективный поиск.
  • Настройки VIEWS теперь адаптивные и отображают список представлений в алфавитном порядке.
  • Терминология вокруг "Изменить" и "настроить" была обновлена для отображения сути.
Блок для полей

В Backdrop 1.3.0 уникальные поля материала или пользователя могут быть размещены в отдельный блок. Соответсвенно блоки доступны на node/% или user/% страницах и каждое поле отображается в своем блоке.

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

Транслитерация в ядре


Для того, чтоб разрешить проблемы, где позволительны только ASCII символы (например название таблиц в базе), Backdrop очень часто применяет machine name элемент. До версии 1.3.0 необходимо было ставить дополнительный модуль для преобразования в ASCII символы, теперь это часть ядра.

Регулярные задачи (cron) и кеширование страниц

Backdrop 1.3.0 включает в себя очень мощную новую возможность - фоновая обработка задач. В предыдущих версиях в конце запроса к странице - cron мог начать выполнять регулярные задачи и тяжелые задачи могли замедлить загрузку страницы. Как пример индексация или очистка кеша. Задержки на 5, 20 и более секунд очень частые. Теперь же Backdrop CMS отдает страницу стразу, не задерживая выдачу до завершения крон задач.

Кроме дополнения в обработку фоновых задач, для кеша страниц так же была добавлена задача "фоновое обновление". Эта функция очень похожа на поведение Varnish 4, когда запрос к странице кеш которой уже истек, получал старый кеш, пока формировался новый.

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

Возвращение черного (и других цветов)

Одним из заметных изменений в Backdrop 1.3.0 является инсталляция и тема по умолчанию, которая теперь в черных цветах вместо привычного синего. Это небольшое изменение нацелено на изменение бренда и отделение от Drupal.

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

Если вы выберите цветовую схему "Blue Lagoon" - вы получите привычный вам Bartik в голубых цветах.

Данный текст является вольным переводом оригинального текста написаного quicksketch

Версия Drupal: Drupal 7.*Ключевые слова: backdrop Форумы: Обзоры и сравнения CMS2 Спасибо
Категории: Друпалургия

Drupal 7 VS Drupal 8 производительность. Бонус: render_boost модуль

вт, 09/02/2016 - 08:09

Для начала начну с того, что после вот этого поста:
http://alexrayu.com/blog/drupal-739-vs-drupal-8-rc1-performance

Я был очень удивлен комментариям, процитирую:

Wim Leers написал:
You conveniently fail to mention two things in the blog post:

1. You compare Drupal 8's out-of-the-box performance with a tweaked Drupal 7: you enabled the page cache.
2. Drupal 7's page cache is inferior: a) no instantaneous updates; b) any node or comment posted causes the entire page cache to be cleared (!); c) many user actions that cause the page to change do not invalidate the page cache, so stale content continues to be sent.

In other words, Drupal 7's page cache:
- shows stale content, hence the site appears broken to the site owner
- is cleared in its entirety and thus far less effective

Hence this is an apples-to-oranges comparison.

A Drupal 8 site with the Page Cache enabled is functionally equivalent to a Drupal 7 site with Page Cache disabled. Make that comparison instead, and the chart will look vastly different.

Wim Leers написал:
Claiming every tutorial recommends this does not make this comparison correct. Only bad articles say "Enable page cache.", good articles say "Enable page cache, but know that it comes with disadvantages X, Y and Z." — i.e. they provide the necessary nuance.

If you simply refuse to compare things correctly or even call out the necessary nuances, fine, then there is nothing else I can say.

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

Окей. Давайте сделаем "правильно":
- установил Drupal 7.
- набил в него контента через devel generate
- мигрировал это все дело на drupal 8
- параметры хостинга: CPU E5-1650 v3 @ 3.50GHz, php_memory_limit = 512M, HW SSD RAID 5, Innodb database. xcache opcode. php-5.6

В итоге получилось 2 такие вот страницы:
http://drupal7.drupal-site.net/node/199
http://drupal8.drupal-site.net/node/199

Если вы посмотрите исходник HTML в самом конце вы увидите данные которые я буду приводить ниже.
Эти данные генерируются на уровне php, специально разработанным модулем php.

Тест 1. Cтраница материала 199 под авторизированым пользователем.
Drupal 7 сайт с отключеным кешированием. , авторизация под админом
Drupal 8 c включенным кешированием, авторизация под обычным пользователем

Drupal 7

        queries total:                  222
                select:                 221
                insert:                 0
                update:                 1
                other:                  0
        memory peak:                    5.25 Mb

        execution time :                130.570 ms
        queries time :                  7.706 ms
        utime:                          120.981 ms
        stime:                          6.000 ms
        oublock:                                0
        inblock:                                0
        profiler_time:                  18.943 ms
        profiler_time_before call:      14.068 ms
        profiler_time_after call:       4.875 ms

Drupal8

Summary:
        queries total:                  125
                select:                 125
                insert:                 0
                update:                 0
                other:                  0
        memory peak:                    16.75 Mb

        execution time :                152.216 ms
        queries time :                  1.403 ms
        utime:                          141.978 ms
        stime:                          7.999 ms
        oublock:                                0
        inblock:                                0
        profiler_time:                  18.522 ms
        profiler_time_before call:      14.900 ms
        profiler_time_after call:       3.621 ms

Итог: Drupal 8 150ms и Drupal 7 130ms
Напоминаю у Drupal 7 отключен кеш для "честного сравнения".

Теперь все тоже, но под анонимным пользователем.

Тест 2. эта страница под анонимным пользователем.
Drupal 7 сайт с отключеным кешированием.
Drupal 8 c включенным кешированием

Drupal 7

        queries total:                  181
                select:                 181
                insert:                 0
                update:                 0
                other:                  0
        memory peak:                    4.50 Mb

        execution time :                110.098 ms
        queries time :                  5.157 ms
        utime:                          99.985 ms
        stime:                          7.998 ms
        oublock:                                0
        inblock:                                0
        profiler_time:                  17.804 ms
        profiler_time_before call:      12.630 ms
        profiler_time_after call:       5.174 ms

Drupal 8

        queries total:                  3
                select:                 3
                insert:                 0
                update:                 0
                other:                  0
        memory peak:                    2.75 Mb

        execution time :                12.615 ms
        queries time :                  0.034 ms
        utime:                          9.999 ms
        stime:                          2.000 ms
        oublock:                                0
        inblock:                                0
        profiler_time:                  0.506 ms
        profiler_time_before call:      0.417 ms
        profiler_time_after call:       0.089 ms

Но при этом контент появился не сразу... и после разбирательств я нашел запрос на
http://drupal8.drupal-site.net/history/199/read

        queries total:                  23
                select:                 22
                insert:                 0
                update:                 1
                other:                  0
        memory peak:                    7.00 Mb

        execution time :                33.361 ms
        queries time :                  0.212 ms
        utime:                          24.996 ms
        stime:                          8.999 ms
        oublock:                                0
        inblock:                                0
        profiler_time:                  2.847 ms
        profiler_time_before call:      2.277 ms
        profiler_time_after call:       0.571 ms

Итог: Drupal 8 12+33 = 45ms и Drupal 7 110ms
Напоминаю у Drupal 7 отключен кеш для "честного сравнения".

А теперь попробуем небольшой модуль render_boost
По сути это кеш рендеринга. А значит данные актуальные при отображении.

Доступно для теста тут: http://rb.drupal7.drupal-site.net/node/199

Тест 1. Авторизированный.

        queries total:                  183
                select:                 182
                insert:                 0
                update:                 1
                other:                  0
        memory peak:                    5.00 Mb

        execution time :                61.199 ms
        queries time :                  8.722 ms
        utime:                          53.992 ms
        stime:                          1.999 ms
        oublock:                                0
        inblock:                                0
        profiler_time:                  7.444 ms
        profiler_time_before call:      4.981 ms
        profiler_time_after call:       2.463 ms

Тест 2. Анонимный

        queries total:                  141
                select:                 140
                insert:                 1
                update:                 0
                other:                  0
        memory peak:                    4.50 Mb

        execution time :                52.414 ms
        queries time :                  5.385 ms
        utime:                          47.993 ms
        stime:                          2.000 ms
        oublock:                                0
        inblock:                                0
        profiler_time:                  6.931 ms
        profiler_time_before call:      4.675 ms
        profiler_time_after call:       2.256 ms

Итого сделаем табличку результата:

Инсталляция                  тест 1 (авторизированый)     тест 2 (аноним)
Drupal 7 no cache               130ms                   110ms
Drupal8 cache enabled           150ms                   45ms
Drupal 7 + render boost         60ms                    52ms

Комментарии к цифрам теста: Я делал по несколько запросов, чтоб дать возможность разогреться opcode cache, drupal cache и render_boost кешу и выбирал лучшую цифру из нескольких запросов. Суть теста показать возможности системы кеширования Drupal 7, Drupal 8 и Drupal 7 + render_boost в приблизительных цифрах

Версия Drupal: Drupal 8.*Drupal 7.* 6 Спасибо
Категории: Друпалургия

Drupal 8, Cache & Rock N' Roll

пн, 08/02/2016 - 01:19

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

Ну и чо там?

Из Symfony в Drupal 8 привита всеобщая кэшируемость. Теперь для любого объекта, который участвует в рендеринге, можно задать свои параметры кэширования.

Выглядит так:

'#cache' => [
    'keys' => ['entity_view', 'node', 5, 'teaser'],
    'contexts' => ['languages', 'timezone'],
    'tags' => ["node:5", 'user:3'],
    'max-age' => Cache::PERMANENT,
  ],

Всего 4 параметра (указывать все необязательно):

keys – массив значений, из которых будет сформировано название для кэша (ID). Типичный набора:
'keys' => array( 'entity_view',  $this->entityTypeId,  $entity->id(),  $view_mode)

contexts –зависимость от эм.. контекста, например языка и временной зоны. Для тех, кто сечет в HTTP заголовках, в качестве его аналогии приводят Vary. Еще примеры возможных значений: cookies, route, session, url, user и т.д. Можно задавать более конкретную привязку, например user.permissions – зависимость только от прав пользователя, или url.query_args:foo – зависимость от параметра foo в адресной строке запроса.При их обработке происходит оптимизация. Например:
[user, user.permissions] => [user], т.к. зависимость от user уже включает в себя user.permissions.

tags – зависимость от конкретных объектов, например от 5-ой ноды, или пользователя с id = 3. Интересно, что можно задавать значения типа config:block_list (зависимость от конфигурации блоков), или config:filter.format.basic_html. А еще node_list.
В значениях тега не должно быть пробелов, т.к. пробел используются в качестве разделителя при передаче через header (странно, конечно, что выбран пробел, ну да не о том речь). Этот самый заголовок (X-Drupal-Cache-Tags) интересен для настройки супербыстрой раздачи через систему обратного проксирования (Varnish, CDN).

max-age – время хранения кэша в секундах (0 – не хранить, Cache::PERMANENT (-1) – без ограничения)

Не надо бла-бла, давай пример

Задача: разработать блок, который будет выводить значение параметра xxx переданного через адресную строку.

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


<?php

/**
 * @file
 * Contains \Drupal\cacheable_block\Plugin\Block\CacheableBlock.
 */

namespace Drupal\cacheable_block\Plugin\Block;

use Drupal;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Annotation\Translation;
use Drupal\Core\Routing;

/**
 * Provides a 'Cacheable Block' block.
 *
 * @Block(
 *   id = "cacheable_block",
 *   admin_label = @Translation("Cacheable block"),
 * )
 */
class CacheableBlock extends BlockBase {

  /**
   * {@inheritdoc}
   */
  public function build() {

    $key = 'xxx';
    $message = "";
    $value = Drupal::request()->query->get($key);

    if($value) {
      $time = date("Y-m-d H:i:s");
      $vars = array('@value' => $value, '@time' => $time);
      $message = $this->t('@value (@time)', $vars);
    }

    return array(
      '#markup' => $message,
      '#cache' => [
        'contexts' => ["url.query_args:$key"],
      ],
    );
  }
}
?>

Пример работы:

my.site?xxx=Kate                   -> Kate  (2016-02-07 11:11:11)
my.site?xxx=Ann                    -> Ann  (2016-02-07 22:22:22)
my.site/node/666?xxx=Ann           -> Ann  (2016-02-07 22:22:22)
my.site?xxx=Kate                   -> Kate  (2016-02-07 11:11:11)

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

Правда если зайти под другим пользователем, то будет другой кэш, хоть никакой зависимости от пользователей и не указано. И так и не понял, как задать
'cache' => DRUPAL_CACHE_GLOBAL

Но, можно делать свой кэш с нужным значением и ключами:

<?php
$value = Drupal::request()->query->get($key);
$message = Drupal::cache()->get("cacheable_block-$key-$value")->data;
if($value && !$message) {
  $time = date("Y-m-d H:i:s");
  $vars = array('@value' => $value, '@time' => $time);
  $message = $this->t('@value (@time)', $vars);
}
Drupal::cache()->set("cacheable_block-$key-$value", $message);
?>

Помни! Если кто-то вобьёт мильярд разных вариантов xxx – ничем хорошим это не закончится. Прямая зависимость кэша от столь легко меняющейся (и бесконечновариантной) зависимости – крайне не рекомендуется.

Еще пример

В блоге Acquia Dev есть пример, в котором рассматривается конвертация изображения в asci-символы. Вот краткая выжимка:

Как тебе такое решение?

<?php
$build = array(
  '#theme' => 'ascii_art',   
  '#attributes' => array('class' => 'ascii-art'),   
  '#caption' => 'My favorite animal',   
  'content' => array(
    '#markup' => generate_ascii_art('llama.png')
  ), 
);
?>

Здесь клёвая картинка ламы будет преобразована в ASCII символы. И, будь уверен, преобразование просто прекрасно! Но сейчас разговор не об искусстве ASCII. Динамическая генерация данных на основе изображения, разве это не медленно, спросишь ты. Ясен день! В конце-то концов, как следует преобразовать пушистую ламу в набор символов не так-то и просто. Нет, без шуток, можно придумать кучу случаев затратного рендеринга (требующего сложных вычислений и кучи запросов к бд). Так что кэширование результатов здорово бы помогло. И вот как это можно устроить:

<?php
$build = array(
  '#theme' => 'ascii_art',
  '#attributes' => array('class' => 'ascii-art'),
  '#caption' => 'My favorite animal',
  '#cache' => array(
    'keys' => array('ascii-art', 'llama'),
  ),
  '#pre_render' => array('ascii_art_pre_render'),
  '#ascii_art_image' => 'llama.png',
);

function ascii_art_pre_render($build) {   
  $build['content'] = generate_ascii_art($build['#ascii_art_image']);   
  return $build; 
}
?> Что за магические значения?

Завязывать кэш на конкретные значения (5-ую ноду, 3-го пользователя) – это, конечно, не солидно. А главное, вряд ли нужные значения известны. Поэтому в реальности, конечно, всё описывается программно.

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

<?php
$nids = [];
foreach ($result as $row) {
    $nids[] = $row->nid;
}
$cache_tags  = Cache::buildTags('node', $nids));
'#cache' => array(
    'keys' => $cache_tags,
),
?> Яма, яма, яма, ямллл…

Кстати, простые настройки можно указывать и через YML

renderer.config:
    auto_placeholder_conditions:
      max-age: 0
      contexts: ['session', 'user']
      tags: [] Настройка через API: $metadata = new CacheableMetadata();
$metadata->setCacheContexts(['qux'])
      ->setCacheTags(['foo:bar'])
      ->setCacheMaxAge(600); Хочу свой рендеринг с кешем и зависимостями

Вот, пожалуйста:


<?php
$renderer = \Drupal::service('renderer');

$config = \Drupal::config('system.site');
$current_user = \Drupal::currentUser();

$build = [
  '#prefix' => '<aside>',
  '#markup' => t('Hi, %name, welcome back to @site!', [
    '%name' => $current_user->getUsername(),
    '@site' => $config->get('name'),
  ]),
  '#suffix' => '</aside>',
  '#cache' => [
    'contexts' => [
      // The "current user" is used above, which depends on the request,
      // so we tell Drupal to vary by the 'user' cache context.
      'user',
    ],
  ],
];

// Merges the cache contexts, cache tags and max-age of the config object
// and user entity that the render array depend on.
$renderer->addCacheableDependency($build, $config);
$renderer->addCacheableDependency($build, \Drupal\user\Entity\User::load($current_user->id()));
?>
Какие есть еще API?

Теги и контексты – обычные массивы, и ничто не мешает их склеить обычным сложением
$tags = $account->getCacheTags() + $comment->getCacheTags()
Но престижней использовать специально обученные метод, ведь это и инкапсуляция и избавления от повторок
Cache::mergeTags($main_tags, $add_tags);
Все это верно и для contexts, и для max-age.
Если в запросе нет подходящего параметра, чтобы подставить в keys, можно использовать его hash (sha256), через метод keyFromQuery.
Еще есть validateTags, invalidateTags и getBins (bins – контейнеры, в которых хранятся кэши, обычно по названию модуля, к которому принадлежат их значения)

Entity и Config – готовы, а ты?

У всех entity или configuration уже настроены параметры кэширования. Получить их можно так:

\Drupal::entityManager()->getDefinition('node')->getListCacheTags();
Или через конкретные объекты:
$node->getEntityType()->getListCacheTags()
(вместо $node, может быть $user, $view…)

Аналогично для contexts и max-age
$this->entityType->getListCacheContexts()

Что там есть в Examples/cache_example

Хоть там todo больше, чем кода, но можно глянуть как вручную сохранять/получать/удалять кэш:

<?php
cacheBackend->set('название_кэша', 'содержимое_кэша', параметры_кэширования);
$cache = cacheBackend->get('название_кэша'); // и потом $cache->data
cacheBackend->delete('название_кэша');
?>

Где cacheBackend - это объект реализующий интерфейс CacheBackendInterface.
Там же, в качестве примера, подсчитывают количество файлов в ядре, угадаешь сколько? А вот и нет, всего лишь 5928.

Так кому, что и почему надо знать?

При описании «революционных изменений» Drupal 8, часто упоминают «кэширование по умолчанию». Но это не просто кэширование. Два модуля, Internal Page Cache и Dynamic Page Cache затащили это дело на новый уровень. Вот как описывает историю их создания ведущий разработчик Вим Лирс (вкратце):

Как-то Дрис начал парить мне голову вопросами о ESI. Я сказал, что то, что у нас называется "поддержкой ESI" - бред, но есть реальный шанс сделать хорошо. Дриса это так проняло, что он даже выложил пост :) А мне пришлось изрядно напрячь башку, чтобы реализовать то, что посулил в запале.

Сначала наша команда довела до ума кэш тэги, благодаря чему Page Cache (ныне Internal Page Cache) стал работать так хорошо, что мы включили его в сборку и включили по умолчанию. А затем добили и контексты кэша. Это далось не просто, пробовали и так и сяк. В резульате на пару с Фабьеном запили через пузырёк. Потом еще 7 месяцев просыхали фиксили, но это того стоило. И теперь Dynamic Page Cache (бывший Smart Cache) тоже в сборке, и тоже по умолчанию.

Конечно, еще есть над чем работать, напрягает и время перед запуском и всякая другая лабуда, но у нас еще есть идеи на этот счет. Кстати, как вам BigPipe?

И еще раз

То, что эти модули включены по умолчанию, означает, что каким бы ты ни был лохом, кэширование на сайте Drupal 8 будет огонь! Только не выключай их. Хотя, если для анонимных пользователей нужны разные результаты (например, с учетом их сессии), то Internal Page Cache придется отключить (либо мутить через AJAX), но это уже другая история.

А в этой истории речь о том, что теперь больше не нужны никакие танцы с Authcache, где нужно учитывать весь код сайта. Более того, опять пошли разговоры, что владелец сайта (админ, маускликер, сайт-билдер) может забыть, что значить фраза «очистить кэш». Поскольку теперь разработчики модулей могут и должны сами настроить зависимости кэша. Контриб-модули даже обязаны предоставить тесты на годность в режиме кэширования.

Но если Dynamic Page Cache мешает запалу разработки, можно временно его отключить, для этого в папке sites/название_сайта/ создается settings.local.php с кодом:

$settings['cache']['bins']['render'] = 'cache.backend.null';

Когда это будет в 7-ке?

Никогда. В Drupal 7 не появится мгновенного обновления кэша (в Drupal 8 кэш обновляется сразу по факту устаревания, не дожидаясь запроса, ога). Не появится Dynamic Cache Page. И не будет BigPipe. Как-то так.

А где же та фраза?

«There are only two hard things in Computer Science: cache invalidation and naming things. -- Phil Karlton»

Склад ссылок, которых здесь еще не было (хотя быть должны)

https://www.drupal.org/developing/api/8/cache
https://www.drupal.org/developing/api/8/render/pipeline
https://www.drupal.org/developing/api/8/render/arrays/cacheability/auto-placeholdering
http://wimleers.com/talk/making-drupal-fly-fastest-drupal-ever-here
http://wimleers.com/blog/drupal-8-page-caching-enabled-by-default
https://drupalwatchdog.com/volume-4/issue-1/automagic-speed-cache
http://tech.dichtlog.nl/php/2015/08/03/lazy-builder-callback.html
http://www.sitepoint.com/exploring-cache-api-drupal-8/
https://github.com/upchuk/cache_demo_d8
http://slides.com/mikkeschiren-1/d8-cache#/1
http://chimera.labs.oreilly.com/books/1230000000845/ch01.html#_queues_and_workers

Тип материала: Предлагаю решениеВерсия Drupal: Drupal 8.*Ключевые слова: drupal 8cache 9 Спасибо
Категории: Друпалургия

Обновление Drupal.ru от 06.02.2016

сб, 06/02/2016 - 10:57


В обновление вошло:

  1. Поиск! Блок доступен на внутренних страницах. Реализован через sphinxsearch. #25
  2. Устранен баг с плавающим блоком #64
  3. Устранен баг с настройками темы #72
  4. Пользовательская сортировка тем на форуме #86
  5. Отображение в форуме даты последнего ответа #94
  6. Устранена проблема с добавлением материалов в форум #102
  7. Устранен баг с мобильной версткой (горизонтальный скролл) в материалах #166
  8. Добавлены метатеги для каждой страницы #178
  9. Более чистое отображение комментариев для анонимного посетителя #192
  10. Добавлен раздел "Бизнес" для каталога Drupal компаний #214
  11. Добавлен раздел "Мероприятия" для отображения ближайших событий #215
  12. Серьезно переделана титульная страница #217, #338
  13. Добавлен Кодекс поведения #118
  14. Добавлены Пользовательское соглашение, Модерация и ответственность и обновлены Правила сайта drupal.ru,Лицензионное соглашение и авторские права #248
  15. Изменена работа с ссылками для комментариев. Для мобильных работает "swipe left, swipe right". #253
  16. Поправлены проблемы с валидацией комментариев и материалов #266, #267, #275, #295
  17. Устранен баг с картинками в комментариях #274
  18. Добавлены "спасибо" для материалов и комментариев. #293
  19. Добавлена возможность "позвать модератора"#314
  20. Прикрепленные изображения к комментариям теперь ссылаются на полный вид картинки #332
  21. Пейджер сделан более компактный и не рассползается на мобильных экранах #335
  22. Трекер на мобильном теперь занимает всю ширину экрана #407

Герои обновления:

bober

Frantsuzzz

orion76

bumble

kirill-dan

tonystar

gor

10 Спасибо
Категории: Друпалургия

Drupal 7 Commerce + ImportKit + 1C УТ часть 2

ср, 03/02/2016 - 01:44

Итак, это продолжение предыдущей (Часть 1) статьи про выгрузку ImportKit.
Когда я делал выгрузку товара первый раз у меня уже было 400 вновь созданных позиций номенклатуры, в итоге, когда я разобрался с модулем, я понял что ВСЕ надо переделывать. Теперь я решил поделиться своим опытом. Надеюсь что будет полезно.
Итак поехали! В прошлой статье я сказал что необходимо использовать характеристики в свойствах номенклатуры. Но это не всегда верно! В каком случае нам использовать характеристики номенклатуры а в каком нет? Все очень просто!
1. Если нам необходимо чтобы несколько товаров добавлялось к одному дисплею, то используем характеристики.
2. Если нам необходимо чтобы к дисплею добавлялся только один товар, то характеристики НЕ используем.
Ссылка для тех кто не знает что такое характеристики и как с ними работать Видео
Давайте еще раз в картинках.

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

Обратите внимание: я использую "Вкус" как характеристику номенклатуры.
На этом все. Спасибо за внимание! Рад и с удовольствием отвечаю на любые комментарии.
P.S. Вопрос для всех, но в первую очередь для тех кого интересует данный модуль и кто его плотно использует:
Надо ли добавить возможность импорта дополнительных полей в карточку товара?

Тип материала: Предлагаю решениеВерсия Drupal: Drupal 7.*Ключевые слова: XML Импорт Форумы: Решение проблем2 Спасибо
Категории: Друпалургия

1С УТ + Drupal 7 Commerce (Ubercard)

сб, 30/01/2016 - 02:19

Часть 1 Часть 2. Хочу поделиться своим скромным опытом в интеграции 1с Управление торговлей 11.1 с Drupal 7 Commerce. А именно передо мной стояла задача осуществить выгрузку товара из базы 1с в интернет магазин, а обратно принять готовые заказы. Обратил внимание на разработки этой команды из Краснодара, пробовал ставить этот модуль. Но первый вариант отпал сразу ввиду его платности, а проект только начинает существование и денег соответственно нет, второй вариант отвалился в виду того что не смог разобраться с модулем.
В итоге наткнулся на данный модуль, называется ImportKit, полностью отвечающий моим требованиям, а это в первую очередь ИМПОРТ/ЭКСПОРТ товаров-заказов с гибкими и простыми настройками. Второе - это возможность в дальнейшем ПОЛНОСТЬЮ автоматизировать рутинный процесс. И третье, последнее в списке, но не последнее по значимости - это бесплатность данного решения.
Расскажу о своих настройках данного модуля.
На сайте я добавил Тип товара со стандартными настройками и Дисплей товара с полями ссылающимися на словари таксономии:

  • Брэнд,
  • Модель,
  • Тип товара,
  • Объем, вес, порции
  • Каталог


Так-же на 1С создал "Вид номенклатуры с характеристиками" и добавил следующие свойства:

  • Брэнд,
  • Модель,
  • Тип товара,
  • Объем, вес, порции


Характеристикой для данного вида является "Вкус"
После надо настроить стандартную выгрузку на сайт в 1С, благо в сети информации по этому вопросу море. Если все настроили правильно, то из 1С мы получаем два файла в формате *.xml -> import и offers
Теперь можно перейти к настройке самого модуля.
После установки и включения модуля мы получаем новую ссылочку в админ-панели Importkit (1C).
В настройках самого модуля мы видим следующее:

Переходим к самому интересному

Данная настройка говорит что в Словарь "Категория" будут добавлены группы из справочника Номенклатура

Здесь настройки не принципиальны.

Как видно из скрина, из файла import.xml переносим GUID (цифро-буквенный идентификатор) в соответствующее поле. Как вы догадались эта настройка говорит что характеристики номенклатуры надо записать в словари таксономии.

Здесь все по стандартному
Ну и наконец цена.

Данный GUID берем из файла offers.xml где в <ИД> для вида цены который вы выгружаете.
В настройке The rate of the retail price устанавливаем 100 или 1, я думаю разберетесь.
В общем то и все настройки. Если меняете настройки, то лучше удалять товары перед обновлением.
Надеюсь, что хоть кому-то помог. Прошу строго не судить, это мой первый опыт написания таких инструкций. Спасибо за внимание!
P.S. модуль лучше качать здесь https://bitbucket.org/mirocow/importkit

Тип материала: Предлагаю решениеВерсия Drupal: Drupal 7.*Ключевые слова: XML ИмпортImportKit Форумы: Решение проблем4 Спасибо
Категории: Друпалургия

Drupal: 15 лет и до сих пор набирает обороты

вт, 19/01/2016 - 22:48

15 Января 2000 года Дрис Байтаерт выпустил первую версию Drupal - 1.0.0.

Сегодня, спустя 15 лет, а по меркам интернета - это вечность, Drupal все еще продолжает набирать обороты.
В ноябре прошлого года, буквально пару месяцев назад, свет увидела версия Drupal 8.0.0!

Пост Дриса на эту тему можно прочитать тут.

Предлагаю в комментариях к этому посту рассказать свою историю: как вы пришли к Drupal, чем занимались и как это изменило вашу жизнь!

1 Спасибо
Категории: Друпалургия

Страницы