31 мая 2013 г.

В данной статье будут периодически появляться небольшие решения в плане улучшения эргономики интерфейса и функциональной логики OpenCart. Базовой версией для улучшения ПО магазина выбрана версия 1.5.4.

Предисловие

Не стоит гнаться за обновлениями успешного продукта, т.к. рано или поздно такой продукт станет неповоротливым и тяжёлым во всех смыслах. Помню, как начинал работать с ПО Bitrix, довольно быстрым и функциональным веб-приложением, сейчас же это программное обеспечение сильно нагружает не только сервер, но и людей. По тому же пути пошли и разработчики CS-Cart, а именно, стали добавлять много ненужных функций и модификаций, которые вроде и доставляют комфорт в работе (на мощных серверах), но на продажи магазинов существенно не влияют. Озвученные выше продукты коммерческие и они вынуждены наращивать избыточную функциональность, чтобы к ним не был потерян интерес.

OpenCart ПО открытое и свободное, в этом его основное преимущество, однако с каждым релизом работать с магазином становится сложнее, прежде всего в плане затрат на ресурсы сервера. Можно продолжать обновлять ПО с выходом каждого релиза, а можно остановиться на базовой версии и уже в ручном режиме вести необходимые доработки, т.к. OpenCart, как функциональный магазин, уже состоялся.

Решения

1. Скрытие продуктов для закрытых категорий.

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

В файле модели ./catalog/model/catalog/product.php для функций getProduct, getProducts, getProductSpecials, getLatestProducts, getPopularProducts, getBestSellerProducts, getProductRelated, getTotalProducts, getTotalProductSpecials в тело условия sql-запроса WHERE после p.status = '1' везде необходимо добавить следующий код: AND (SELECT IFNULL(SUM(cat.status),1) FROM category cat INNER JOIN product_to_category p2cat ON (cat.category_id = p2cat.category_id) WHERE p2cat.product_id = p.product_id) > 0
Теперь ссылающиеся товары, товары производителей и товары в результатах поиска будут также скрыты, если скрыты их категории.

2. Увеличение "веса" ссылки для категории продуктов

Если включён режим SEO URL, то для продуктов в тело тега <head> добавляется каноническое имя страницы <link href="http://your.domain.com/seo_url" rel="canonical" />, котороё сообщает поисковому роботу приоритетный адрес этой страницы.

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

В файл ./catalog/controller/product/category.php после строки $this->document->setKeywords($category_info['meta_keyword']); добавьте $this->document->addLink($this->url->link('product/category', 'path=' . $this->request->get['path']), 'canonical');

3. Сообщаем поисковым роботам о запрете индексирования несуществующих страниц сайта

Для начала необходимо привести в соостветствие заголовок об ошибке 404 (см. RFC 2616). В файле ./catalog/controller/error/not_found.php наидите строку $this->response->addHeader($this->request->server['SERVER_PROTOCOL'] . '/1.1 404 Not Found'); и удалите из аргумента /1.1. Дело в том, что в переменной SERVER_PROTOCOL уже содержится (реальная) версия протокола.

Теперь стоит обратить внимание на запрет индексации подобных страниц, чтобы поисковый робот не отслеживал их. Для этого в файл ./system/library/document.php добавим следующие строки кода:

private $robots;
public function setRobots($robots) {
$this->robots = $robots;
}
public function getRobots() {
return $this->robots;
}

Далее в тело функции index() файла ./catalog/controller/common/header.php добавим $this->data['robots'] = $this->document->getRobots(); и соответственно внесём изменения для шаблона ./catalog/view/theme/default/template/common/header.tpl добавив следующий код:

<?php if ($robots) { ?>
<meta name="Robots" content="<?php echo $robots; ?>" />
<?php } ?>

Вернёмся в контроллер not_found.php и после добавления заголовка страницы поместим строку $this->document->setRobots('noindex');

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

4. Сообщаем поисковым роботам о режиме обслуживания

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

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

Для этого в файл /catalog/controller/common/maintenance.php до передачи страницы на рендеринг добавьте строку $this->response->addHeader($this->request->server['SERVER_PROTOCOL'] . ' 503 Service Temporarily Unavailable');.

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

5. Выгрузка базы данных больших размеров из панели администрирования OpenCart

При работе с базой данных через пункт меню "Резервное копирование", браузер может неожиданно сбросить соединение. Одна из основных причин - нехватка памяти, выделенной скрипту. В журнале ошибок Apache могут быть сообщения вида Fatal error: Allowed memory size exhausted, что подтвердит описанную выше проблему.

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

6. Доработка менеджера файлов или окна управления изображениями

В OpenCart управление изображениями осуществляется через файл ~/admin/controller/common/filemanager.php при помощи мода jsTree для библиотеки jQuery. Мод отличается от других ему подобных своей простотой и элегантностью, но каждый раз открывая менеджер файлов в панели управления OpenCart, приходится раскрывать всё дерево в поисках текущего каталога для сохраненного файла, а потом уже и поиска самого файла среди множества других. Это очень неудобно и отнимает большое количество времени при наличии сложной иерархической структуры файлового хранилища. Однако было найдено решение и для этой проблемы!

7. Обращаем внимание на список рекомендуемых товаров

На главной странице магазина при помощи модуля Featured можно выводить список рекомендуемых товаров. Для постоянных посетителей этот список может быстро потерять актуальность, поскольку они могут к нему "привыкнуть". Хорошо бы данный список перемешивать каждый раз при открытии страницы. Поручить эту задачу стоит самому модулю (~/catalog/controller/module/featured.php), добавив в код функцию перемешивания ассоциативного массива.

8. Массовая рассылка сообщений покупателям OpenCart

В OpenCart есть сервис рассылки электронных писем покупателям и партнерам магазина (меню Продажи > Почта). Довольно полезная утилита, всегда есть возможность сообщить о ключевых новостях непосредственно по E-mail. Однако при большом количестве зарегистрированных пользователей в системе, такая рассылка попадает под определение массовой, на которую могут быть наложены санкции со стороны провайдера. Речь идёт об ограничениях на количество рассылаемых писем в единицу времени на хостинге. Функционал этой утилиты был успешно доработан, теперь появилась возможность задавать очередь сообщений с определенным интервалом в интерактивном режиме (отображение информации о ходе рассылки в реальном времени).

9. Определение "красной" цены в OpenCart

По умолчанию в OpenCart если товар участвует в акции, то визуально его старая цена перечеркнута в красном цвете, а новая стоит рядом (в черном). Однако это в корне неверно, поскольку в науке о продажах сказано, что "красная" цена - это минимально возможная цена, которая удовлетворяет как покупателя, так и продавца. В файле стилей stylesheet.css для классов .price-new и .price-old цвета нужно поменять местами.

10. Не учитывается часовой пояс при формировании дат.

Это больше касается настроек сервера, нежели OpenCart. Суть проблемы в том, что в журнал ошибок OpenCart записываются записи с временем отличным от времени сервера. Тоже самое касается начала действия акций, срока существования корзины покупателя и т.д. Основная причина - это отсутствие временной зоны по умолчанию в файле php.ini. Исправить можно несколькими способами:
1) В файле php.ini найти секцию [Date] и прописать действующую временную зону, например, date.timezone = Europe/Moscow 
2) В файле .htaccess через переменные окружения задать SetEnv TZ Europe/Moscow
3) В начале индексных файлов OpenCart добавить строку date_default_timezone_set(" Europe/Moscow");
11. Показ скрытых цен для авторизованных пользователей OpenCart.

Если в магазине скрыты цены для гостей, то при редактировании карточек товаров не всегда удобно проверять страницу товара под видом рядового посетителя. Приходится проходить авторизацию под тестовой учетной записью покупателя или отключать соответствующую опцию в настройках магазина. Чтобы об этом не думать, достаточно в файле ./index.php после строки $config->set('config_language', $languages[$code]['code']); добавить следующий код:

if (empty($session->data['user_id']) == false) { $config->set('config_customer_price', false); }

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

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

Чтобы появилась возможность активировать тот или иной модуль на всех страницах магазина сразу, потребуется внести изменения в следующие файлы:
./catalog/controller/common/column_left.php
./catalog/controller/common/column_right.php
./catalog/controller/common/content_bottom.php
./catalog/controller/common/content_top.php
В этих файлах найдите запись $module['layout_id'] == $layout_id и замените на ($module['layout_id'] == $layout_id || $module['layout_id'] == $this->config->get('config_layout_id')). Теперь если для модуля выбрать одну из схем по умолчанию, которая задаётся в настройках магазина, то он будет показан при открытии любой страницы сайта.

13. Пользователь системы с привилегированными правами.

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

В файле ./system/library/user.php после строки private $username; добавить private $is_owner = false; и везде после $this->username = $user_query->row['username']; добавить $this->is_owner = ($user_query->row['email'] === $this->config->get('config_email')); В этом же файле (дополнительно) после $this->session = $registry->get('session'); добавить $this->config = $registry->get('config'); Затем в теле класса добавить функцию: public function isOwner() { return $this->is_owner; }

Теперь для разграничения доступа к тем или иным полям интерфейса можно использовать конструкцию вида if ($this->user->isOwner()) { // Do stuff }.

Собственно, по такому же принципу можно создать более весомый признак $this->user->isAdmin(), значение которого будет истинным, если в группе текущего пользователя максимальное количество разделов с разрешением на модификацию из всех существующих групп пользователей.

14. Защита от перехода по внешним ссылкам из панели администрирования.

Эта стандартная и довольно популярная схема компрометирования учётной записи пользователя административной панели, когда под текстом "родной" ссылки скрывается совершенно другой адрес. Суть проблемы, как минимум, заключается в передаче реферера после перехода по внешней ссылке. Подобный переход значительно упрощает процесс взлома аккаунта. Чтобы этого не происходило, важно убедить себя не переходить по внешним ссылкам из любой панели управления (не только OpenCart) или добавить предупреждающее уведомление:


$(document).click(function(event) {
if ((event.target.tagName = 'A') && (event.target.href.indexOf('http') == 0)) {
if (location.hostname != event.target.hostname) {
return (prompt('Are you sure you want to go?', event.target.href) !== null);
}
}
});


Если у Вас возникли трудности с применением описанных выше решений на практике или просто на это нет времени, то мы готовы взять работы по модификации OpenCart на себя.

10 комментариев:

  1. По многочисленным просьбам 8-ой пункт реализован в виде отдельного модуля OpenCart Mailing Pro (http://opencartsoftware.blogspot.ru/2014/02/mailing-pro-module.html)

    ОтветитьУдалить
  2. Здравствуйте. Подскажите, как в опенкарт правильно добавить name="robots" content="noindex, follow" на страницы типа http://site.ru/bio-uv.html?token=67f08a156a6ddcbcfaeff6109ef9508d

    ОтветитьУдалить
    Ответы
    1. Здравствуйте! Смотрите п.3, в соответствующий контроллер (категория или товар) добавьте $this->document->setRobots('noindex, follow');

      Удалить
  3. В пункте 3 если удалить аргумент /1.1 то сервер выдает ответ 500 вместо 404. Это нормально?

    ОтветитьУдалить
    Ответы
    1. Здравствуйте! Нет, это не нормально, скорее всего нарушен синтаксис кода. Проверьте закрывающие апострофы, строка должна выглядеть следующим образом: $this->response->addHeader($this->request->server['SERVER_PROTOCOL'] . ' 404 Not Found');

      Удалить
  4. Здравствуйте! Подскажите, как можно реализовать rel="canonical" для категорий товаров? по п.2 у меня не работает...

    ОтветитьУдалить
    Ответы
    1. Все решения приведены для OpenCart версий 1.5+. Они могут быть несовместимы с более старшей версией OpenCart или клонами этого ПО.

      Для решения проблем совместимости существует возможность заказа той или иной модификации у автора блога (см. Услуги).

      Удалить
  5. Уважаемые подписчики! Вторая часть темы доступна по следующему адресу: http://opencartsoftware.blogspot.ru/2016/01/second-revision-opencart.html

    ОтветитьУдалить
  6. Для пункта 6, Ваше решение может переходить сразу к загружаемому файлу?

    ОтветитьУдалить
    Ответы
    1. Смысл доработки в том, что запоминается последнее местоположение файла в обозревателе. Таким образом не нужно перебирать дерево каталогов, прежде чем выполнить загрузку файла на сервер. Также добавлены мультивыбор для удаления и переименования файлов и быстрая вставка после его загрузки. Все для экономии времени.

      Удалить

  • RSS
  • Twitter
  • Youtube