Я постараюсь максимально кратко описать опыт разработки платформы для разработчиков печатных плат на WordPress (WP) без начальных знаний о web-разработке, остановившись на некоторых нюансах, которые могут оказаться полезными тем, кто будет проходить тот же путь. Также я с радостью выслушаю советы от экспертов, если я в чём-то окажусь не прав. Я знаю, несколько мощное сообщество программистов на Хабре, понимаю отношение к WP, так как видел заминусованные посты о нём и, в принципе, сам понимаю неоптимальность такого решения. Но тем не менее, мне за три месяца с оплатой только одного плагина удалось реализовать на нём функционал, сторонняя разработка которого, по словам моего институтского товарища, могла бы стоить несколько сотен тысяч рублей и ещё большего времени. Поэтому я решил начать с пилотной версии проекта, от которой можно будет отталкиваться дальше – например, в качестве ТЗ при сторонней разработке.


Задача была в создании платформы для разработчиков печатных плат со следующим функционалом:

  • калькуляторы, упрощающие выполнение специфических расчётов;
  • база знаний по разработке печатных плат;
  • коллективный блог;
  • личные сообщения.

Я изначально скептически относился к тому, что это можно реализовать на WP без написания собственных модулей. Одностраничные сайты, блоги, интернет-магазины простые – да, но инженерные расчеты? Но всё получилось – для proof-of-concept очень даже неплохо, и дальше – об этом. Буду излагать в виде отдельных кейсов, чтобы было структурировано.

Повторюсь, что я – разработчик печатных, поэтому, когда у меня получалось с помощью изменения PHP-кода плагина добиваться того, что я от него хотел, я дико радовался. То есть то, о чём я буду рассказывать – это для тех, кто сайт сделать хочет, но в тонкостях WP, CSS и PHP не разбирается, хотя в общем и целом в программировании понимает и кода не боится.

1. Инструменты разработки


Я не проводил долгого сравнительного анализа программ, мне было достаточно того, что необходимая задача решается. Вот, что я использовал:

  • Visual Studio Code – приятный бесплатный редактор, для просмотра и редактирования кода плагинов;
  • Firefox Developer Edition – для просмотра, редактирования и анализа страниц;
  • FileZilla — для обмена файлами плагинов/темы с сервером;
  • Total Commander – для поиска файлов по заданному тексту.

Сюда же отнесу один WP-плагин – Loco Translate, просто незаменимый инструмент создании или корректировки перевода плагинов.

Что касается самого WP, то в англоязычном сегменте YouTube рассказано, наверное, всё (не помню, чтоб по возникшему вопросу не нашёл ответ), много хороших каналов, отметил бы каналы WPBeginner и WordPress Tutorials.

2. Калькуляторы


У WP есть огромное количество плагинов, но выполнение расчётов – задача совсем не типовая, если только это не расчёт стоимости товара или страховки. Но есть плагин и под эту задачу. Когда я его нашёл, то понял, что идея реализуема. Calculated Fields Form – и даже в бесплатной версии он позволяет делать формы для расчётов, в общем, он прекрасен. С интерфейсом и редактором формул придётся поразбираться, но они логичны и хорошо документированы. Вот как выглядит меню редактирования на примере одного из калькуляторов.


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

Каждое поле имеет свой идентификатор с порядковым номером типа «fieldname%n», по факту это имя переменной, которая используется при расчётах в вычисляемых полях. При этом служебные вычисляемые поля могут быть скрыты.

Среди готовых функций есть всё необходимое даже для сложных расчётов, в том числе тригонометрические и обратные тригонометрические. Синтаксис вычисляемого поля позволяет использовать ветвление if-else, функции на js, а также сторонние вызовы (вот пример из документации). Пример синтаксиса функции:

( function() {
if( fieldname3 > 100 ) return fieldname1+fieldname2;
if( fieldname3 <= 100 ) return fieldname1*fieldname2;
} ) ();

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


Плагин меня, честно говоря, просто спас. Конечно, страницы получились тяжеловесными, потому что в некоторых расчётах несколько десятков вычисляемых полей и сложные формулы. Хочется, чтобы летало, поэтому в планах – разобраться с вызовом сторонних функций, которые бы облегчили интерфейс, хотя я пока понятия не имею, как это всё сделать. Ну, тут step-by-step, буду разбираться.

Ещё из опыта, что бы порекомендовал:

  1. Для сложных формул проще их сначала отладить в том же Excel (или лучше тот, где функции на английском: не «СТЕПЕНЬ», а «POW»), а потом вставлять в редактор значения вычисляемого поля с заменой аргументов, так удобней.
  2. Служебные поля лучше скрывать только после того, как форма отлажена, что с первого раза у меня почти никогда не получалось для сложных вычислений. Так сразу видно, в каком расчёте затык.
  3. В качестве вывода (например, в случае ошибки) можно использовать строку 'текст сообщения'.
  4. Объединять связанные блоки в специальные контейнеры – поля «Div», которые можно скрывать или отображать со всем содержимым. Но стоит иметь в виду, что к скрытым полям обращаться нельзя. Я некоторые общие «переменные» выносил в отдельный блок, делая их «глобальными».

3. Настройка отображения


Некоторые плагины предоставляют в бесплатной версии основной функционал, но ограничивают возможности настройки внешнего вида, хотя настроить отображение – это достаточно просто. Для этого есть два инструмента – CSS и сам PHP-код. Буду объяснять как дилетант для дилетанта.

3.1. Настройка стилей с помощью CSS


Для каждого элемента html-страницы можно определить стиль отображения с помощью CSS:

CSS-селектор {
свойство_1: значение;
свойство_2: значение;
}

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

Чтобы узнать, какой стиль применяется для элемента и какой у него идентификатор, потребуется Firefox Developer Edition или аналог. Если нажать на элемент правой клавишей и выбрать «Inspect Element», то отобразится чудо-панель.



На данном примере рассмотрим селекторы. Интересующий элемент – поле ввода имя пользователя. В html-коде заданы класс и id, поэтому в качестве селектора можно использовать:

.input-text input           // .имя_класса имя_элемента (вар. 1)
input.input-text            // имя_элемента.имя_класса (вар. 2)
#fep-message-top input      // #id имя_элемента (вар. 1)
input#fep-message-top       // имя_элемента#id (вар. 2)
#fep-message .input-text    // #id имя_класса

Селектор по id более «сильный», так как на одной странице не может быть элементов с одинаковым id. Так что если конкретная форма плагина используется только на одной странице или стиль отображения подходит для всех случаев, то селектор по id – то, что нужно. Но что, если id для элемента не задан, что часто бывает. Тогда стоит посмотреть на наличие id у родительских блоков. Например, в данном случае id=”user_box” есть у контейнера . Тогда к элементу можно обратиться так:

div#user_box input      // родительский_элемент#id имя_элемента

Вложенность может быть и больше. В качестве примера:

div.fep-field div.field-with-icon input     // два родителя с классами и элемент

Так как в данном случае доступ не id, то описанный для данного селектора стиль будет применим и для поля ввода имени пользователя, и для поля ввода темы сообщения (оба в контейнерах класса «field-with-icon»).

Ещё один вариант, который может пригодится – это селектор по значению свойства. Для данного примера:

input[type="text"] // селектор по свойству «type»

Думаю, суть примерно ясна, тут прекрасно работает метод проб и ошибок. Прелесть в том, что Firefox DE позволяет на лету редактировать код страницы, селекторы, свойства. Добиваемся нужного результата – и переносим полученные изменения на сервер. Для этого существует три метода:

  • в плагине в настройках может быть поле типа «Custom CSS», «Дополнительные стили» и т.п.;
  • то же в настройках внешнего вида темы;
  • внести изменения в исходный файл с CSS (выделен на рисунке выше) — с этим методом у меня не получилось с общим файлом стилей темы style.css, там что-то хитрое нужно делать.

Между значениями одного свойства в разных селекторах всегда будет выбрано одно, имеющее максимальный приоритет. Логика такая, что локальное определение имеет более высокий приоритет, чем глобальное. В случае, когда переопределить свойство с помощью селектора не получается, то есть модификатор «!important», который поднимает приоритет значения:

input[type="text"] {
padding-left: 40px !important; // высокий приоритет значения
}

Последнее, о чём важно сказать про селекторы – это конструкция для адаптивных стилей «@media», которая активирует селектор при определённых условиях: минимальной или максимальной ширине дисплея и т.п. Вот пример использования:

@media(max-width:650px) {     // для небольших дисплеев
   .comment-body .avatar {
        width: 50px !important;    // уменьшить аватар в комментарии
}
}

Что касается самих свойств, то их очень много и нужно на конкретной задаче смотреть документацию. Самое простое, что можно сделать – это изменить цвет, позиционирование, скопировать готовый стиль. Цвет удобно подбирать «на лету» в Firefox DE. По поводу позиционирования надо разобраться с различными значениями свойства «display» и прочитать, например, вот эту статью.

3.2. Настройка отображения с помощью PHP


Два основных повода, по которым я редактировал PHP-код плагина – это замена текста и редактирование вёрстки страниц.

3.2.1. Замена текста


Что касается первого, то по-хорошему он должен решаться с помощью меню настройки самого плагина, либо с помощью плагина Loco Translate. Как работает вообще локализация плагина? Язык сайта выбирается в общих настройках в консоли WP и это определяет то, какой файл перевода будет использоваться. Файл перевода (расширение .po) имеет следующую структуру:

#: utilities.php:1910 utilities.php:2095     // номера строк в файлах плагина
msgid "Lost your password?"                    // исходная строка
msgstr "Забыли пароль?"                        // перевод

В соответствующей строке файла utilities.php (1910-ой, например) находим:

$str .= '<div class="impu-form-links-pass"><a href="'.$lost_pass_page.'">'.__('Lost your password?', 'ihc').'</a></div>';

Здесь видна исходная строка и идентификатор текстового домена плагина 'ihc', а подставляться в итоге будет переведённая строка. Loco Translate предоставляет простой и удобный интерфейс работы с файлами перевода.


Иногда в этих строках есть символы, определющие форматы вывода – %s, %d и т.п. – в переводе их, конечно, нужно сохранять. С ними, кстати, возникают проблемы со множественным числом и разными окончаниями для разных чисел («3 комментария», «5 комментариев»), приходится выкручиваться.

Так вот, иногда, если разработчики не указывают вхождение в файле перевода, то до неё не достучаться вышеуказанным методом. Приходится действовать иначе. Скачиваются все файлы плагина из директории /wp-content/plugins/название_плагина и с помощью поиска по строке или части строки (Total Commander или аналог в помощь) искать php-файл, в котором она содержится (хорошо, если она уникальная, а не «Login»). И можно либо заменить строку прямо в коде, либо внести вхождение в файл перевода и сделать нужный перевод.

3.2.2. Редактирование вёрстки


Может возникнуть ситуация, когда хочется изменить то, что не меняется в настройках плагина или темы. Я разберу здесь один пример. Если этот раздел вызовет интерес и вопросы, то я его дополню. Больше всего я дорабатывал плагин для личной переписки Front End PM. Непопулярная функциональность, поэтому это, наверное, единственный плагин, который её реализует, соответственно, проработка в части отображения низкая. Я вернул обратно оригинальный файл, который формировал верхний блок страницы, чтобы показать, как это делалось последовательно.


Находим через «Inspect Element» блок, который мы хотим изменить и ищем уникальные идентификаторы для поиска – это специфические для плагина названия классов или id, прежде всего. Тут для общего контейнера задан id = “fep-header”. Поиск по вхождению строки приводит на файл с говорящим названием header.php. Открываем с помощью VS Code и наблюдаем следующий код:

<div id="fep-header" class="fep-table">
        <div>
            <div>
                <?php echo get_avatar( $user_ID, 64, '', fep_user_name( $user_ID ) ); ?>
            </div>
            <div>
                <div>
                    <strong><?php esc_html_e( 'Welcome', 'front-end-pm' );?>: <?php echo fep_user_name( $user_ID ); ?></strong>
                </div>
                <div>
                    <?php echo strip_tags( sprintf( __('You have %1$s and %2$s unread', 'front-end-pm'), '<span class="fep_unread_message_count_text">' . sprintf( _n( '%s message', '%s messages', $unread_count, 'front-end-pm' ), number_format_i18n( $unread_count ) ) . '</span>', '<span class="fep_unread_announcement_count_text">' . sprintf( _n( '%s announcement', '%s announcements', $unread_ann_count, 'front-end-pm' ), number_format_i18n( $unread_ann_count ) ) . '</span>' ), '<span>' ); ?> 
                </div>
                <div class="<?php echo $box_class; ?>">
                    <?php echo strip_tags( sprintf( __( 'Message box size: %1$s of %2$s', 'front-end-pm' ), '<span class="fep_total_message_count">' . number_format_i18n( $total_count ) . '</span>', $max_text ), '<span>' ); ?>
                </div>
            </div>
            <?php do_action( 'fep_header_note', $user_ID ); ?>
        </div>
    </div>

Для тех, кто не знаком с PHP (как я), выглядит как HTML, но с вставками <?php… код … ?>. Эти вставки служат для формирования HTML-страницы в зависимости от различных параметров, например, от пользовательских данных. То есть PHP – это такой статический конструктор страницы, а JavaScript служит для обработки сложных событий.

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

<div class="fep-header">
        <div class="fep-header-left-side">
            <div class="fep-user-page-avatar">
                <?php echo get_avatar( $user_ID, 96, '', fep_user_name( $user_ID ) ); ?>
            </div>
        </div>
        <div class="fep-header-right-side">
            <div class="fep-header-username">
                <?php echo fep_user_name( $user_ID );?>
            </div>
            <div class="fep-header-top-info">
                <?php
                    if( $unread_count == 0 && $unread_ann_count == 0 )
                    {
                        echo sprintf( __('У Вас нет непрочитанных сообщений.', 'front-end-pm'));
                    }else{
                        if( $unread_count != 0 )
                            echo sprintf( __('Непрочитанных сообщений: %d<br>', 'front-end-pm'), $unread_count);
                        if( $unread_ann_count != 0 )
                            echo sprintf( __('Непрочитанных объявлений: %d', 'front-end-pm'), $unread_ann_count);
                    }
                ?>
            </div>
        </div>
        <?php do_action( 'fep_header_note', $user_ID ); ?>
        <div class="fep-top-background"></div>
    </div>


Естественно, процесс итеративный и не очень удобный, но я смирился. Возможно, есть более удобные способы. Сохраняем файл (VS Code), заменяем файл на сервере (FileZilla), обновляем страницу (Firefox DE). Это не самый сложный кейс, но суть процесса он показывает. Так же и в более сложных задачах – с изменениями функций, классов и массивов данных, и в более простых – когда надо просто поменять местами блоки или удалить лишний.

Но такой подход значительно усложняет процесс обновления. После обновления, наверно (я не делал, пока по принципу «работает – и хорошо»), придётся заново вносить правки. Это, наверное, основной минус.

4. Производительность


WP – это не про скорость. Попытки улучшить скорость загрузки, честно говоря, ни к значительному приросту не привели, а вот к нарушению работоспособности приводили. Использовал плагин Asset CleanUp для удаления загрузки лишних CSS/JS, но заметного прироста скорости загрузки не заметил (смотрел по GTmetrix), зато заметил, что некоторые плагины лезут на все страницы вне зависимости от того, используются они там или нет. В итоге пока отключил. Потом попробовал W3 Total Cache для кэширования страниц на сервере – когда страница не генерируется с помощью PHP, а загружается готовая копия (которая периодически обновлется). Но с ним у меня упал сайт, а потом я ещё и увидел про то, что рекомендуемая настройка – не кэшировать страницы для залогиненых пользователей, то стало понятно, что кэширование страниц в моём случае настроить достаточно трудно. Оставил только частичное кэширование кусков кода, буду разбираться ещё (иногда теперь приходится сбрасывать кэш, чтобы изменения настроек плагинов вступили в силу).

В общем, думаю, что в каких-то случаях это работает здорово, но мне вот так вот сразу, из коробки не сильно помогло. Насколько я понял, в платном WP Rocket есть опция кэширования для каждого пользователя, возможно, потом его попробую.

5. Безопасность


В своё время в институте изучал специальность «Информационная безопасность». Хотя после окончания стал электроникой заниматься, профдеформация осталась. Что Интернет дырявый с точки зрения безопасности. Представляю, сколько уязвимостей в WP при его нагроможденной структуре. Всех не залатать, но самые простые вещи сделал по следам вот этой инструкции и установил Wordfence – посмотрим, если производительность не будет сильно страдать, можно будет его оставить.

6. Плагины


6.1. Управление пользователями и доступом


Для решения задачи я выбрал плагин Indeed Ultimate Membership Pro ($41). По нему есть отдельные видео-уроки – вот здесь. Пришлось повозиться, много настроек, но обо всём подробно писать не буду, если будут вопросы – отвечу в комментарии или в личных сообщениях. Расскажу только про способы разграничения доступа.
Базово есть несколько ролей в системе (которая изначально было заточена под блоги), и многие плагины с ними работают, управляя возможностями в зависимости от роли. В «Настройки > Общие» задаётся роль по умолчанию.


Плагин управления подписками добавляет ещё один уровень – уровень подписки, в соответствие с которым можно управлять доступом к контенту и его отображением для каждой страницы, в том числе отображение элементов меню. Также пользователи делятся на зарегистрированных и незарегистрированных – это ещё один способ разделения отображения контента.

Плагин не без недостатков, но свою работу делает. Много настроек, но есть обучающие видео, разобраться можно, техподдержка, если что, поддержит и подскажет.

6.2. База знаний


Сначала книгу разместил на платформе с помощью плагина просмотра pdf-файлов, но там нет возможности создания ссылок на материал, да и pdf не самый гибкий вариант для просмотра. Отказался и решил перейти на базу знаний. Я просто назову плагин – Echo Knowledge Base, он, действительно, хорош. Но что касается просмотра pdf – не могу не упомянуть плагин Flowpaper, он очень красивый, может, кому-то пригодится.

6.3. Голосования


Мне понравился плагин Yop Poll – опять же, есть всё, что нужно, все настройки, даже внешний вид неплохой на бесплатной версии.

7. Что не получилось


Для коллективного блога нужна возможность для пользователей создавать публикации. Выбор не велик — плагин User Submitter Posts (есть ещё вот такой платный). Я даже его платную версию купил, но она убила у меня форму авторизации, конфликт с плагином управления подпиской — вернул деньги, разработчик вопрос не решил, валил всё на другие плагины. Но суть не в этом. Форма создания публикации не даёт возможность загружать и вставлять в публикацию изображения.


Стал разбираться с правами, было полезно, установил полезный плагин User Role Editor. Так вот получилось загрузить изображение только тогда, когда пользователю были даны права редактирования всех страниц. Жесть полная, естественно, отключил, пока можно только URL-ссылку на изображения ставлять. Проблема тут в том, что публикации ещё не существует и WP смотрит на добавление изображения как на редактирование страницы, на которой находится форма создания публикации. Может, кто подскажет, как решить этот затык?

8. Вывод


WP позволил создать с минимальными затратами пилотный проект платформы с не самой простой функциональностью. Свою задачу он решил, создав отправную точку для дальнейшего развития. Если по содержанию поста возникнут дополнительные вопросы, с удовольствием поделюсь опытом. Также буду благодарен за комментарии и рекомендации специалистов (по повышению производительности – поможет ли CDN типа CloudFlare, или платный WP Rocket, специализированный хостинг типа WP Engine, нужен ли по факту Wordfence).

Всем удачи!