Чиним ошибку 400 Bad Request с помощью mod_rpaf у BitrixEnv

, Михаил

Воспользуемся замечательнейшим модулем https://github.com/gnif/mod_rpaf который протестирован и успешно эксплуатируется в на многих серверах

Собираем модуль:

yum groupinstall "Development Tools"
yum install httpd-devel
wget -O /tmp/mod_rpaf.c https://raw.githubusercontent.com/gnif/mod_rpaf/stable/mod_rpaf.c
apxs -c -i /tmp/mod_rpaf.c

Далее создаём /etc/httpd/bx/custom/rpaf.conf

LoadModule              rpaf_module modules/mod_rpaf.so
RPAF_Enable             On
RPAF_ProxyIPs           127.0.0.1 Ваш.IP.Сервера
RPAF_SetHostName        On
RPAF_SetHTTPS           On
RPAF_SetPort            On
RPAF_ForbidIfNotProxy   Off

И не забываем выключить remoteip в файле /etc/httpd/conf.modules.d/00-base.conf и удалить файл /etc/httpd/bx/conf/mod_rpaf.conf

На выходе получим

[root@divasoft ~]# apachectl -M | grep -E 'remoteip|rpaf'
 rpaf_module (shared)

Перезапускаем httpd


В файле /etc/nginx/bx/site_enabled/ssl.s1.conf меняем строки

proxy_set_header   Host   $host:443;
proxy_set_header   HTTPS   YES;

На

proxy_set_header   Host   $host;
proxy_set_header   X-Forwarded-Proto   $scheme;
proxy_set_header   X-Forwarded-Port   $server_port;

В файле /etc/nginx/bx/site_enabled/s1.conf меняем строки

proxy_set_header   Host   $host:80;

На

proxy_set_header   Host   $host;
proxy_set_header   X-Forwarded-Proto   $scheme;
proxy_set_header   X-Forwarded-Port   $server_port;

Перезапускаем nginx

Итоговый блок будет выглядеть как

proxy_set_header   X-Real-IP   $remote_addr;
proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
proxy_set_header   Host   $host;
proxy_set_header   X-Forwarded-Proto   $scheme;
proxy_set_header   X-Forwarded-Port   $server_port;

Приём заказов в 1С из Битрикс с нескольких сайтов

, Михаил

Существует проблема с выгрузкой заказов с 2 разных сайтов.

Если сайты являются копиями друг друга, т.е. сначала был сделан сайт 1, затем скопирован и развернут с базой, и на его основе с незначительными доработками был сделан сайт 2. Для 1 сайта уже была подключены импорты заказов из Битрикс, и импорт номенклатуры в Битрикс. Для второго сайта сделали идентичные обмены, также путем копирования обменов, и изменения параметров отбора и древа групп в обмене товарами, и без изменений обмена заказами, кроме url адреса. При включении на автомате обоих импортов заказов происходит проблема дублирования заказа. Со 2 сайта приходит заказ, и полностью удаляет документы старого заказа в 1С, полученного с первого сайта. При сравнении и проверке заказов выяснилось, что заказы с идентичными ID.

Самый быстрый - меняем стартовое значение поля ID
Оно int(11), значит у нас есть в запасе от (-2147483648 до 2147483647)

Для первого сайта оставляем всё как есть.
Для второго:
ALTER TABLE `b_sale_order` AUTO_INCREMENT = 100000000; Для третьего:
ALTER TABLE `b_sale_order` AUTO_INCREMENT = 200000000;

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

Чиним fastdownload у nginx в bitrixenv для Яндекс Cloud Storage

, Михаил

Решил все загружаемые файлы в наш Битрикс24 отправить в Яндекс.Облако

Но файлы просто не скачивались, после долгой отладки нашёл как отдаются файлы, копнул глубже... ядро Битрикс, используя заголовок x-accel-redirect, делает всю эту магию с внешними хранилищами.
Находим секцию в /etc/nginx/bx/conf/bitrix_general.conf

# Use nginx to return static content from s3 cloud storage
# /upload/bx_cloud_upload/...amazonaws.com/
location ^~ /upload/bx_cloud_upload/ {

И туда добавляем

    location ~ ^/upload/bx_cloud_upload/(http[s]?)\.([^/:\s]+)\.storage\.yandexcloud\.net/([^\s]+)$ {
        internal;
        resolver 8.8.8.8;
        proxy_method GET;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Server $host;
        #proxy_max_temp_file_size 0;
        proxy_pass $1://$2.storage.yandexcloud.net/$3;
    }

Как в Битриксе, на странице оформления заказа, раскрыть все блоки

, Михаил

Берём за основу стандартный .default шаблон компонента sale.order.ajax, работаем в файле order_ajax.js

  1. Включаем редактирование блоков «Регион» и «Пользователь»: комментируем строки /*if (this.activeSectionId !== this.regionBlockNode.id) this.editFadeRegionContent(this.regionBlockNode.querySelector('.bx-soa-section-content')); if (this.activeSectionId != this.propsBlockNode.id) this.editFadePropsContent(this.propsBlockNode.querySelector('.bx-soa-section-content'));*/
  2. Удаляем кнопки «Далее» и «Назад»: комментируем строки /*node.appendChild( BX.create('DIV', { props: {className: 'row bx-soa-more'}, children: [ BX.create('DIV', { props: {className: 'bx-soa-more-btn col-xs-12'}, children: buttons }) ] }) );*/
  3. Все блоки раскрываем: меняем строку var active = section.id == this.activeSectionId На строку var active = true,
  4. Удаляем обработчики при клике на заголовки: комментируем строки /*BX.unbindAll(titleNode); if (this.result.SHOW_AUTH) { BX.bind(titleNode, 'click', BX.delegate(function(){ this.animateScrollTo(this.authBlockNode); this.addAnimationEffect(this.authBlockNode, 'bx-step-good'); }, this)); } else { BX.bind(titleNode, 'click', BX.proxy(this.showByClick, this)); editButton = titleNode.querySelector('.bx-soa-editstep'); editButton && BX.bind(editButton, 'click', BX.proxy(this.showByClick, this)); }*/
  5. Удаляем ссылки «Изменить»: в конец функции editOrder добавляем код var editSteps = this.orderBlockNode.querySelectorAll('.bx-soa-editstep'), i; for (i in editSteps) { if (editSteps.hasOwnProperty(i)) { BX.remove(editSteps[i]); } }

Находим файл из Битрикс24.Диск по ID

, Михаил

Нельзя просто так взять и получить настоящий файл для дальнейшей манипуляции с ним. В api такого нет. ORM d7 в помощь


<?php 
function getRealFileFromDiskById($diskId) {
	    \Bitrix\Main\Loader::includeModule('disk');
	    $resObjects = \Bitrix\Disk\Internals\ObjectTable::getList([
	            'select' => ['NAME''FILE_ID'],
	            'filter' => [
	                '=ID' => $diskId,
	            ]
	    ]);
	    if ($arObject $resObjects->fetch()) {
		        $arObject['PATH'] = CFile::GetPath($arObject['FILE_ID']);
		        $arObject['FULL_PATH'] = $_SERVER['DOCUMENT_ROOT'].$arObject['PATH'];
		        return $arObject;
		}
	    return false;
	}

?>

У Битрикса тоже есть чёрный список

, Михаил

На одном из серверов мы увидели доступ заблокирован администратором сайта [271] при обновлении системы

Сначала тех.поддержка сказала что "Подобного сообщения нет ни в продукте, ни на сервере(в стандартной поставке). Данный вопрос необходимо решать именно с администратором вашего сервера."

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

Битрикс24 - приём задач от клиентов

, Михаил

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

Когда клиенты сами ставят задачи - они могут:

  1. Написать в общую ленту.
  2. Написать самому себе в ленту.
  3. Поставить задачу самому себе.
  4. Написать в личное сообщение. себе.
  5. Поставить ответственных всех участников (что отвлекает сотрудников).
  6. Поставить задачу менеджеру, которого нет в онлайне.
  7. Напишут ещё куда нибудь, но только не в ленту группы.

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

О жизни в нашем офисе или несколько полезных советов разработчикам, которые мы используем сами

, Александра Потапова

Ни для кого не секрет, что жизнь IT-специалиста связана с сидячим образом жизни, чаще всего в офисе, бесконечным кофе и вездесущими печеньками. Мы решили поделиться с вами несколькими советами, которые используем сами, что б к 40-ка годам всем офисом не стать больными старичками и старушками.

  • мы почти полностью перешли на заварной чай. Никакого кофе, никаких чайных пакетиков, никакой газировки. Зеленый-для жаркой погоды, красный-для морозных зимних деньков, пуэры-чтоб взбодриться и сконцентрироваться;
  • мы любим забить калабас и попить мате. А еще любим напитки на травах;
  • пейте больше воды. Именно воды. Даже чай вовсе не тоже самое, что вода. Не стоит пренебрегать водно-солевым балансом в организме;
  • мы за здоровый образ жизни! К сожалению, от коллег, мы часто слышим их жалобы на больную спину, шею или начинающийся тоннельный синдром. Поэтому сами стараемся после работы заниматься спортом. Кто-то регулярно ходит в спортзал, кто-то совершает длительные пешие прогулки. Главное, после и во время работы двигаться;
  • что бы ваши руки не уставали, рекомендуем приобрести подставку для запястья. Стоит она не дорого, зато так вы обеспечите себе более здоровые суставы;
  • зимой, мы стараемся через каждые 2-3 часа проветривать помещение. Во-первых, приятно вернуться в прохладный, свежий офис. Во-вторых, это лишний повод походить, размяться;
  • не лишним будет влажная уборка офиса хотя бы раз-два в месяц. Меньше пыли-легче дышится!
  • у нас всегда в офисе стоит увлажнитель воздуха. Зимой, когда отопление работает на полную, увлажнитель становится крайне полезной вещью. Ваши глаза и кожа скажут вам спасибо;
  • так же, чтобы давать небольшой отдых и зарядку для глаз, мы советуем через каждые пол час ненадолго переключаться на далеко стоящие предметы. На соседнее здание, к примеру;
  • если вам хочется поесть – поешьте. Не стоит надеяться на перекусы и печеньки. Но если все совсем плохо и поесть ну никак не получается, заведите себе маленькую баночку орехов. Они прекрасно утоляют голод и пользы от них точно больше, чем от столь любимых многими программистами мучных изделий;
  • не пренебрегайте сном. Выспавшийся разработчик-залог успешной работы всей команды.

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

A.I.Divasoft - Пишем бота для Slack, создаём репозиторий в Bitbucket и оповещаем о коммитах в группе Битрикс24

, Михаил

Продолжаю оптимизировать рабочий процесс. Теперь создание репозитория, привязка оповещений в Битрикс24 из Bitbucket, к выбранной группе становится гораздо быстрее!

A.I.Divasoft - репозиторий

A.I.Divasoft - Пишем бота для Slack, создаём связанный контакт с компанией, генерируем лид/сделку, создаём или находим группу, приглашаем в группу

, Михаил

Куча действий связанных с новым клиентом, за пару кликов - новая функция нашего бота!

A.I.Divasoft - приглашаем клиента

A.I.Divasoft - Пишем бота для Slack, добавляем задачи в Битрикс24

, Михаил

Первым делом научили делать задачу в наш Битрикс24!

Текст задачи может формироваться сразу из сообщения в чате, постановщик - тот кто пишет, ответственный запоминается, как и выбор проектов.

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

A.I.Divasoft - ставим задачу в Битрикс 24

Устанавливаем заголовки в элементе/разделе из SEO модуля Битрикс

, Михаил

Добавляем в result_modifier.php


<?php 

// /local/templates/.default/components/bitrix/catalog.section/.default/result_modifier.php
// для элемента используем $ipropValues = new \Bitrix\Iblock\InheritedProperty\ElementValues($IBLOCK_ID,$ELEMENT_ID);
$ipropValues = new \Bitrix\Iblock\InheritedProperty\SectionValues($arResult['IBLOCK_ID'], $arResult['ID']);
$SEO $ipropValues->getValues();
$cp $this->__component;
if (is_object($cp)) {
	    $cp->arResult['SEO'] = $SEO;
	    $cp->SetResultCacheKeys(array('SEO'));
	    $arResult['SEO'] = $cp->arResult['SEO'];
	}
?>

Добавляем в component_epilog.php


<?php 

// /local/templates/.default/components/bitrix/catalog.section/.default/component_epilog.php
global $APPLICATION;
// DIVASOFT
if ($arResult['SEO']['SECTION_META_TITLE']) {
	    $APPLICATION->SetPageProperty("title"$arResult['SEO']['SECTION_META_TITLE']);
	}
if ($arResult['SEO']['SECTION_META_DESCRIPTION']) {
	    $APPLICATION->SetPageProperty("description"$arResult['SEO']['SECTION_META_DESCRIPTION']);
	}
if ($arResult['SEO']['SECTION_META_KEYWORDS']) {
	    $APPLICATION->SetPageProperty("keywords"$arResult['SEO']['SECTION_META_KEYWORDS']);
	}
if ($arResult['SEO']['SECTION_PAGE_TITLE']) {
	    $APPLICATION->SetTitle($arResult['SEO']['SECTION_PAGE_TITLE']);
	}
?>

Переход с чистого CSS на SASS. Первые впечатления

, Александра Потапова

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

Уж не знаю почему так повелось, но довольно долго я верстала на чистом CSS и, в целом, меня все устраивало. Необходимости перехода на новый динамический язык стилей не было. Справедливости ради, стоит отметить, что идея перехода на LESS или SASS зародилась уже давно. Просто не находила своей практической реализации, к тому же я долго думала, какое из расширений мне больше пойдет.

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

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

Порадовало, что есть очень подробная документация на русском на официальном сайте https://sass-scss.ru, так что даже новичок быстро освоится. Для меня, как для человека, который давно практикует чистый CSS, никаких проблем не было. SASS поддерживает все то же, но при этом гораздо удобнее, за счет вложенности, переменных, импорта, большого количества удобных директив и так далее, и, даже, немного умеет в программирование, что удивило и порадовало.

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

Стоит обратить внимание, что SASS все же требует предварительной установки в ваш IDE, но с этим тоже нет трудностей. Опять же, обо всем этом можно почитать на официальном сайте, там все объяснено доступным языком.

Так же стоит помнить, что браузеры не понимают других стилей кроме как с расширением css, поэтому при работе с SASS ваша среда разработки должна уметь компилировать все sass-стили в одни большие общие стили CSS. Либо стоит обзавестись любым удобным для вас препроцессором.

В целом SASS оставляет приятное ощущение, он прост, удобен и имеет гораздо больше возможностей чем CSS. Он попросту шире. Тем не менее, если вы только встали на путь Frontend-разработчика, стоит какое-то время поработать с чистым CSS. Понять, как работает наследование, псевдоэлементы и стили в целом. Отточить навык. И только потом переходить на расширения.

Обновляем BitrixEnv до 7.3.4

, Михаил

Перед всеми манипуляциями делаем бэкап/снапшот.

Обновляем ядро/модули Bitrix до последних стабильных обновлений или бета-версий.

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

yum install https://repo.percona.com/yum/percona-release-latest.noarch.rpm

Или временный фикс /etc/yum.repos.d/percona-release.repo - отключаем проверку ключа

[percona-release-noarch]
name = Percona-Release YUM repository - noarch
baseurl = http://repo.percona.com/release/...
enabled = 1
gpgcheck = 0
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-Percona

Повышаем версию php с 5.6 до версии 7.1

Задаем пароль root mysql через меню, или вручную в файле /root/.my.cnf

Повышаем версию mysql с 5.5 до версии 5.7

Чайный мастер

, Александра Потапова

Как приятно, когда есть вкусный, ароматный чай.

сертификат.jpg

Дизайн в мелочах

, Александра Потапова

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

Кстати, вот код rgb на тот самый деревянный цвет: #b3a28f. Теперь мы знаем, как он выглядит :)

Диверсия на сайте клиентов от сторонних разработчиков

, Михаил

Пишет нам один заказчик, что сайт начал долго открываться.
Заходим - действительно, ~17 секунд отдаётся контент.
Начали анализировать ситуацию, в одном из включаемых файлов видим это:

sleep15.png

sleep(15); - команда которая говорит серверу - подожди просто 15 секунд, потом делай свои дела дальше.

Это даже не смешно, "случайно" такую команду не напишешь, за 15 лет разработки я применил эту команду только 1 раз, и то, в сервисном скрипте, который по крону делает что то.

Так что это 100% диверсия. И те разработчики, если такое сделали, не известно, на что ещё способны. Как минимум 2 бэкдора уже нашёл.

О вреде виджетов

, Михаил

Попросили сделать оптимизацию на одном сайте - результат: открытие страницы за 0.4 секунды, Гугл рад.

m1.png
m2.png

Но пришло время добавить виджеты... 2 Яндекс метрики, Слиза, Гугл таг, Гугл аналитик, Калибри, Вк чат, Вк виджет и ещё что то от вк

m4.png
m3.png

Такие вот дела.

Возвращаем информацию об удаляющихся при обмене с 1С оплате и доставке в админке Битрикса

, Михаил

Продолжение этой проблемы. Когда менеджер работает одновременно в админке и в 1С, и когда в 1С выключена загрузка отгрузок и оплат - Битрикс убирает из заказа эти данные. Возвращаем информацию в заказ, и список заказов.


<?php 

// local/php_interface/init.php
// Добавляем информацию внутри заказа
\Bitrix\Main\EventManager::getInstance()->addEventHandler('sale''onSaleAdminOrderInfoBlockShow', ['DivasoftFixSyncOrderInfo''onSaleAdminOrderInfoBlockShow']);
// Заполняем колонки в списке заказов
\Bitrix\Main\EventManager::getInstance()->addEventHandler("main",  "OnAdminListDisplay", ['DivasoftFixSyncOrderInfo''onAdminListDisplay']);
\Bitrix\Main\EventManager::getInstance()->addEventHandler("main",  "OnAdminSubListDisplay", ['DivasoftFixSyncOrderInfo''onAdminListDisplay']);
class DivasoftFixSyncOrderInfo {
	    static function getSystemDeliveryNameByOrderD7($order) {
		        $shipmentCollection $order->getShipmentCollection();
		        $shipmenName "Не выбрана";
		        $systemShipmentItemCollection $shipmentCollection->getSystemShipment()->getShipmentItemCollection();
		        foreach ($shipmentCollection as $obShipment) {
			            if ($obShipment->isSystem()) {
				                $arShipment $obShipment->getFields()->getValues();
				                $shipmenName $arShipment['DELIVERY_NAME'];
				}
			}
		        return $shipmenName;
		}
	    static function getSystemPaymentNameByOrderD7($order) {
		        // getSystemPayment такого метода нет, запросим информацию по тому что есть
		        $paySystemService = \Bitrix\Sale\PaySystem\Manager::getObjectById($order->getField('PAY_SYSTEM_ID'));
		        $payName $paySystemService->getField("NAME");
		        return $payName;
		}
	    function onSaleAdminOrderInfoBlockShow(\Bitrix\Main\Event $event) {
		        $order $event->getParameter("ORDER");
		        $shipmenName self::getSystemDeliveryNameByOrderD7($order);
		        $payName self::getSystemPaymentNameByOrderD7($order);
		        return new \Bitrix\Main\EventResult(
		            \Bitrix\Main\EventResult::SUCCESS, array(
		            array('TITLE' => 'Доставка:''VALUE' => $shipmenName'ID' => 'dvs_system_shipment'),
		            array('TITLE' => 'Оплата:''VALUE' => $payName'ID' => 'dvs_system_payment'),
		            ), 'sale'
		        );
		}
	    function onAdminListDisplay(&$list) {
		        if ($list->table_id == "tbl_sale_order") {
			            foreach ($list->aRows as &$row) {
				                foreach ($row->aFields as $key => &$val) {
					                    $order false;
					                    if ($key == "DELIVERY") {
						                        if (!$val['view']['value']) {
							                            if (!$order) {
								                                $order = \Bitrix\Sale\Order::load($row->arRes['ID']);
								}
							                            $val['view']['value'] = self::getSystemDeliveryNameByOrderD7($order);
							}
						}
					                    if ($key == "PAY_SYSTEM") {
						                        if (!$val['view']['value']) {
							                            if (!$order) {
								                                $order = \Bitrix\Sale\Order::load($row->arRes['ID']);
								}
							                            $val['view']['value'] = self::getSystemPaymentNameByOrderD7($order);
							}
						}
					}
				}
			}
		}
	}
?>

Опасность архивирования заказов в Битрикс, или почему увеличиваются остатки на складе

, Михаил

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

В итоге, обратил внимание на "Архивирование заказов". Долго описывал проблему, в итоге тех.поддержка ответила:

Посмотрели по заказу у отгрузки стоит RESERVED=Y, но сам заказ и отгрузки по нему не отгружались, поэтому перед архивацией произошло разрезервация остатков. Такая же логика и при удалении заказа, если отгрузка не отгружалась, то резервация возвращается обратно. А архивирование по своему поведению совпадает с удалением.
Это штатное поведение функционала резервирования и архивирования.

Штатное поведение Карл! Выключаем архивацию, радуемся правильным остаткам на складе.

О псевдоэлементах в браузерах

, Александра Потапова

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

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

Часто появляющимися псевдоэлементами может похвалиться Google Chrome, впрочем, другим движкам на webkit так же ничего не мешает отличиться.

Надо методично стилизовать все внутренние стили браузера.

Например, довольно часто встречается баг фича с input, когда на input с типом search появляется «синий крестик». На самом деле это кнопка, которая чистит ввод поиска пользователя. Кнопка может и полезная, но практика показывает, что с точки зрения дизайна сайта она совершенно не приемлема и не вписывается в общую концепцию. Убирается эта кнопка по средствам следующей конструкции:

input[type=search]{ -webkit-appearance: none;}

Еще одним интересным примером является настройка placeholder все в том же input. Помимо стандартного: цвета шрифта и бекграунда формы, выявилась интересная особенность ее отображения внешнего вида все в том же Google Chrome. Суть проблемы заключалась в том, что слово, заложенное в placeholder на темном бекграунде так же оставалось темным. Несмотря на значение color: #fff для input, ничего не менялось. Решение представлено ниже. Обратите внимание на двойное двоеточие. Если оно будет одинарным, то правило не сработает:

input[type=text]::placeholder{opacity: 1; color: #fff;}

В <input type=“number”> в разных браузерах появляются «стрелочки» прокрутки цифр. Для разных браузеров убираются они по-разному.

Для браузеров на webkit:

input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
}

Для Firefox:

input[type='number'] {
-moz-appearance: textfield;
}

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

Символы для генерации капчи или пароля

, Михаил

Очень часто нужно сделать генерацию пароля, который пользователь должен вводить при входе, или генерировать код капчи. Но мы не задумываемся - как распознавать символы который похожи друг на друга? Например I и l - это i и L, O и 0 - Буква и цифра. А если используется шрифт, где буквы и цифры практически одинаковые, то всё становится очень сложно.

Делаем алфавит только по символам, которые нельзя трактовать двояко:

23456789ABCDEFHKMNPRSTUXYZabcdefhkmprstuxyz

Исключаем 1iIlLjJgG0OoQqVvWw

Работа с чужим кодом на сайте. Наследие фриланса.

, Михаил

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

Почему разработчики не любят работать с чужим кодом?

Это нежелание вызвано в основном трудностью в прогнозировании сроков и стоимости работ. Это - то же самое, что сказать таксисту: «Отвези меня в Тридевятое царство». Таксист спрашивает: «Где это?». «Не знаю» - отвечаете вы, и при этом требуете от таксиста озвучить сроки и стоимость.

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

При этом помним, что в России крайне низкое качество php кода. Все высококлассные программисты работают в крупных веб-студиях на высоких зарплатах. На фрилансе, их просто нет.

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

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

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

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

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

Однако, мы можем предложить работу с чужим кодом нашим заказчикам.

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

Автозапуск Sypex Dumper по ссылке

, Михаил

Sypex Dumper можно запускать по cron, но нельзя запустить вручную сохранённую задачу.
Добавляем на самую первую строчку index.php

if (isset($_GET['startjob']) && $_GET['startjob']=="Y") { $argc=2; $argv=["index.php","-j=job_name"]; }

И теперь по ссылке можно запускать предустановленную задачу.

Отлючаем bootstrap.css у 1С-Битрикс

, Михаил

Добавляем в init.php обработчик и у нас нет встроенного бутстрапа, даже если включить объединение стилей - тоже сработает. т.к. файлы ядра не добавляются в единый файл


<?php 
\Bitrix\Main\EventManager::getInstance()->addEventHandler("main""OnEndBufferContent""deleteKernelCss");
function deleteKernelCss(&$content) {
	    global $USER$APPLICATION;
	    if(strpos($APPLICATION->GetCurDir(), "/bitrix/")!==false) return;
	    if($APPLICATION->GetProperty("save_kernel") == "Y") return;
	    $arPatternsToRemove = Array(
	        '/<link.+?href=".+?bitrix\/css\/main\/bootstrap.css[^"]+"[^>]+>/',
	    );
	    $content preg_replace($arPatternsToRemove""$content);
	    $content preg_replace("/\n{2,}/""\n\n"$content);
	}
?>

Запрещаем удаление оплат и отгрузок из заказов Битрикса при синхронизации с 1С

, Михаил

Уже долгое время компания 1С-Битрикс сделала полную синхронизацию заказа 1С и БУС, что черевато ситуацией - удаляются отгрузки и оплаты

Временное решение данной проблемы init.php:


<?php 

use \Bitrix\Main\EventManager;
use \Bitrix\Main\Event;
use \Bitrix\Main\Entity;
use \Bitrix\Sale\Order;
use \Bitrix\Sale\Payment;
use \Bitrix\Sale\PaySystem\Manager;
use \Bitrix\Sale\Shipment;
use \Bitrix\Sale\Helpers\Admin\Blocks\OrderBasketShipment;
$inst EventManager::getInstance();
$inst-> addEventHandler('sale''OnBeforeCollectionDeleteItem''saveInfo');
$inst-> addEventHandler('sale''OnSaleOrderBeforeSaved''reverseInfo');
//Небольшая прослойка, возвращает доступные поля
/**
 * @param array $arValues
 * @param array $allowedFields
 * @return array $result
 */
function checkFields$arValues$allowedFields) {
	   $result = array();
	   foreach ( $arValues as $key => $value ) {
		      if ( in_array$key,$allowedFields ) && !in_array($key, array('ACCOUNT_NUMBER')) ) {
			         $result[$key] = $value;
			}
		}
	   return $result;
	}
function saveInfo(\Bitrix\Main\Event $event ) {
	   /**
	    * @var \Bitrix\Sale\Shipment|\Bitrix\Sale\Payment $entity
	    */
	   if ( $_SESSION['BX_CML2_EXPORT'] ) {
		      $entity $event->getParameter('ENTITY');
		      if ( $entity instanceof Shipment ) {
			         if ( !is_array$_SESSION['BX_CML2_EXPORT']['DELETED_SHIPMENTS'] )  )
			            $_SESSION['BX_CML2_EXPORT']['DELETED_SHIPMENTS'] = array();
			         if ( !$entity->isSystem() )
			            $_SESSION['BX_CML2_EXPORT']['DELETED_SHIPMENTS'][] = checkFields$entity->getFields()->getValues(), Shipment::getAvailableFields() );
			}
		      if ( $entity instanceof Payment ) {
			         if ( !is_array$_SESSION['BX_CML2_EXPORT']['DELETED_PAYMENTS'] )  )
			            $_SESSION['BX_CML2_EXPORT']['DELETED_PAYMENTS'] = array();
			         $_SESSION['BX_CML2_EXPORT']['DELETED_PAYMENTS'][] = checkFields$entity->getFields()->getValues(), Payment::getAvailableFields() );
			}
		}
	   else {
		      return;
		}
	}
function reverseInfo(\Bitrix\Main\Event $event ) {
	   /**
	    * @var \Bitrix\Sale\Order $order
	    * @var \Bitrix\Sale\ShipmentCollection $shipmentCollection
	    * @var \Bitrix\Sale\Shipment $shipment
	    * @var \Bitrix\Sale\PaymentCollection $paymentCollection
	    * @var \Bitrix\Sale\Payment $payment
	    * @var \Bitrix\Sale\PropertyValue $somePropValue
	    * **/
	   if ( $_SESSION['BX_CML2_EXPORT'] ) {
		      $order $event->getParameter("ENTITY");
		      if ( $_SESSION['BX_CML2_EXPORT']['DELETED_SHIPMENTS'] ) {
			         //Вернем отгрузки
			         $shipmentCollection $order->getShipmentCollection();
			         $systemShipmentItemCollection $shipmentCollection->getSystemShipment()->getShipmentItemCollection();
			$products = array();
			         $basket $order->getBasket();
			         if ($basket)
			         {
				            /** @var \Bitrix\Sale\BasketItem $product */
				            $basketItems $basket->getBasketItems();
				            foreach ($basketItems as $product)
				            {
					               $systemShipmentItem $systemShipmentItemCollection->getItemByBasketCode($product->getBasketCode());
					               if ($product->isBundleChild() || !$systemShipmentItem || $systemShipmentItem->getQuantity() <= 0)
					                  continue;
					               $products[] = array(
					                  'AMOUNT' => $product->getQuantity(),
					                  'BASKET_CODE' => $product->getBasketCode()
					               );
					}
				}
			         /** @var \Bitrix\Sale\Shipment $obShipment */
			         /** @var array $shipmentFields */
			         foreach ( $_SESSION['BX_CML2_EXPORT']['DELETED_SHIPMENTS'] as $shipmentFields ) {
				            $fg true;
				            foreach( $shipmentCollection as $obShipment ) {
					               if ($obShipment->isSystem())
					                  continue;
					               $usedFields checkFields($obShipment->getFields()->getValues(), Shipment::getAvailableFields() );
					               if ( countarray_diff_assoc$shipmentFields$usedFields) ) == )
					                  $fg false;
					 //доставка с такими полями уже есть
					}
				            if ( $fg ) {
					               $shipment $shipmentCollection->createItem();
					               $shipment->setFields$shipmentFields );
					               OrderBasketShipment::updateData($order$shipment$products);
					}
				}
			         unset( $_SESSION['BX_CML2_EXPORT']['DELETED_SHIPMENTS'] );
			}
		      if ( $_SESSION['BX_CML2_EXPORT']['DELETED_PAYMENTS'] ) {
			         //Вернем оплаты
			         $paymentCollection $order->getPaymentCollection();
			         /** @var \Bitrix\Sale\Payment $obPayment */
			         /** @var array $paymentFields */
			         foreach ( $_SESSION['BX_CML2_EXPORT']['DELETED_PAYMENTS'] as $paymentFields ) {
				            $fg true;
				            foreach( $paymentCollection as $obPayment ) {
					               $usedFields checkFields$obPayment->getFields()->getValues(), Payment::getAvailableFields() );
					               if ( countarray_diff_assoc$paymentFields$usedFields) ) == )
					                  $fg false;
					 //такая оплата уже есть
					}
				            if ( $fg ) {
					               $payment $paymentCollection->createItem();
					               $payment->setFields$paymentFields );
					}
				}
			         unset( $_SESSION['BX_CML2_EXPORT']['DELETED_PAYMENTS'] );
			}
		      //Проверим сумму заказа
		      $paymentCollection $order->getPaymentCollection();
		      if ( ($sumP $paymentCollection->getSum() ) != ($sumO $order->getPrice() ) ) {
			         $diff $sumO $sumP;
			         $innerPayID Manager::getInnerPaySystemId();
			         foreach ( $paymentCollection as $payment ) {
				            if ( $payment->getPaymentSystemId() != $innerPayID) {
					               $newVal floatval($payment->getField("SUM")) + floatval($diff);
					               $payment->setField("SUM"$newVal);
					}
				}
			}
		}
	}

?>

Устанавливаем Viber на Ubuntu 18

, Михаил

Скачиваем Viber с официального сайта

wget -O ~/viber.deb http://download.cdn.viber.com/cdn/desktop/Linux/viber.deb

Дальше нужно пересобрать пакет с новой зависимостью libcurl4

dpkg-deb -x viber.deb viber
dpkg-deb --control viber.deb viber/DEBIAN

Меняем libcurl3 на libcurl4

nano viber/DEBIAN/control

Собираем пакет обратно, и устанавливаем. Минут 10 пакет будет собираться.

dpkg -b viber vibernew.deb
sudo dpkg -i vibernew.deb

Первый взгляд на Font Awesome 5

, Александра Потапова

Несмотря на относительно давний релиз Font Awesome 5, руки его хоть сколько-нибудь обозреть дошли только сейчас.

Итак, что же такое Font Awesome и чем версия 4.7.0 отличается от 5?

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

Данный шрифт базируется на возможностях CSS и LESS.

Впервые Font Awesome показал себя миру 21 августа 2012 года и с тех пор прочно вошел в набор инструментов многих фронтенд разработчиков.

Шло время, шрифт набирал популярность, обновлялся, и, совсем недавно вышел релиз Font Awesome 5. Для того, чтобы его создать, команда разработчиков создала страницу на кикстартере и уже через месяц собрала сумму в 3 раза превышающую запрошенный бюджет.

Итак, 7 декабря 2017 года мир увидел обновленный Font Awesome. В чем же отличия от старой версии? Ну, во-первых, само количество иконок 946 бесплатных иконок (и +1 535 в Про-версии. Итого 2481 новых иконок) против 675 в версии 4.7.0. Кстати, введение Font Awesome 5 Pro-версии тоже стало нововведением. Отличием от бесплатной версии является более разнообразный дизайн уже имеющихся иконок. Появилось разделение на «толстые», «обычные» и «тонкие» символы. Во-вторых больше функций и возможностей. Но, обо всем поподробнее ниже.

Не смотря на релиз в декабре 2017 года, на момент написания статьи вышло уже несколько обновлений (Font Awesome 5.0.8). Шрифт до сих пор дорабатывается. Например, до сих пор налаживается поддержка для Android и iOS.

Подключается Font Awesome 5 так же, как и его младший собрат, то есть 2-мя способами: как сторонний подключаемый из вне компонент и как скачиваемый и внедряемый шрифт. Если вы имели дело с более ранними версиями, то с подключением нового Font Awesome у вас не будет проблем. Сохранилась функция изменения размера иконки непосредственно в ее теге. Например, при добавлении класса fa-sm вы получите иконку равную .875em. так же остались анимированные символы. А вот в отличие от предыдущих версий появились интересные функции Power Transforms, Masking и Layering, Text, & Counters.

Так что же в них интересного? Power Transforms позволяет как бы увеличивать и выносить иконку за пределы ее бэкграунда. Благодаря этому, создается эффект цельности фона и иконки. С помощью Masking можно одни иконки накладывать на другие. Layering, Text, & Counters в целом, выполняет те же функции, что и Masking, но с гораздо большим количеством иконок и их вариативностью расположения.

Поддержка осуществляется всеми браузерами начиная от IE 10 и IE 11 и заканчивая Яндексом в последних двух версиях.

Но, пожалуй, наиболее важным является возможность подключения Font Awesome как SVG и через JavaScript.

Обо всем этом, можно подробнее ознакомиться непосредственно на сайте Font Awesome 5

В целом, новая версия Font Awesome оставила приятное впечатление: как и прежде, простота в подключении, основной контент и набор самых распространенных иконок по-прежнему бесплатен, понятная документация в открытом доступе, приятный дизайн новых и редизайн старых иконок, больше возможностей для реализации всех творческих замыслов, удобный конструктор создания своих уникальных символов за счет функций Masking и Layering, Text, & Counters. Ну и конечно, совместимость с SVG и JavaScript.

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

Дивасофт на «Продвижение без пафоса»

, Александра Потапова

Как-то невзначай, на календаре обозначилась дата - 20 февраля, а это значит, что пора собираться на конференцию «Продвижение без пафоса», что, в сущности, и сделала команда Дивасофт.

Данная конференция посвящена SEO, SMM, контекстной рекламы и E-mail маркетинга и проводилась второй раз. Было приглашено множество спикеров из разных регионов России. А количество участников стремилось перевалить за цифру в 300 человек.

foto.jpg

Нами было решено первым посетить доклад Алексея Трудова с темой: «Извлекаем реальную пользу из кейсов по интернет-маркетингу». И, в целом, ни разу не пожалели. Доклад оказался интересным, ёмким, с простыми аналогиями и случаями из жизни.

foto2.jpg

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

Так же, нам запомнился Дмитрий Сидорин с докладом «Вирусный SMM при ограниченном бюджете». Отличительной чертой его выступления был акцент на юмор, как в повествовании самой лекции, так и применительно для работы. Спикер привел множество примеров из практики, когда юмор, как инструмент маркетинга, помог развернуться некоторым проектам.

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

После приятной трапезы, мы продолжили окунаться в мир SEO. Следующими докладами который нам понравились и запомнились были от Ксении Хайт и Ильи Исерсона.

Ну и в завершении, вишенкой на торте стал доклад Ксении Аникеевой: «Нестандартные возможности аналитических систем на примере Рамблер/топ-100: персональные рекомендации, бренд-лифты и аналитика для SMM-менеджеров.». Как видно из названия, Ксения много рассказывала о новых технологиях Рамблера, некоторые из которых решили и мы взять себе на вооружение.

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

Ну что ж, посмотрим, что организаторы покажут нам в следующем году!

Типовое задание для интеграции вёрстки интернет магазина

, Михаил

Есть вёрстка (набор html шаблонов) для Интернет магазина. Все скрипты, шрифты, изображения и дополнительные стили лежат на одном уровне с шаблоном, в соответствующих каталогах.

Основной файл стилей называется styles.css, и лежит на первом уровне.
Если используется less, то итоговый файл так же компилируется в один styles.css
Допускается размещение font-awesome.min.css на одном уровне с styles.css

Структура шаблона для интеграции

Предоставляются все типовые страницы, кроме корзины (sale.basket.basket), оформления заказа (sale.order.ajax), истории заказов, детального заказа, отмены заказа. Так же не верстается блок с фильтром (catalog.smart.filter)

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

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

Платёжная система - тип "Наличный расчет (cash)"
Доставка - никаких авто расчётов, только названия и логотипы Транспортных компаний.

Рекапча на сайте от гугла. Подключаем вот так https://divasoft.ru/blog/podklyuchenie-neskolkikh-recaptcha2-v-bitrikse/

Для доп.настроек используем Настройки++

На главной есть форма подписки - ajax добавление подписчика, в т.ч анонимного, без лишних вопросов.

Убираем индикатор ajax загрузки у Битрикс

, Михаил

Добавляем стиль, и блок больше не показывается

div[id^="wait_"] { display: none !important; background: none !important; border: 0 !important; color: #000000; font-family: Verdana, Arial, sans-serif; font-size: 11px; font-style: normal !important; font-variant: normal !important; font-weight: normal; letter-spacing: normal !important; line-height: normal; padding: 0 !important; position: absolute; text-align: center !important; text-indent: 0 !important; width: 0px !important; height: 0px !important; word-spacing: normal !important; z-index: 0; content: ""; }

Находим актуальную версию Flashphoner WebSDK 2.0

, Михаил

Сложно найти в этом огромном списке актуальную версию сборки. Теперь с помощью небольшого парсера получаем свежую дату и название файла для скачивания.

Результат можно посмотреть тут


<?php 

    public function getListUpdates() {
	        $html file_get_contents('https://flashphoner.com/downloads/builds/flashphoner_client/wcs_api-2.0/');
	        $build [];
	        $doc = new DOMDocument();
	        $doc->loadHTML($html);
	        $xpath = new DOMXpath($doc);
	        $xPathText $xpath->query('//table//tr');
	        foreach ($xPathText as $row) {
		            $a $xpath->query('.//td[2]/a'$row);
		            $date $xpath->query('.//td[3]'$row);
		            if (trim($a->item(0)->nodeValue) != "Parent Directory" && trim($date->item(0)->nodeValue) != "") {
			                $build[trim($date->item(0)->nodeValue)] = trim($a->item(0)->nodeValue);
			            }
		        }
	        uksort($build"cmpDate");
	        reset($build);
	        return ["date" => key($build), "file" => current($build)];
	    }

?>

И функция для сравнения двух дат


<?php 
function cmpDate($a$b) {
	    $cA strtotime($a);
	    $cB strtotime($b);
	    if ($cA == $cB) {
		        return 0;
		    } else {
		        return ($cA $cB) ? : -1;
		    }
	}
?>

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


<?php 

        file_put_contents($this->updateFilefile_get_contents($this->urlUpdate $this->freshBuild['file']));
        // Распаковываем обновления
        exec("tar -zxvf {$this->updateFile} -C {$this->archive_dir}");
        // Определяем набор файлов для обновления
        $findFirst glob($this->archive_dir "/*"GLOB_ONLYDIR);
        reset($findFirst);
        $unpackedDir current($findFirst);
        $filesUpdate = [
            "$unpackedDir/examples/demo/dependencies/websocket-player/video-worker2.js" => "$this->pathToUpdate/video-worker2.js",
            "$unpackedDir/examples/demo/dependencies/websocket-player/WSReceiver2.js" => "$this->pathToUpdate/WSReceiver2.js",
            "$unpackedDir/examples/demo/dependencies/js/utils.js" => "$this->pathToUpdate/utils.js",
            "$unpackedDir/media-provider.swf" => "$this->pathToUpdate/media-provider.swf",
            "$unpackedDir/flashphoner.min.js" => "$this->pathToUpdate/flashphoner.min.js",
            "$unpackedDir/flashphoner.js" => "$this->pathToUpdate/flashphoner.js",
             // С player.js осторожнее, т.к. в нём изменения, потом сравниваем в редакторе
            "$unpackedDir/examples/demo/streaming/embed_player/player.js" => "$this->pathToUpdate/player.upd.js",
        ];
        // Копируем обновлённые файлы
        foreach ($filesUpdate as $fileFrom => $fileTo) {
	            copy($fileFrom$fileTo);
	        }

?>

Важное уведомление от 1С-Битрикс

, Михаил

С 1 января 2018 года будет ограничена поддержка 1С-Битрикс на PHP версии ниже 5.6.
Обратитесь в компанию Дивасофт, мы поможем перейти на подходящую версию PHP!

Рекомендации по выбору виртуального сервера (VPS) для 1С-Битрикс

, Михаил

Мы перепробовали много хостинговых компаний.
Самый выгодный и удобный оказался Flops.

В частности тариф "Оплата за потребление" - зачем платить постоянно за полную мощность, когда есть вариант платить только за потреблённые ресурсы.
Так же есть возможность быстро добавить или убрать оперативную память, добавить или убрать ядра процессоров, расширить место на SSD диске.

Все действия без доплаты, в течении нескольких секунд. Можно сделать в 1 клик клон сервера, потом его быстро удалить. Конечно не amazon, но в РФ это очень интересный вариант!

Скидка и промокод на Битрикс24

, Михаил

Используйте при регистрации промокод divasoft5gb, что бы получить бесплатные 5Gb места в вашем Битрикс24!

Закажите через нас продление Битрикс24, со скидкой 5%!

Динамическое изменение данных в CRM-форме Битрикс24

, Михаил

Если нужно разместить на странице несколько кнопок, которые будут заполнять значения одной CRM-Формы, которые будут приходить по ajax - нужно сделать так:

  • Добавляем на страницу скрытую кнопку с классом, b24-web-form-popup-btn-X, где X = ID Битрикс формы
  • Используем функцию reinitB24Dvsform(), в данном случае на вход идёт значение поля формы LEAD_TITLE, можно вызывать в скрипте, или по событию onclick

<?php 

// JS код
/**
 * Инициализируем настройки формы, ОБЯЗАТЕЛЬНО указываем ref
 */
function initB24CrmDvsForm(b24val) {
	    return {"id":"8","lang":"ru","sec":"y8awav","type":"button","click":"""ref":'https://ACCOUNT.bitrix24.ru/bitrix/js/crm/form_loader.js'"fields": {
			        'values'b24val
			    }};
	}
var b24paramsload initB24CrmDvsForm({});
/**
 * Стандартное добавление скрипта и переменных для скрипта Б24
 */
(function(w,d,u,b){w['Bitrix24FormObject']=b;
	w[b] = w[b] || function(){arguments[0].ref=u;
		        (w[b].forms=w[b].forms||[]).push(arguments[0])};
	        if(w[b]['forms']) return;
	        s=d.createElement('script');
	r=1*new Date();
	s.async=1;
	s.src=u+'?'+r;
	        h=d.getElementsByTagName('script')[0];
	h.parentNode.insertBefore(s,h);
	})(window,document,b24paramsload.ref,'b24form');
b24form(b24paramsload);
/**
 * Реинициализируем форму, частично повторяем функцию init у Bitrix24FormLoader
 */
function reinitB24Dvsform(nVal) {
	    if(!window.Bitrix24FormObject || !window[window.Bitrix24FormObject])
	        return;
	    if(!window[window.Bitrix24FormObject].forms)
	        return;
	    // Уничтожаем форму
	    Bitrix24FormLoader.unload(b24paramsload);
	    // Пересоздаём параметры формы
	    b24paramsload initB24CrmDvsForm({'LEAD_TITLE'nVal});
	    // Инициируем форму с новыми данными
	    Bitrix24FormLoader.params b24paramsload;
	    Bitrix24FormLoader.init();
	    // Открываем попап
	    Bitrix24FormLoader.showPopup(b24paramsload);
	}

?>

Исправляем ошибку 400 Bad Request при включении https у 1С-Битрикс

, Михаил

После включение редиректа на https, в некоторых случаях появляется ошибка 400 Bad Request The plain HTTP request was sent to HTTPS port

Всё происходит из за mod_dir, он берет на себя редирект с папки без слеша на папку с слешом, но он не воспринимает "HTTPS on" как побудитель использования схемы https:// 

Что бы всё это заработало, нужно:

  • В конфигах nginx'a ничего не трогаем
    proxy_set_header       Host       $host:443;
  • В конфиге апача который отвечает за ваш домен
    Если у вас конфигурация многосайтовая - /etc/httpd/bx/conf/bx_ext_site.local.conf
    односайтовая - /etc/httpd/bx/conf/default.conf
    К названию сервера ServerName  site.local  дописываем:
    ServerName  https://site.local 

смысл следующий: http://httpd.apache.org/docs/2.2/mod/core.html#servername

Sometimes, the server runs behind a device that processes SSL, such as a reverse proxy, load balancer or SSL offload appliance. When this is the case, specify the https:// scheme and the port number to which the clients connect in the ServerName directive to make sure that the server generates the correct self-referential URLs.

Так же можно применить вот это решение Чиним ошибку 400 Bad Request с помощью mod_rpaf у BitrixEnv

Массовая атака криптором Wana decrypt0r 2.0

, Михаил

Более 60.000 компьютеров подверглись атаке и заражению вирусом-вымогателем Wana Decrypt0r. Авторы Wana Decrypt0r используют эксплоит ETERNALBLUE, созданный специалистами АНБ для уязвимости в SMBv1 (MS17-010) для доставки вредоносного кода на Windows системы. Вирус зашифровывает все файлы на компьютере и требует выкуп — 300 долларов США в биткоинах. На выплату дается три дня, потом сумма увеличивается вдвое.

Группа экспертов по кибербезопасности MalwareHunterTeam утверждает, что больше всех в результате атаки пострадали серверы на территории России и Тайваня. Под ударом оказались и другие страны: Великобритания, Испания, Италия, Германия, Португалия, Турция, Украина, Казахстан, Индонезия, Вьетнам, Япония и Филиппины.

Интерактивная карта заражения всего мира в реальном времени

Ссылки на пакеты исправления для различных систем

Уязвимость так-же можно закрыть, полностью отключив поддержку SMBv1 и закрыв 135 и 445 порт. Для этого достаточно выполнить следующую команду в командной строке запущенной от имени Администратора:

dism /online /norestart /disable-feature /featurename:SMB1Protocol netsh advfirewall firewall add rule dir=in action=block protocol=TCP localport=135 name="Block_TCP-135" netsh advfirewall firewall add rule dir=in action=block protocol=TCP localport=445 name="Block_TCP-445"


Распространение вируса-вымогателя WannaCrypt удалось приостановить, зерегистрировав домен iuqerfsodp9ifjaposdfjhgosurijfaewrwergwea.com.
Специалист по безопасности, который ведет твиттер @MalwareTechBlog, обнаружил, что вирус зачем-то обращается к этому домену и решил зарегистрировать его, чтобы следить за активностью программы.

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

Вирус-вымогатель начал распространяться 12 мая. Он блокирует компьютер и требует 300 долларов за разблокировку. Десятки тысяч заражений зарегистрировали в 99 странах, в том числе в России, где жертвами стали «Мегафон», МВД и СК. В других странах пострадали телекоммуникационные компании, банки, консалтинговые фирмы. В Великобритании из строя вышли компьютеры системы здравоохранения.
Регистрация вышеупомянутого домена не помогла тем, кто уже был заражен, но дала время другим установить обновление Windows, после которого вирус не работает.

Появился набор эксплоитов FuzzBunch, который группа хакеров Shadow Brokers украла у Equation Group, хакеров из Агенства Нац. Безопасности США. Microsoft потихому прикрыла дырки обновлением MS 17-010, возможно самым важным обновлением за последние десять лет. В то время как набор эксплоитов уже неделю лежит в открытом доступе https://github.com/fuzzbunch/fuzzbunch с обучающими видео. В этом наборе есть опасный инструмент DoublePulsar. Кратко - если открыт 445 порт и не установлено обновление MS 17-010, то DoublePulsar простукивает этот порт, делает перехваты системных вызовов и в память внедряет вредоносный код.

Интернет Касса в 1С-Битрикс (Соответствие ФЗ-54)

, Михаил

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

  1. Заключить договор с ОФД на передачу данных
  2. Приобрести ККМ со встроеным Фискальным Накопителем
  3. Зарегистрировать ККМ в ФНС
  4. Подключить ККМ к ОФД
  5. Скачать и установить драйвера ККМ на локальный компьютер
    Драйвер для работы кассового аппарата в режиме формирования чека
    Ставится для поддержки виртуальных COM-портов В драйверах содержится программа «Тест драйверов («fptr_t.exe», в каталоге «nt-x86-mingw»)» - требуется для настройки и просмотра портов
    Скачать здесь

    Драйвер для отправки кассой данных в ОФД
    Ставится для работы службы «Ethernet over USB» и настраивается с помощью «DTOintegrator.exe», в каталоге «\Program Files (x86)\ATOL\Drivers8\Bin\». Настраивается в режиме «Администратора»
    Скачать здесь
  6. Скачать (msiexe) и установить приложение 1С-Битрикс.Кассы
  7. Обновить 1С-Битрикс Управление сайтом до версии 17.хх
  8. Установите галочку «Разрешить печать чеков» в настройках платежной системы, если требуется печатать чек.

Обращайтесь в компанию Дивасофт, мы поможем вам с ФЗ-54
ККМ ФЗ-54

ФЗ-54 и что с этим делать клиенту и разработчику

, Михаил

С 1 февраля 2017 вступил в силу новая версия 54-ФЗ «О применении контрольно-кассовой техники». Новый порядок применения ККТ предполагает, что данные о продажах с каждого выбитого чека должны передаваться в налоговую инспекцию через интернет. То есть вместо касс с ЭКЛЗ придется использовать кассы с фискальным накопителем (ФН).

При чем тут интернет-магазины?

Если раньше использование касс, при оплате заказа при помощи банковских карт, электронных денег(электронных средств платежа) в интернет-магазинах (ИМ) не требовалось, то теперь, вне зависимости от того как была произведена оплата, владельцы ИМ будут обязаны пробить чек и предоставить его покупателю как минимум в электронном виде в течении пяти минут. Причем зарегистрировать чек в налоговой надо сразу после поступления денежных средств. Электронный чек должен быть отправлен на e-mail покупателя указанный при получении заказа. Все данные о чеках сохраняются в фискальном накопителе.

Внимание! Если ИМ получает деньги с помощью Киви-кошелька, банковских карт, Вебмани, Yandex.Денег — надо печатать чек.

Под закон не подпадают только банковские переводы. Пример: ИМ выставил счёт клиенту, клиент идёт на почту или в банк, оплачивает. Вот тут ИМ не пробивает чек.

В остальных случаях — выбивает. Это самое большое изменение. Кассовый аппарат регистрируется. Чек выписывается сразу.

Печатать чеки не обязательно, физический чек не нужен, если ИМ не работает с оффлайн-клиентами. Для таких ИМ существую специальные интернет-кассы без печатного блока (например, АТОЛ 42ФС)
atol42fs.jpg

Что надо будет делать клиенту по ФЗ-54?

  1. Нужно приобрести кассу с поддержкой ФЗ-54 или модернизировать старую. Можете порекомендовать клиентам «Атол» или «Штрих-М».(ссылки) Драйверы универсальные, вся линейка «Атол» прекрасно работает с нашим решением. «ШТРИХ-М» тоже будет работать в ближайшее время.
  2. Проверить, возможна ли модернизация текущей кассы или подобрать новую можно на сайте: https://kkt-online.nalog.ru/#_rkkt
  3. Клиент заключает договор с ОФД. Это легко. Клиент регистрирует кассу в ФНС. Подключает её к ОФД. Нужно прописать внутри касс определённые параметры. Обычно это делают сервисные компании по продаже касс.
  4. Нужно установить и настроить драйвер кассы(ссылки). Устанавливаете приложение «1С-Битрикс». Система сама найдёт конкретный магазин, сама заберёт нужные данные, сама получит ключи авторизации.
  5. Нужно настроить кассу в «1С-Битрикс: Управление сайтом». Подключаете кассу внутри сервиса, настраиваете и клиент работает по новому закону.

Когда надо перейти?

  • С 1 февраля 2017 года вступил в силу запрет на регистрацию касс старого образца, а регистрация онлайн-касс стала обязательной. Пока еще работать можно как на кассах с ЭКЛЗ, так и на кассах с ФН.
  • С 1 июля 2017 года кассы с ЭКЛЗ станут вне закона. Работать можно будет только на новых кассах с ФН.
  • С 1 июля 2018 – кассы должны использовать все предприниматели, которые раньше могли их не использовать: ПСН, ЕНВД. ИП, оказывающие услуги должны будут выдавать бланк строгой отчетности.

Что если владелец ИМ не будет использовать новую кассу?

Неприменение ККТ (первый инцидент):

  • для должностных лиц и ИП - 25-50% суммы чека, но не менее 10 000 рублей;
  • для юридических лиц - 75-100% суммы чека, но не менее 30 000 рублей.

Повторный инцидент неприменения ККТ:

  • приостановление деятельности ИП или юр. лица на срок до 90 суток;
  • для должностных лиц - дисквалификация до 2 лет.

Не направление покупателю электронного чека:

  • для должностных лиц и ИП - до 2 000 рублей;
  • для юридических лиц - до 10 000 рублей.

Незаконная работа с кассовым аппаратом старого образца:

  • для должностных лиц и ИП - до 3 000 рублей.
  • для юридических лиц - до 10 000 рублей

Чем поможет «1С-Битрикс»?

Есть бесплатное приложение «1C-Битрикс.Кассы». Оно ставиться на компьютер, и к нему подключается касса.

Приложение работает по принципу сервера:

  • подключается к вашему интернет-магазину;
  • получает от него данные;
  • отправляет их на кассу.

Касса сама не умеет инициировать соединение. Она умеет как принтер — получать данные. Значит нужен посредник, который будет связан с интернет-магазином. Он будет получать данные и печатать на этом принтере. Таким посредником является «1С-Битрикс.Кассы»

Сейчас поддерживаются два типа аппаратов: «Атол» и «Штрих-М». На данный момент все тесты происходят с аппаратами «Атол». Со «Штрих-М» пока есть технические задержки.

Чуть позже разницы не будет, клиент будет выбирать кассу, с которой привычнее работать.

Важно: Приложение работает при наличии активной лицензии «1С-Битрикс»!

Кроме того, к интернет-магазину можно подключить несколько касс, если заказов очень много. Возможно подключение разных юр. лиц, подключение к разным ОФД, поддерживаются все необходимые виды чеков: чек-возврат, чек-аванс, чек с учетом аванса.

Опасный спам

, Михаил

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

По ссылке скачивается schet_oplata.zip, в котором лежит вредоносный счет_нов_xls.js

С первого взгляда может показаться, что в архиве Excel таблица, но будьте внимательны... там js = JavaScript

Содержимое файла доступно по ссылке в блоке ниже, изучайте, но не запускайте

http://pastebin.com/M2ieX5Pk

Варианты подключения скрипта для Asset::getInstance()->addString() у 1С-Битрикс

, Михаил

Третий аргумент для функции addString() можно подсмотреть только в ядре: /bitrix/modules/main/lib/page/asset.php


<?php 

use Bitrix\Main\Page\Asset;
use Bitrix\Main\Page\AssetLocation;
Asset::getInstance()->addString("<script>***</script>"trueAssetLocation::AFTER_JS);
// AssetLocation::BEFORE_CSS;
// AssetLocation::AFTER_CSS;
// AssetLocation::AFTER_JS_KERNEL
// AssetLocation::AFTER_JS

?>

Подключение CSS и JS файлов в шаблоне компонента 1С-Битрикс

, Михаил

Для оформления и реализации front-end логики компонента, в шаблоне доступны не обязательные файлы

  • style.css — дополнительные стили для шаблона;
  • script.js — дополнительные скрипты для шаблона.

Архитектурно правильный способ - создать component_epilog.php


<?php 

global $APPLICATION;
$APPLICATION->AddHeadScript(SITE_TEMPLATE_PATH ."/js/divasoft.js");
$APPLICATION->SetAdditionalCss(SITE_TEMPLATE_PATH ."/css/divasoft.css");
$APPLICATION->AddHeadString("<link href='http://fonts.googleapis.com/css?family=PT+Sans:400&subset=cyrillic' rel='stylesheet' type='text/css'>");

?>

Или в новом стиле D7:


<?php 

use Bitrix\Main\Page\Asset;
Asset::getInstance()->addJs(SITE_TEMPLATE_PATH "/js/divasoft.js");
Asset::getInstance()->addCss(SITE_TEMPLATE_PATH "/css/divasoft.css");
Asset::getInstance()->addString("<link href='http://fonts.googleapis.com/css?family=PT+Sans:400&subset=cyrillic' rel='stylesheet' type='text/css'>");

?>

И теперь самый простой и правильный способ в template.php


<?php 

$this->addExternalCss("/local/styles.css");
$this->addExternalJS("/local/liba.js");

?>

Ещё один способ, если весь шаблон находится в отложенной функции template.php


<?php 

//we can't use $APPLICATION->SetAdditionalCSS()
$css $APPLICATION->GetCSSArray();
if(!is_array($css) || !in_array("/bitrix/css/main/font-awesome.css"$css)) {
	    $strReturn .= '<link href="'.CUtil::GetAdditionalFileURL("/bitrix/css/main/font-awesome.css").'" type="text/css" rel="stylesheet" />'."\n";
	}

?>

Подключение нескольких recaptcha2 в Битриксе

, Михаил

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

В init.php добавляем код, инициализирующий рекапчу. Код добавляется структурно после подключения всех стилей и скриптов, что исключает ошибку раннего старта рекапчи. Код добавляется именно через addString(), а не через addScript():


<?php 

// Открытый ключ
define("DVS_RC_KEY","123");
// Секретный ключ 
define("DVS_RC_SECRET","321");
use Bitrix\Main\Page\Asset;
use Bitrix\Main\Page\AssetLocation;
if (!defined("DVS_RECAPTCHA")) {
	// Подключаем скрипт рекапчи
	    Asset::getInstance()->addString('<script src="//www.google.com/recaptcha/api.js?onload=divaCaptchaRender&render=explicit" async defer></script>',
	 trueAssetLocation::AFTER_JS);
	// Инициализируем массив рекапч
	    Asset::getInstance()->addString("<script>window.rc = {};
	 var divaCaptchaRender = function () {
		        $('.g-recaptcha').each(function() {
			          window.rc[$(this).attr('id')] = grecaptcha.render( this,{ 'sitekey': '" DVS_RC_KEY "', 'theme': 'light'} );
			});
		};
	</script>"trueAssetLocation::AFTER_JS);
	    define("DVS_RECAPTCHA"true);
	}

?>

Дальше мы можем обращаться из любого компонента к объекту рекапчи, которые находятся в массиве window.rc.

Вставка рекапчи:


<?php 
<div id="recaptchaUID" class="g-recaptcha"></div>
?>

Сброс рекапчи:


<?php 
<script>grecaptcha.reset(window.rc[recaptchaUID]);
</script>
?>

Тем самым не зная точно сколько рекапч будет на странице, мы можем работать с ними по идентификатору дива (в данном случае это recaptchaUID), в котором эта капча инициализировалась по классу g-recaptcha.

Принудительное включение https в ядре 1С-Битрикс

, Михаил

Есть недокументированная возможность заставить Битрикс работать по протоколу https (если не корректно настроен хостинг)

Создаём/редактируем файл bitrix/.settings_extra.php


<?php 

return array (
    "https_request" => array("value" => true),
);
 
?>

Как правильно включить отображение ошибок в 1С-Битрикс

, Михаил

В файле bitrix/.settings.php


<?php 
'exception_handling' => 
  array (
    'value' => 
    array (
      'debug' => true,
      'handled_errors_types' => E_ALL & ~E_NOTICE & ~E_STRICT & ~E_USER_NOTICE & ~E_DEPRECATED,
      'exception_errors_types' => E_ALL & ~E_NOTICE & ~E_WARNING & ~E_STRICT & ~E_USER_WARNING & ~E_USER_NOTICE & ~E_COMPILE_WARNING,
      'ignore_silence' => false,
      'assertion_throws_exception' => true,
      'assertion_error_type' => 256,
      'log' => 
      array (
        'settings' => 
        array (
          'file' => 'bitrix/err.log',
          'log_size' => 1000000,
        ),
      ),
    ),
    'readonly' => false,
  )
?>

Логи будут в файле bitrix/err.log

Как можно увеличить количество хранимых логов обмена с 1С на сайте 1С-Битрикс?

, Михаил

Для этого необходимо доработать компонент catalog.import.1c

Где в файле \bitrix\components\diva\catalog.import.1c\class.php

А именно в функции public function cleanUpDirectory($directoryName) есть условие


<?php 
if ($directoryEntry->isDirectory() && $directoryEntry->getName() === "Reports"{} 
?>

В котором нужно отключить $reportsEntry->delete();

Зачем защищать сайт от взлома?

, Михаил

По данным компании Sophos ежедневно взламывают более 50000 сайтов. 9 из 10 проверенных нами сайтов содержат уязвимости, позволяющие злоумышленникам проникнуть в закрытые разделы сайта, разместить вредоносный код, извлечь данные из базы данных или получить доступ к конфиденциальной информации посетителей сайта.

Очевидна причина данной ситуации: владелец и другие разработчики сайта не уделяют достаточного внимания безопасности сайта и защите от взлома.

Дивасофт заботится о безопасности своих клиентов, помогает с защитой и обновлением вашего сайта.

Недостаточное внимание к проблеме безопасности сайта приводит к печальным последствиям: сайт либо перестает полностью функционировать, либо начинает распространять вирусы. В результате взлома в 90% случаев сайт теряет позиции в поисковых системах и посетителей. Поэтому необходимо заранее уделить внимание проблеме безопасности сайта и провести необходимые мероприятия по укреплению защиты.

Защищён — значит сохранил и приумножил прибыль. ©


Зачем лечить сайт от вирусов?

, Михаил

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

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

Если ваш сайт заразился, то Дивасофт поможем вам. Вылечим. Починим. Обновим.

Обещания Google начали сбываться — теперь сайты https помечаются как надёжные

, Михаил
Различие между HTTPS и HTTP

(Различие между HTTPS и HTTP)

Пару месяцев назад Google решила перевести весь интернет на HTTPS, агитируя пользователей браузера Chrome посещать только HTTPS сайты. 
Сейчас начался первый этап — пометка сайтов с https как безопасные.

Следующий этап перехода на https намечен на конец января — в Google Chrome 56 версии. Тут уже будет однозначно сообщаться, что сайт не безопасен.

Что бы посмотреть, как это будет, заходите по адресу chrome://flags/ - это тонкие настройки браузера, вручную скопируйте этот адрес, и перейдите по нему.

Включите настройку: Помечать незащищенные источники как небезопасные Mac, Windows, Linux, Chrome OS, Android = "Отмечать страницы HTTP как опасные"

Но есть и одна особенность, которая позволит сайту не переходить на HTTPS — если у сайта нет форм ввода, то пометки не будет. 

Компания Дивасофт поможет выбрать SSL сертификат, приобрести и установить его на ваш сервер или хостинг.

Программы для удалённого подключения к рабочему столу клиента

, Михаил

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

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

Федеральный закон ФЗ-54 «О применении контрольно-кассовой техники» для интернет-магазинов на платформе 1С-Битрикс

, Михаил

Компания Дивасофт поможет решить эту задачу, установить нужное оборудование, интегрировать кассу с вашим Интернет-магазином, а так же проконсультирует по всем вопросам.

CMS 1С-Битрикс подружит интернет-магазины с физическими кассами для соблюдения нового 54-ФЗ «О применении контрольно-кассовой техники». Сайты смогут подключать к интернет-магазину несколько параллельно работающих касс для соответствия законодательству. С 1 февраля 2017 года вступит в силу часть поправок к 54-ФЗ, которые изменят порядок работы и приема оплаты в интернет-магазинах. В полную силу закон заработает с 1 июля 2017 года. Каждый интернет-магазин должен иметь кассовый аппарат (ККТ), подключенный к интернету и соединенный с оператором фискальных данных (ОФД).

Касса будет оформлять чек на каждую транзакцию в интернет-магазине. А после - отправлять его в ОФД для регистрации. Чек будет дублироваться клиенту на почту. Электронный вариант чека станет полноценной альтернативой бумажному.

Из ОФД данные будут уходить в Федеральную налоговую службу. Штраф за каждую незарегистрированную покупку в интернет-магазине будет составлять до 100% ее суммы, но не меньше 30 000 рублей, а за неправильную кассу или неотправку электронной версии чека клиенту – 10 000 рублей.

Количество заказов в интернет-магазине распределяется неравномерно – в зависимости от времени суток, сезона, акционных предложений. За одну секунду в интернет-магазин может поступать несколько десятков заказов, а текущее кассовое оборудование позволяет печатать не более 2 чеков за секунду.

К интернет-магазинам на «1С-Битрикс» можно будет подключить несколько кассовых аппаратов параллельно. Эта проблема решена.

HTTPS становится обязательным

, Михаил
  • C 1 января 2017 года Google внутри браузеров Chrome, FireFox будет отмечать, что сайт небезопасен.
  • HTTPS становится важным фактором ранжирования сайтов (сайты с HTTPS будут выше в выдаче).

Компания Дивасофт поможет выбрать SSL сертификат, приобрести и установить его на ваш сервер или хостинг.

Бесплатный SSL сертификат от Let's Encrypt

, Михаил

Получить сертификат на 90 дней можно у Let's Encrypt. Для удобства, пользуемся сервисами gethttpsforfree.com и  zerossl.com, что бы сгенерировать эти SSL сертификаты. Потом можно продлить сколько угодно раз. Как вручную, так и автоматически с помощью  certbot

90-дневные сертификаты — вовсе не новость для Всемирной паутины. Согласно телеметрии Firefox, 29% всех TLS-транзакций используют 90-дневные сертификаты, и ни одно иное время жизни не составляет большую долю транзакций. Точка зрения Let's Encrypt состоит в том, что короткие времена жизни сертификатов имеют два главных, основных преимущества:

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

Типы SSL сертификатов

, Михаил
  • Esential SSL — самый не дорогой и быстро оформляемый сертификат. Доступен как для юридических, так и для физических лиц. Проверяется только право владения доменным именем, личные данные или регистрация компании не проверяются. Выдается на 1 домен.
  • Instant SSL — доступен и для физ. лиц, и для юр. лиц. Проверяется право владения доменом, регистрационные данные компании либо личность физ. лица. Выдается на 1 домен.
  • SGC SSL-сертификат. — Аналогично Instant SSL, но с поддержкой 40-битных расширений (актуально для старых ОС и браузеров). Выдается на 1 домен, либо wildcard (см. ниже).
  • Обычный Wildcard. — тоже самое, что и обычный сертификат, но выдается не на 1 домен, а на все поддомены корневого домена. Т.е. не только на domain.com, a и на www.domain.com, bill.domain.com и т.д. Стоит на порядок дороже.
  • EV (Extended Validation) сертификат. — сертификат расширенной проверки, доступен только юридическим лицам. Проверяется владение доменом, компания, нотариально заверенные переводы документов на английский язык, требует подтверждения данных третьей стороной. Позволяет установить на сайте картинку-подтверждение владением и отображается в браузерах как гарантированно доверенный (зеленым цветом), против желтого у обычных сертификатов. Стоит в 2-3 раза дороже обычного, регистрация занимает продолжительное время.
  • EV Wildcard и EV SGC. — Усиленная проверка Wildcard и SGC.
  • Instant и Essential сертификаты позиционируются как продукт для сайтов частных лиц и органзаций, не связанных с электронной коммерцией. Extended Validation — для сайтов, связанных с финансами, услугами (интернет-банкинг, платежные системы, интернет-магазины и пр.).

Компания Дивасофт поможет выбрать SSL сертификат, приобрести и установить его на ваш сервер или хостинг.

Меняем точность округления цены в 1С-Битрикс

, Михаил

Цена/Количество в 1С-Битрикс может округляться до 4 знака. Но что делать, если нам нужна точность до 10 знаков?

Устанавливаем константу /php_interface/init.php define("SALE_VALUE_PRECISION", 10);

В настройках модуля "Интернет-магазин" ставим
Знаков после запятой при выводе количественного значения: Авто

В свойствах таблицы b_sale_basket меняем все длины полей, что содержат 18,4 на 18,10

ALTER TABLE `b_sale_basket` MODIFY `PRICE` decimal(18,10) NOT NULL ;
ALTER TABLE `b_sale_basket` MODIFY `BASE_PRICE` decimal(18,10) NULL DEFAULT NULL  ;
ALTER TABLE `b_sale_basket` MODIFY `QUANTITY` decimal(18,10) NOT NULL DEFAULT "0.0000000000000" ;

Если заказ создаётся через API, а не через админку - то всё хорошо. Если через админку будем менять цену за единицу товара - то всё хорошо, но если "сохранить" без изменений... точность сбросится до 4го знака.

Отключаем микрофон в Android (нужен root)

, Михаил

Нам понадобится рутованный аппарат. Редактируем файл /system/etc/mixer_path.xml
Находим что нибудь связанное с MIC1 / MIC2 / MIC3 и значениями On. Меняем на Off, перезагружаем. Если всё нормально то телефон включится. Если нет - откатываем изменения(через рекавери).
У каждого производителя этот файл уникальный, но принцип одинаковый. 
Так же в этом файле можно увеличить громкость динамиков, и чувствительность/усиление микрофона.

Омеднённый алюминий в локальной сети

, Михаил
  • Омеднённый алюминий это даже на 20-ти метровом отрезке 10 мегабит.
  • Кабель проходит тестирование только на 3-ю категорию. 
  • Хрупкий, проводник ломается при небольших радиусах изгиба. 
  • Плохо обжимается коннектором RJ-45
  • Жила плохо держится в разъеме (плавающий контакт)
  • Небольшая длина сегментов
  • Непредсказуемость волнового сопротивления (импеданса) в зоне контакта при заделки кабеля в IDC разъем розетки или патч-панели
  • Как правило, более тонкая жила на синей (4-5) и коричневой (7-8) парах
  • В большинстве случаев поддерживает только 10Base-T (т.е. только 10 мбит/сек.)
  • Непредсказуемое изменение характеристик при последующей перекладке кабеля
  • Омеднёный алюминий окислится через 15 лет на местах соединений. 
  • Стандарт разрешает длину кабеля не более 120 метров.
    Омеднённый кабель может не завестись на длинных линках - выше 60-80 метров. 
  • Омеднённая витая пара UTP 5(e) дешевле чем медная

Добавляем ID профиля покупателя в свойство заказа 1С-Битрикс на ядре D7

, Михаил
  1. Создаём поле для хранения идентификатора покупателя SUBUSER_ID
  2. Добавляем обработчик OnBeforeSaleOrderFinalAction, что бы установить наше свойство.

<?php 
\Bitrix\Main\EventManager::getInstance()->addEventHandler('sale''OnBeforeSaleOrderFinalAction''OnBeforeSaleOrderFinalActionHandler');
function OnBeforeSaleOrderFinalActionHandler(\Bitrix\Main\Event $event) {
	    $order $event->getParameter('ENTITY');
	    $userProfileId getContragentID($order->getId());
	    $propertyCollection $order->getpropertyCollection();
	    foreach ($propertyCollection as $property) {
		        if($property->getField('CODE') == 'SUBUSER_ID') {
			            $property->setValue($userProfileId);
			}
		}
	    return new \Bitrix\Main\EventResult(
	        \Bitrix\Main\EventResult::SUCCESS, array(
	            "ENTITY" => $order,
	        )
	    );
	}
?>

  1. Дальше находим ID профиля покупателя по ИНН у Юр.Лиц, и по ФИО(названию профиля) у Физ.Лиц.

<?php 
function getContragentID($orderID) {
	    CModule::IncludeModule("sale");
	    if ($arOrder CSaleOrder::GetByID($orderID)) {
		        $profiles = \Bitrix\Sale\Helpers\Admin\Blocks\OrderBuyer::getBuyerProfilesList($arOrder['USER_ID'],$arOrder['PERSON_TYPE_ID']);
		        unset($profiles[0]);
		        $profiles array_flip($profiles);
		        $filterValue = array('PERSON_TYPE_ID' => $arOrder['PERSON_TYPE_ID'], 'IS_PROFILE_NAME' => 'Y');
		        if ($arOrder['PERSON_TYPE_ID']==2) {
			            $filterValue = array('PERSON_TYPE_ID' => $arOrder['PERSON_TYPE_ID'], 'CODE' => 'INN');
			        }
		        $rsOrderProps CSaleOrderProps::GetList(array(), $filterValue);
		        if ($arOrderProp $rsOrderProps->Fetch()) {
			            $rsProps CSaleOrderPropsValue::GetList(array('SORT' => 'ASC'), array('ORDER_ID' => $orderID'ORDER_PROPS_ID' => $arOrderProp['ID']));
			            if ($arProp $rsProps->Fetch()) {
				                $rsUP CSaleOrderUserPropsValue::GetList(array(), array('ORDER_PROPS_ID' => $arOrderProp['ID'],
				                    'VALUE' => $arProp['VALUE'],
				                    'PROP_PERSON_TYPE_ID' => $arOrder['PERSON_TYPE_ID']));
				                if ($arUP $rsUP->Fetch()) {
					                    if (array_key_exists($arProp['VALUE'], $profiles) && $arOrder['PERSON_TYPE_ID']==1) {
						                        return $profiles[$arProp['VALUE']];
						                    }
					                    return $arUP['USER_PROPS_ID'];
					                } else {
					                    $db_sales CSaleOrderUserProps::GetList(array(),array("USER_ID" => $arOrder['USER_ID'],"PERSON_TYPE_ID"=>$arOrder['PERSON_TYPE_ID'],"=NAME"=>$arProp['VALUE']));
					                    if ($ar_sales $db_sales->Fetch()) {
						                       return $ar_sales["ID"];
						                    }
					                }
				            } 
			        }
		    }
	}
?>

Что такое FUSER_ID в 1С-Битриксе

, Михаил

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

Ситуация первая:

  1. Вы не авторизованный пользователь, кладёте товар в корзину - создаётся новый FUSER_ID, пусть это FUSER_X, который записывается в базу и в куки.
  2. Затем вы авторизуетесь под аккаунтом пользователя A. Если к идентификатору пользователя А не привязан никакой FUSER_ID, то он опять же создаётся, привязывается к идентификатору пользователя А (записывается в базу) и пишется в куки, путь это будет FUSER_Y. В это же время все товары не авторизованного пользователя переносятся в корзину авторизованного пользователя.
    Товары покупателя FUSER_X ---> Товары покупателя FUSER_Y.
    Соответственно, теперь к FUSER_X не будет привязано ни одного товара и FUSER_X удаляется из базы.

Ситуация вторая:

  1. Вы авторизованный пользователь А, вашей корзине есть товары, которые привязаны к FUSER_Y.
  2. Вы решили разлогиниться.
  3. В этот момент Вы становитесь не авторизованным пользователем и FUSER_ID из куков удаляется и пишется НОВЫЙ, корзина пуста, т.к. именно по FUSER_ID осуществляется выборка товаров в корзине.

Ситуация третья:

  1. Вы авторизованный пользователь А, вашей корзине есть товары, которые привязаны к FUSER_Y.
  2. Вы не собираетесь разлогиниваться, но получается так, что закончилось время жизни сессии - вы становитесь не авторизованным пользователем. Из куков не удаляется FUSER_ID, он остаётся таким же равным FUSER_Y, т.е. получается, что вы не авторизованный пользователь с FUSER_ID пользователя А, поэтому в корзине присутствуют товары пользователя А.
    Это нормальная ситуация. Согласитесь, пользователю будет не очень приятно, если он набрал 50 товаров в корзину, время сессии закончилось и его товары из корзины пропали.
  3. За этот же компьютер садится другой пользователь, который авторизуется под аккаунтом пользователя В, идентификатор пользователя магазина FUSER_Z. И тут возникает "первая ситуация", перенос товаров от FUSER_Y к FUSER_Z.
  4. В итоге, если пользователь А, авторизуется снова (на другом компьютере или браузере), то его корзина будет пуста.
    Т.е. пользователь B "приобрёл" корзину пользователя А.

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

p.s. FUSER_ID это ID покупателя, но НЕ ID профиля покупателя, это разные сущности.

Требования к дизайн-макетам

, Ashe Gentle
  • Макет должен быть представлен в форматах PSD, цветовое пространство RGB
  • Имя файла должно состоять только из латинских букв, цифр и _ (underscore)
  • Если макет дополняет/изменяет уже существующий, то в его имени должен быть суффикс, указывающий версию (например landing_v2.psd)
  • В рамках макета должна быть соблюдена сетка
  • Направляющие должны быть выровнены точностью до одного пикселя. Полпикселя не допустимо
  • В случае фиксированного шаблона должно быть четкое соответствие ширины макета утвержденной минимальной ширине сайта. Ширина макета всегда должна быть меньше подразумеваемой ширины устройства отображения на 20-24 пикселей
  • При разработке дизайна «под разрешение» обязательно отрисовывать в разрешение окна браузера, а не монитора (при 1024 пкс ширина браузера 1000 пкс)
  • Желательно в виде отдельных файлов прикладывать все паттерны, используемые в макете
  • Обязательно наличие отдельного макета, в котором представлено оформления стандартных элементов типографики веб-страницы (заголовки, параграфы, таблицы, списки, блоки цитаты)

Слои (layers)

  • Каждый элемент должен находиться в одном слое, названным человечески-понятным именем, а не «slice 1,2».
  • Все слои одного логического элемента должны быть в одной папке (например, модуль авторизации)
  • Если применяется градиент к слою, использовать обычный режим наложения (blend Mode: normal) и его реальные цвета
  • Не должно быть никаких полупрозрачных градиентов и сложных режимов наложения типа «multiply, screen, overlay, и т.д.»
  • Нельзя использовать сложные режимы наложения (blend mode) на любых свойствах слоя (типа inner shadow, drop shadow и т.д.)
  • Нельзя использовать режим «Наложение цвета»
  • Нежелательно использовать «слой на слое» для создание различных эффектов (например градиента)
  • Нежелательно использовать градиентные границы (border, stroke)
  • Нежелательно сводить простые стили слоёв (например отбрасывание тени легко реализуется средствами CSS)

Текст

  • Для текстовых элементов обязательно указывать шрифт и его размер
  • Размеры (кегль) шрифтов должны быть целыми числами. Использование 14.22 в качестве размера шрифта недопустимо
  • Запрещено изменять высоту/ширину шрифта способом, отличным от обычного изменения размера кегля
  • Обязательно прикладывать к макету шрифты, которые были использованы в макете, в формате TTF, OTF
  • Шрифт должен быть бесплатным или должна быть приобретена лицензия на его использование
  • Недопустимо использовать прозрачность для элементов содержащих текст
  • Недопустимо использовать градиент для текста
  • Для каждого отдельного блока текста - отдельный text box
  • Отличный от дефолта (100%) межбуквенный/межсловный интервал должен быть отмечен комментарием
  • Из текстовых эффектов допустима лишь тень, и всё что с её возможностью можно эмулировать
  • Желательно не использовать в макете больше 2-х не стандартных шрифтов, их количество сильно влияет на рендеринг страницы в браузере

Рыба

  • Для повторяющихся элементов (списки и т.д.), рыба должна быть разной

Интерактивные элементы

Кнопки, текстовые поля, ссылки и прочие интерактивные элементы должны иметь несколько представленных в макете состояний:

Ссылка

  • Обычная (не посещённая, не активная)
  • При наведении (hover)
  • При нажатии (click)
  • Посещённая (visited)

Кнопка

  • Обычная
  • При наведении (hover)
  • При нажатии (click)
  • Заблокированная (disabled)

Поле ввода (input, select)

  • Обычное
  • Активное (active)
  • Заблокированное (disabled)

Checkbox, radio button

  • Обычный
  • Активный (active)
  • При наведении (hover)
  • Заблокированный (disabled)

Возвращаем блок "Предложите покупателю" в детальный заказ 1С-Битрикс

, Михаил
Добавляем в /bitrix/php_interface/admin_header.php

<?php 
<? if (stripos($_SERVER['REQUEST_URI'], '/bitrix/admin/sale_order_view.php') !== false) : ?>
    <script type="text/javascript">
        $(function () {
	            $.ajax({  method: "GET", url: "/bitrix/diva/ajaxGetBasket.php", data: {ID: <?= intval($_REQUEST['ID']) ?>}
		}).done(function (msg) {
		                $('.adm-s-result-container').prepend(msg);
		});
	});
    </script>
<?endif;
?>
?>

Создаём обработчик /bitrix/diva/ajaxGetBasket.php

<?php 

define("NO_KEEP_STATISTIC"true);
//define("NOT_CHECK_PERMISSIONS", true);
require($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/include/prolog_before.php");
$ID intval($_REQUEST['ID']);
if (!$USER->IsAdmin() || $ID == 0) {
	    die();
	}
require($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/sale/general/admin_tool.php");
require($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/sale/lang/ru/admin/order_detail.php");
CModule::IncludeModule("iblock");
CModule::IncludeModule("catalog");
CModule::IncludeModule("sale");
$dbOrder CSaleOrder::GetList(
                array("ID" => "DESC"), array("ID" => $ID), falsefalse, array(
            "ID""LID""PERSON_TYPE_ID",
            "PAYED""DATE_PAYED""EMP_PAYED_ID""PAY_VOUCHER_NUM""PAY_VOUCHER_DATE",
            "CANCELED""DATE_CANCELED""EMP_CANCELED_ID""REASON_CANCELED",
            "STATUS_ID""DATE_STATUS""EMP_STATUS_ID""PRICE_DELIVERY",
            "ALLOW_DELIVERY""DATE_ALLOW_DELIVERY""EMP_ALLOW_DELIVERY_ID",
            "DEDUCTED""DATE_DEDUCTED""EMP_DEDUCTED_ID""REASON_UNDO_DEDUCTED",
            "MARKED""DATE_MARKED""EMP_MARKED_ID""REASON_MARKED",
            "PRICE""CURRENCY""DISCOUNT_VALUE""SUM_PAID""USER_ID""PAY_SYSTEM_ID",
            "DELIVERY_ID""DATE_INSERT""DATE_INSERT_FORMAT""DATE_UPDATE""USER_DESCRIPTION",
            "ADDITIONAL_INFO""PS_STATUS""PS_STATUS_CODE""PS_STATUS_DESCRIPTION",
            "PS_STATUS_MESSAGE""PS_SUM""PS_CURRENCY""PS_RESPONSE_DATE""COMMENTS",
            "TAX_VALUE""STAT_GID""RECURRING_ID""AFFILIATE_ID""LOCK_STATUS",
            "USER_LOGIN""USER_NAME""USER_LAST_NAME""USER_EMAIL""DELIVERY_DOC_NUM",
            "DELIVERY_DOC_DATE""STORE_ID""ACCOUNT_NUMBER""TRACKING_NUMBER",
                )
);
if (($arOrder $dbOrder->Fetch())) {
	    ?>
	    <div class="load_product order_summary" style="float: left;
	">
	        <table width="100%" class="itog_header"><tr><td>Предложите покупателю</td></tr></table>
	        <div id="tabs">
	            <?
	            $crmMode false;
	            $displayNone "block";
	            $displayNoneBasket "block";
	            $displayNoneViewed "block";
	            $arFilterRecomendet = array();
	            $arBasketItems = array();
	            $dbBasketTmp CSaleBasket::GetList(array("ID" => "ASC"), array("ORDER_ID" => $arOrder["ID"]), falsefalse, array("ID""PRODUCT_ID"));
	            while ($arBasketTmp $dbBasketTmp->GetNext()) {
		                $arBasketItems[] = $arBasketTmp;
		            }
	            //pr($arBasketItems);
	            foreach ($arBasketItems as $arItem) {
		                if (!CSaleBasketHelper::isSetItem($arItem)) {
			                    $arFilterRecomendet[] = $arItem["PRODUCT_ID"];
			                }
		            }
	            $arRecommendedResult CSaleProduct::GetRecommendetProduct($arOrder["USER_ID"], $arOrder["LID"], $arFilterRecomendet);
	            $recomCnt count($arRecommendedResult);
	            if ($recomCnt 2) {
		                $arTmp = array();
		                $arTmp[] = $arRecommendedResult[0];
		                $arTmp[] = $arRecommendedResult[1];
		                $arRecommendedResult $arTmp;
		            }
	            if ($recomCnt <= 0)
	                $displayNone "none";
	            $arErrors = array();
	            $arFuserItems CSaleUser::GetList(array("USER_ID" => intval($arOrder["USER_ID"])));
	            $arCartWithoutSetItems = array();
	            $arTmpShoppingCart CSaleBasket::DoGetUserShoppingCart($arOrder["LID"], $arOrder["USER_ID"], $arFuserItems["ID"], $arErrors, array());
	            if (is_array($arTmpShoppingCart)) {
		                foreach ($arTmpShoppingCart as $arCartItem) {
			                    if (CSaleBasketHelper::isSetItem($arCartItem))
			                        continue;
			                    $item findPositionsByID($arCartItem["PRODUCT_ID"]);
			                    if ($item['IBLOCK_ID'] != CATALOG_IBLOCK_ID) {
				                        if ($item['PROPS']['CML2_LINK']['VALUE'] != "") {
					                            $item findPositionsByID($item['PROPS']['CML2_LINK']['VALUE']);
					                        }
				                    }
			                    $arCartItem['MASTER'] = $item;
			                    $arCartItem['NAME'] = "[" $item['PROPS']['CML2_ARTICLE']['VALUE'] . "] " $arCartItem['NAME'];
			                    $arCartWithoutSetItems[] = $arCartItem;
			                }
		            }
	            $basketCnt count($arCartWithoutSetItems);
	            if ($basketCnt 2) {
		                $arTmp = array();
		                $arTmp[] = $arCartWithoutSetItems[0];
		                $arTmp[] = $arCartWithoutSetItems[1];
		                $arCartWithoutSetItems $arTmp;
		            }
	            if ($basketCnt <= 0)
	                $displayNoneBasket "none";
	            ///
	            $arViewed = array();
	            $arViewedIds = array();
	            $viewedCount 0;
	            $mapViewed = array();
	            if (CModule::includeModule("catalog")) {
		                $viewedIterator = \Bitrix\Catalog\CatalogViewedProductTable::getList(array(
		                            'order' => array("DATE_VISIT" => "DESC"),
		                            'filter' => array('FUSER_ID' => $arFuserItems["ID"], "SITE_ID" => $arOrder["LID"]),
		                            'select' => array("ID""FUSER_ID""DATE_VISIT""PRODUCT_ID""LID" => "SITE_ID""NAME" => "ELEMENT.NAME""PREVIEW_PICTURE" => "ELEMENT.PREVIEW_PICTURE""DETAIL_PICTURE" => "ELEMENT.DETAIL_PICTURE")
		                ));
		                while ($viewed $viewedIterator->fetch()) {
			                    $viewed['MODULE'] = 'catalog';
			                    $arViewed[$viewedCount] = $viewed;
			                    $arViewedIds[] = $viewed['PRODUCT_ID'];
			                    $mapViewed[$viewed['PRODUCT_ID']] = $viewedCount;
			                    $viewedCount++;
			                }
		                unset($viewedCount);
		                $baseGroup CCatalogGroup::getBaseGroup();
		                if (!empty($arViewedIds)) {
			                    $priceIterator CPrice::getList(
			                                    array(), array("PRODUCT_ID" => $arViewedIds'CATALOG_GROUP_ID' => $baseGroup['ID']), falsefalse, array("PRODUCT_ID""PRICE""CURRENCY"));
			                    while ($productPrice $priceIterator->fetch()) {
				                        if (isset($mapViewed[$productPrice['PRODUCT_ID']])) {
					                            $key $mapViewed[$productPrice['PRODUCT_ID']];
					                            $arViewed[$key]["PRICE"] = $productPrice["PRICE"];
					                            $arViewed[$key]["CURRENCY"] = $productPrice["CURRENCY"];
					                        }
				                    }
			                }
		                $viewedCnt count($arViewed);
		                $arViewed array_slice($arViewed02);
		                if (count($arViewed) <= 0)
		                    $displayNoneViewed "none";
		            }
	            else {
		                $displayNoneViewed "none";
		            }
	            $tabBasket "tabs";
	            $tabViewed "tabs";
	            if ($displayNoneBasket == 'none' && $displayNone == 'none' && $displayNoneViewed == 'block')
	                $tabViewed .= " active";
	            if ($displayNoneBasket == 'block' && $displayNone == 'none')
	                $tabBasket .= " active";
	            ?>
	            <div id="tab_1" style="display:<?= $displayNone ?>"       class="tabs active"     onClick="fTabsSelect('buyer_recmon', this);
	" ><?= GetMessage('SOD_SUBTAB_RECOMENET'?></div>
	            <div id="tab_2" style="display:<?= $displayNoneBasket ?>" class="<?= $tabBasket ?>" onClick="fTabsSelect('buyer_basket', this);
	"><?= GetMessage('SOD_SUBTAB_BASKET'?></div>
	            <div id="tab_3" style="display:<?= $displayNoneViewed ?>" class="<?= $tabViewed ?>" onClick="fTabsSelect('buyer_viewed', this);
	"><?= GetMessage('SOD_SUBTAB_LOOKED'?></div>
	            <?
	            if ($displayNone == 'block') {
		                $displayNoneBasket 'none';
		                $displayNoneViewed 'none';
		            }
	            if ($displayNoneBasket == 'block') {
		                $displayNone 'none';
		                $displayNoneViewed 'none';
		            }
	            if ($displayNoneViewed == 'block') {
		                $displayNone 'none';
		                $displayNoneBasket 'none';
		            }
	            ?>
	            <div id="buyer_recmon" class="tabstext active" style="display:<?= $displayNone ?>">
	                <? echo fGetFormatedProductData($arOrder["USER_ID"], $arOrder["LID"], $arRecommendedResult$recomCnt$arOrder["CURRENCY"], 'recom'$crmMode);
	 ?>
	            </div>
	            <div id="buyer_basket" class="tabstext active" style="display:<?= $displayNoneBasket ?>">
	                if (count($arCartWithoutSetItems) > 0)
	                echo fGetFormatedProductData($arOrder["USER_ID"], $arOrder["LID"], $arCartWithoutSetItems, $basketCnt, $arOrder["CURRENCY"], 'basket', $crmMode);
	                ?>
	            </div>
	            <div id="buyer_viewed" class="tabstext active" style="display:<?= $displayNoneViewed ?>">
	                <?
	                if (count($arViewed) > 0)
	                    echo fGetFormatedProductData($arOrder["USER_ID"], $arOrder["LID"], $arViewed$viewedCnt$arOrder["CURRENCY"], 'viewed'$crmMode);
	                ?>
	            </div>
	        </div>
	        <script type="text/javascript">
	            function fTabsSelect(tabText, el)
	            {
		                BX('tab_1').className = "tabs";
		                BX('tab_2').className = "tabs";
		                BX('tab_3').className = "tabs";
		                BX(el).className = "tabs active";
		                BX(el).className = "tabs active";
		                BX(el).style.display = 'block';
		                BX('buyer_recmon').className = "tabstext";
		                BX('buyer_basket').className = "tabstext";
		                BX('buyer_viewed').className = "tabstext";
		                BX('buyer_recmon').style.display = 'none';
		                BX('buyer_basket').style.display = 'none';
		                BX('buyer_viewed').style.display = 'none';
		                BX(tabText).style.display = 'block';
		                BX(tabText).className = "tabstext active";
		            }
	        </script>
	        <script type="text/javascript">
	            /*
	             * click on recommendet More
	             */
	            function fGetMoreProduct(type)
	            {
		                BX.showWait();
		                productData = <? echo CUtil::PhpToJSObject($arFilterRecomendet);
		 ?>;
		                var userId = '<?= $arOrder["USER_ID"?>';
		                var fUserId = '<?= $arFuserItems["ID"?>';
		                var currency = '<?= $arOrder["CURRENCY"?>';
		                var lid = '<?= $arOrder["LID"?>';
		                BX.ajax.post('/bitrix/admin/sale_order_detail.php', '<?= CUtil::JSEscape(bitrix_sessid_get()) ?>&ORDER_AJAX=Y&type=' + type + '&arProduct=' + productData + '&currency=' + currency + '&LID=' + lid + '&userId=' + userId + '&fUserId=' + fUserId + '&ID=<?= $ID ?>', fGetMoreProductResult);
		            }
	            function fGetMoreProductResult(res)
	            {
		                BX.closeWait();
		                var rs = eval('(' + res + ')');
		                if (rs["ITEMS"].length > 0)
		                {
			                    if (rs["TYPE"] == 'basket')
			                        BX("buyer_basket").innerHTML = rs["ITEMS"];
			                    if (rs["TYPE"] == 'recom')
			                        BX("buyer_recmon").innerHTML = rs["ITEMS"];
			                    if (rs["TYPE"] == 'viewed')
			                        BX("buyer_viewed").innerHTML = rs["ITEMS"];
			                }
		            }
	        </script>    
	    </div>
	    <?
	}
require($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/include/epilog_after.php");

?>

SECTION_CODE_PATH не учитывает отсутствие символьного кода родительского раздела (1С-Битрикс)

, Михаил

<?php 
У catalog.section->result_modifier.php
foreach ($arResult["ITEMS"] as &$arItem) {
	   // FIX: SECTION_CODE_PATH
	   $nav CIBlockSection::GetNavChain($arItem['IBLOCK_ID'], $arItem['~IBLOCK_SECTION_ID']);
	   $arNavi=array();
	   while($arNavItem $nav->Fetch()) {
		        $arNavi[]=$arNavItem['CODE'];
		}
	   $arNavi[]=$arItem['CODE'];
	   $arItem['DETAIL_PAGE_URL']="/".implode("/"$arNavi)."/";
	}

?>

Подключаем почтовый сервис для рассылок электронных писем mailgun.com для 1С-Битрикс

, Михаил

Скачиваем библиотеку https://github.com/mailgun/mailgun-php


<?php 
require_once $_SERVER['DOCUMENT_ROOT'].'/bitrix/divasoft/mailgun-php/vendor/autoload.php';
use Mailgun\Mailgun;
function custom_mail($to$subject$message$additionalHeaders ''){
	        $mg = new Mailgun("key-*****");
	        $domain "divasoft.ru";
	/*$mg = new Mailgun('key-*****', null, 'bin.mailgun.net');
	 // TEST
	$mg->setApiVersion('*******');
	*/
	      //Получаем тему письма
	    $elements imap_mime_header_decode($subject);
	    $title =  '';
	    for ($i=0;
	 $i<count($elements);
	 $i++) {
		        $title .= $elements[$i]->text;
		    }
	        preg_match('/From: (.+)\n/i'$additionalHeaders$matches);
	        list(, $from) = $matches;
	        preg_match('/BCC: (.+)\n/i'$additionalHeaders$matches);
	        list(, $bcc) = $matches;
	        preg_match('/CC: (.+)\n/i'$additionalHeaders$matches);
	        list(, $cc) = $matches;
	        preg_match('/Reply-To: (.+)\n/i'$additionalHeaders$matches);
	        list(, $rt) = $matches;
	        $mg->sendMessage($domain, array('from'    => $from, 
	                                        'to'      => $to, 
	                                        'bcc'      => $bcc, 
	                                        'subject' => $subject, 
	                                        'html' => $message, 
	                                        'text'    => strip_tags($message)));
	        // DEBUG
	        /*$result = $mg->get("$domain/log", array('limit' => 25, 
	                                                'skip'  => 0));
	        $logItems = $result->http_response_body->items;
	        foreach($logItems as $logItem){
		            //echo $logItem->message_id . "\n";
		}*/
	        return true;
	}
?>

И волшебный ответ для того, что бы аккаунт прошёл валидацию
"your account is temporarily disabled. business verification please contact support to resolve"

Hello!

What types of emails will you be sending - transactional or marketing?
transactional + marketing
Register info, Order info, Promo mail.

Where do you source your database of email addresses?
CMS 1C-Bitrix (divasoft.ru)

Are all of your email addresses double-opt in?
I do not quite understand, but the mail has.

What is your expected monthly volume of messages?
100-1000, we just start online-store

Have you read our Email Best Practices document? 
Yes!

Can you please give us the URL that your users use to sign up for your email as well as a link to your Terms of Service?
In development, link to register page = https://divasoft.ru/login/?register=yes&backurl=%2F

p.s. этот сервис скрывает ip адрес сервера отправителя

Тонкий пробел в цене для 1С-Битрикс

, Михаил

Если у вас php 5.3, то понадобится обработчик


<?php 
/bitrix/php_interface/init.php
// Установка тонкого пробела в цене для php 5.3
AddEventHandler("currency""CurrencyFormat""thinNbspFormat");
function thinNbspFormat($fSum$strCurrency) {
	    $separator '&#8201;
	';
	    $summ preg_replace('/(?<=\d)\x' bin2hex($separator[0]) . '(?=\d)/'$separatornumber_format($fSum2'.'$separator)).$separator.'руб.';
	    $summ str_replace(".00"""$summ);
	    return $summ
	}
?>

Дальше в Админ-панели, в настройках Валюты->Языковые настройки (/bitrix/admin/currency_edit.php?lang=ru&ID=RUB)

Строка формата для вывода валюты: #&thinsp;руб.

Разделитель тысяч при выводе: Другое значение &thinsp;

Дальше меняем длину одного столбца в SQL таблице: ALTER TABLE `b_catalog_currency_lang` CHANGE `THOUSANDS_SEP` `THOUSANDS_SEP` varchar(10) COLLATE 'utf8_unicode_ci' NULL DEFAULT ' ' AFTER `DEC_POINT`;

И устанавливаем нужное значение разделителя вручную (т.к. в админке ограничение на 5 символов при вводе): UPDATE `b_catalog_currency_lang` SET `THOUSANDS_SEP` = ' ' WHERE `CURRENCY` = 'RUB' AND `LID` = 'ru';

Включаем PDO в BitrixEnv5 (Витруальная машина 1С-Битрикс)

, Михаил

Подключаем модуль PDO

mv /etc/php.d/pdo.ini.disabled /etc/php.d/pdo.ini
mv /etc/php.d/pdo_sqlite.ini.disabled /etc/php.d/pdo_sqlite.ini
mv /etc/php.d/pdo_mysql.ini.disabled /etc/pdo_mysql.d/pdo.ini
mv /etc/php.d/pdo_dblib.ini.disabled /etc/php.d/pdo_dblib.ini

Если получаем ошибку

Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock'

То смотрим в файле /etc/my.cnf где находится сокет

socket = /var/lib/mysqld/mysqld.sock

Добавляем в /etc/php.ini этот путь

pdo_mysql.default_socket=/var/lib/mysqld/mysqld.sock

Разные цены для разных сайтов 1С-Битрикс

, Михаил

В /bitrix/php_interface/init.php


<?php 
global $TYPE_PRICE;
$TYPE_PRICE 3;
 //ID цены на 1м сайте
?>

В /bitrix/php_interface/s2/init.php добавляем обработчик:


<?php 
AddEventHandler("catalog""OnGetOptimalPrice"'OnGetOptimalPriceHandler');
global $TYPE_PRICE;
$TYPE_PRICE 4;
  //ID цены на 2м сайте
function OnGetOptimalPriceHandler($productID$quantity 1$arUserGroups = array(), $renewal "N"$arPrices = array(), $siteID "s2"$arDiscountCoupons false) {
	    CModule::IncludeModule("iblock");
	    Cmodule::IncludeModule('catalog');
	    global $TYPE_PRICE;
	    $db_res CPrice::GetList(array(), array("PRODUCT_ID" => $productID"CATALOG_GROUP_ID" => $TYPE_PRICE));
	    if ($ar_res $db_res->Fetch()) {
		        $price $ar_res['PRICE'];
		        $currency $ar_res['CURRENCY'];
		        $arResult = array(
		            'PRICE' => array(
		                'PRICE' => $price,
		                'CURRENCY' => $currency,
		            )
		        );
		        $arDiscounts CCatalogDiscount::GetDiscount($productID15);
		 // ID Инфоблока с торговыми предложениями (в данном случае)
		        if ($arDiscounts) {
			            foreach ($arDiscounts as $arDiscount) {
				                $arResult['DISCOUNT_LIST'][] = array(
				                    'VALUE_TYPE' => $arDiscount['VALUE_TYPE'],
				                    'VALUE' => $arDiscount['VALUE'],
				                    'CURRENCY' => $arDiscount['CURRENCY']
				                );
				}
			}
		} else {
		        return true;
		}
	    return $arResult;
	}
?>

Ставим статус "Отменен" в заказе 1С-Битрикс

, Михаил

Ставим статус "Отменен" в заказе 1С-Битрикс

Для этого правим:


<?php 
/bitrix/modules/sale/general/order_loader.php
function collectOrderInfo($value) {
	***
	$arOrder["TRAITS"] = array();
	$arOrder["TRAITS"][GetMessage("CC_BSC1_CANCELED")] = $value["#"][GetMessage("CC_BSC1_CANCELED")][0]["#"];
	}

?>

Добавляем SITE_ID в экспорт/импорт заказов 1С-Битрикс

, Михаил

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

Для этого правим:

Добавляем SITE_ID в выгрузку заказа


<?php 

/bitrix/modules/sale/general/export.php
<<?=GetMessage("SALE_EXPORT_DOCUMENT")?>>
<SITE_ID><?=$arOrder["LID"]?></SITE_ID>

?>

Добавляем SITE_ID в импорт заказа, сохраняя основную функциональность


<?php 
/bitrix/modules/sale/general/order_loader.php
function collectOrderInfo($value) {
	***
	$arOrder["ID"] = $value["#"][GetMessage("CC_BSC1_NUMBER")][0]["#"];
	$arOrder["SITE_ID"] = ($value["#"]["SITE_ID"][0]["#"])?$value["#"]["SITE_ID"][0]["#"]:$this->arParams["SITE_NEW_ORDERS"];
	}

?>


<?php 
/bitrix/modules/sale/general/order_loader.php
if ($orderInfo['SITE_ID']!=$arOrder['SITE_ID']) {
	    $arAditFields["SITE_ID"]=$arOrder['SITE_ID'];
	    $arAditFields["UPDATED_1C"] = "Y";
	}
****
if(count($arAditFields)>0)
    CSaleOrder::Update($orderInfo["ID"], $arAditFields);

?>


<?php 
/bitrix/modules/sale/general/order_loader.php
if(IntVal($arOrder["USER_ID"]) > 0) {
	$orderFields = array( "SITE_ID" => $arOrder["SITE_ID"],
	***
	)
	}

?>

Завтра будет Минусинск

, Ashe Gentle

Ну что, завтра день Х? Запуск нового алгоритма «Минусинск» от Яндекса, о котором только и говорят последние несколько недель. Считается, что после него всё изменится, причём в худшую сторону для нас, компаний, продвигающих сайты, и SEO умрёт. Впрочем, смерть ему предрекают при каждом запуске нового алгоритма.

А между тем, ни один из наших сайтов предупреждения от Яндекса не получил, так что мы просто посмотрим =)

Есть повод... похвастаться

, Ashe Gentle

А мы! А нас! Нас показывали по телевизору!

В прошедшую пятницу генеральный директор ООО «Дивасофт» принял участие в программе Павла Прохоренкова «1 студия». На передаче обсуждалась важная и сложная тема «Легко ли быть предпринимателем?»

Передача 1 студия Директор Дивасофт Передача 1 студия

Взяв слово, Иван Олегович выразил интересную идею о возможности создания готового инструмента продаж для практически любого бизнеса:

Было бы здорово, если бы человек, который к вам пришел, сказал, например: „Я хочу продавать вашу мебель в другом регионе“. И получил полностью каталог ваших товаров, вашу базу, цены и площадку для торговли этими товарами.

Идея так называемой «франшизы» не нова, но ещё ни разу не реализовывалась с таким размахом.

Полностью посмотреть передачу можно на youtube. А мы пока горды своим директором.

Если у вас DDoS или почему не стоит пользоваться услугами Rusonyx

, Ashe Gentle

Солнечное утро пятницы не предвещало ничего плохого, когда раздался звонок от клиента: «Долго открывается сайт». «Слишком много пользователей», — первым делом решили мы, потому что такое уже случалось. Да и хостер не отреагировал на резкое увеличение трафика, так что причин для беспокойства пока ещё не было. Полезли в админку, чтобы в очередной раз прибавить мощностей, но от представших нашему взору графиков потеряли дар речи.

Графики нагрузки

Ответ мог быть только один — DDoS. И тут неожиданно очнулся Rusonyx и отключил к чёрту наш сайт. Не, ну а что? Конечно, мы написали в техподдержку и спустя полтора часа получили пространный ответ:

На Ваш сервер зафиксирован сильный поток входящего трафика, IP адрес заблокирован. Вам нужно: 1. Обратиться к компании, предоставляющей услуги фильтрации траффика (Qurator например). 2. Купить новый IP адрес. 3. Сообщить нам информацию, которую Вам предоставили (Qurator например), далее мы сделаем все самостоятельно.

Сказано, сделано. Покупаем IP-адрес всё у того же Rusonyx. Благоразумно отказываемся от предложенного Qurator, ибо есть сервисы куда дешевле и надёжнее, к ним и обращаемся. И всю полученную информацию скидываем обратно хостеру. Ждём. И, внезапно, обнаруживаем, что свежекупленный IP тоже попал под DDoS. «КАК?!» — спрашиваем у хостера, — «...» Покупаем ещё один IP и снова ждём. Ждём. Ждём. Тут стоит упомянуть, что у русоникса две линии техподдержки: круглосуточная для простых обращений и инженерная (не работающая по выходным, да) для вот таких проблем. А меж тем пятница стремительно проходит.

Изрядно переживаем, потому что клиент теряет прибыль, а мы — нервные клетки. Мандража добавляет отсутствие доступа к панели управления и невозможность снять свежие бекапы сайта. Снова и снова бомбардируем техподдержку хостера, как наступает он — вечер пятницы, злоклятое время для всех админов. На этом, видимо, рабочий день инженеров русоникса заканчивается и они со спокойной совестью уходят домой. Напоминаю, вся информация для защиты у них есть уже несколько часов, а сайт всё ещё DDoS'ят. С сожалением понимаем, что не можем ничего сделать до понедельника. В нервном ожидании проводим и выходные и, наконец, получаем в воскресенье наши бэкапы. И первое, что делаем — уходим к другому хостеру.

Итого

  1. Мы скрыли сайт системой фильтрации трафика на уровне DNS.
  2. Отказались от услуг недобросовестного, а точнее бессовестного русоникса.
  3. Защитили исходящие сообщения от почтового сервиса сайта через сторонний SMTP-сервис.
  4. Вышли из-под DDoS.
  5. Дополнительно узнали, что у клиента есть серьёзно настроенные конкуренты.
  6. ...
  7. Profit!

Ах да, чуть не забыла самое интересное, вместе с недоступностью сайта клиента просто катастрофически выросли посещения на наш сайт ;)

Посещаемость сайта divasoft.ru

P.S.: Обратили внимание, что в посте не названы сервисы и ресурсы, которые мы использовали? Конечно это сделано нарочно, для обеспечения безопасности. Впрочем, все они легко гуглятся, если вас интересует эта тема.

Лень — двигатель прогресса

, Ashe Gentle

Знаете, что случается, когда человеку становится лень? Правильно. Он придумывает способ (иногда новаторский и гениальный) облегчить свою работу. Поэтому у нас сразу две хорошие новости.

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

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

Ах, да. Ещё у нас обновлены иконки соцсетей, как там говорят в таких случаях? Подписывайтесь, ставьте лайки, ждите свежих новостей =)

«Сказка»

, Ashe Gentle

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

— В объявлении написано, что у вас можно взять квест,— сказал Полуэльф бургомистру.— Но там не объясняется, в чём суть. Вы не могли бы уточнить?
— Да всё просто,— пожал плечами бургомистр.— Видите вон тот холм? На нём засел гоблин с гранатомётом. И периодически обстреливает город. Вот, собственно, и вся проблема.
— Ага, понятно. Надо убить гоблина...
— Что вы, что вы!?— бургомистр вытаращил глаза и замахал руками.— Его ни в коем случае нельзя убивать!
— Почему?— удивился Гном.— Это же гоблин!
— Вот именно! Если мы его убьём, мировая общественность скажет, что это геноцид, а мы расисты.
— Ну и что? Пусть говорит что хочет.
— И введёт войска,— мрачно закончил свою мысль бургомистр.
— Хм...— задумался Полуэльф.— То есть, этот засранец стреляет по вам из гранатомёта, а вы терпите и не смеете дать сдачи?
— Не смеем,— развёл руками бургомистр.— Иначе нас назовут агрессорами.
— Ну хорошо, а если, допустим, не убивать гоблина, а прогнать его куда-нибудь подальше?
— С его холма? Невозможно. Тогда нас назовут оккупантами.
— Поймать и отобрать гранатомёт?
— Экспроприаторами.
— Посадить под замок вместе с гранатомётом?.. Ладно-ладно, не отвечайте,— быстро проговорил Полуэльф, когда бургомистр открыл было рот.— Я всё понял. Действительно, интересный случай.
— Ну так чего же вы от нас хотите?— не выдержала Принцесса.— Убивать нельзя, разоружать нельзя, ловить и прогонять тоже нельзя, а что тогда остаётся? Перевоспитывать? Это не наш профиль.
— Нет, что вы... Для такой работы мы бы позвали психолога. Но, кстати, тогда мировая общественность обвинила бы нас в оказании психологического давления.
— И в осквернении самобытных традиций,— добавил Гном, солидно качнув головой.— Пострелять из гранатомёта по людишкам — это же для гоблинов святое!
— Вот-вот,— радостно воскликнул бургомистр,— вы меня понимаете.
— Ну а от нас-то что требуется?— снова встряла Принцесса.
— Отнести посылку,— вздохнул бургомистр.
— Кому? Гоблину?
— Ну да. Ведь там, на холме, нет никаких запасов еды. Через час гоблин проголодается, объявит перемирие и начнёт переговоры. Он так каждый день делает. Требует, чтобы ему приносили еду, вино, оружие, иногда ещё чего-нибудь... А потом, когда наестся, заявляет, что мирные переговоры зашли в тупик и он вынужден возобновить огонь. Мировая общественность ему очень сочувствует. Считает, что он принципиальный.
— А если вы откажетесь предоставлять ему еду и оружие...
— Тогда про нас скажут, что...
— Ладно-ладно, мы поняли,— замахал руками Полуэльф.
— ...и введут войска,— пробубнил бургомистр.
— Ну хорошо, а мы-то вам зачем? Послали бы кого-нибудь из своих отнести мешок.
— Посылали уже. Никто не вернулся.
— Что, гоблин их всех убил?
— Он утверждает, что нет.
— А...
— А мировая общественность ему верит.
— А...
— А тогда скажут, что мы провокаторы. Понимаете, это ведь он, гоблин, проявляет мирную инициативу, это его жест доброй воли. И если что-то пошло не так, то только по нашей вине. Очевидно же! А вы... ну вроде как посторонние, вас он, может, и не тронет.
— Ну хорошо,— подытожил Полуэльф.— Если отбросить всякую политическую шелуху, то от нас требуется взять посылку у заказчика и отнести её клиенту, верно? Обычный почтовый квест. А всё остальное — только ваши проблемы. Так?
— Всё верно,— подтвердил бургомистр,— значит, договорились?
— По рукам,— кивнул Полуэльф. Бургомистр облегчённо вздохнул.
— Можно вопрос?— подняла руку Принцесса.— Вот вы так боитесь, что мировая общественность назовёт вас агрессорами, или милитаристами, или ещё чем похуже — а как она вас называет сейчас?
— Идиотами,— печально ответил бургомистр.

«Спутник». Обзор и личное мнение

, Ashe Gentle

Невероятно, но факт. Сегодня запущена российская поисковая система «Спутник», разработанная Ростелекомом под эгидой государства. Её даже можно посмотреть и попробовать на sputnik.ru.

Главная страница sputnik.ru

Пользователям рунета предлагается поиск идеологически правильных материалов. Особенно мне понравилась фраза «Спутник делает проще доступ к официальной информации». Неприкрытая правда, как ни странно. Шутить на эту тему можно долго. Однако мои коллеги предпочли воздержаться от комментариев. Все мало-мальски разумные пользователи понимают, к чему ведёт ввод государственной поисковой системы. Особенно на фоне обострившихся законов против интернета.

Мы же в этой статье попытаемся найти плюсы

Было бы здорово, если бы «Спутник» позволил прекратить бесконечную бумажную волокиту по простейшим делам. Для этого, наверное, и предназначен сервис «Удобная Россия». Но увы, он не оправдывает ожиданий и представляет собой обычный поиск по заранее выбранной теме. Система выводит на первом месте наиболее релевантный материал (по её мнению) и предлагает другие варианты. На этом всё. Где обещанное «удобно»?

Далее сервис «Мой дом». Вот это уже поинтереснее. Он предлагает найти информацию обо всех организациях, обслуживающих конкретное здание, а также о находящихся поблизости. Плюс, опять таки, релевантные результаты поиска. Увы, информация пока не полная, о чём предупреждает сама система. Надеемся на будущее развитие. Ах да, строка поиска никак не хочет запоминать, что вы уже искали.

Удобная Россия Мой дом

«Лекарства» обещают показать инструкцию по применению и найти ближайшие аптеки (кстати, с ценами). Хорошо, если бы «не». Анальгин за 50 рублей в единственном месте в городе? Да ладно!

«Финансы». Что это? Курс валют и конвертер. Зачем выносить в отдельный сервис то, что можно запихать в поиск? Как это сделали google и яндекс. «Спутник» так не умеет и по запросу «1 доллар в рублях» выдаёт результаты, а не вычисления. Сомнительно.

«Телепрограмма». О боже, неужели действительно достойный сервис?! Он и правда показывает программу по всем каналам! Причём с удобной фильтрацией и возможностью добавить в избранное! И даже с онлайн-трансляцией! Здорово. Простите мой сарказм, действительно хорошо и достойно внимания. Уже отчаялась найти полезное.

«Карта» — список учреждений. Каких-то. Одному «Спутнику» известных. Потому что просто воспользоваться картой и узнать информацию по любому зданию нельзя. И снова тот же вопрос: «зачем»?

Лекарства Финансы Телепрограмма Карта

На этом всё, что я имею сказать по функционалу. Теперь про внешний вид. Удивительно, но сайт выглядит достаточно приятно. Светлый, просторный, всё как мы любим. Но неудобный. Непонятно назначение серого пространства справа при выдаче результатов. Сейчас там показывается реклама сервисов. Не очевидная, потому что случайно нажимается и уходит на другую страницу. Зато красиво и хорошо сделана страница «About». По веяниям последних технологий параллакса. Содержание вызывает улыбку, советую прочитать.

Теперь совсем всё. Быть «Спутнику» или не быть? Ответ простой: через пару дней ажиотаж вокруг стихнет, и сервис умрёт. Пока существуют Яндекс и Гугл, конкуренцию им составить нереально. А ввиду того, что половина заявленного функционала не отвечает ожиданиям, и подавно. Ну или пока пресловутая «Чебурашка» не заработает.

Добро пожаловать на „Спутник“. Пользуйтесь интернетом безопасно и с пользой!

Открытие блога

, Ashe Gentle

Как и всякая хорошая вещь, блог «Дивасофт» начинался с идеи. Чужой. Где-то на просторах интернета мы подсмотрели дизайн с широкими картинками и узким текстом. Отталкиваясь от него, приступили к созданию своего варианта. Долго, очень долго мы вымеряли шаблоны. Придумывали, где разметить дату и подпись, использовать ли теги, как обойтись с картинками, что делать с разделением по страницам. И вот, спустя чуть ли не полгода, блог «Дивасофт» запущен и работает.

И он красивый. Да, вот так вот нескромно с нашей стороны это заявлять, но посудите сами. Чего только стоит исходный код страницы, залюбоваться можно. Разметка всех и вся сделана по стандартам HTML5. Вы только посмотрите на структуру каждого поста!

<article>
    <header>
        <h2><a href="">Открытие блога</a></h2>
        <span><time datetime="2014-05-16T12:00:00+04:00">16 Мая 2014</time>, <em>Ashe Gentle</em></span>
    </header>
    <p>Как и всякая хорошая вещь, блог «Дивасофт» начинался с идеи. Чужой. Где-то на просторах интернета мы подсмотрели дизайн с широкими картинками и узким текстом. Отталкиваясь от него, приступили к созданию своего варианта. Долго, очень долго мы вымеряли шаблоны. Придумывали, где разметить дату и подпись, использовать ли теги, как обойтись с картинками, что делать с разделением по страницам. И вот, спустя чуть ли не полгода, блог «Дивасофт» запущен и работает.</p>
    <h3>Подзаголовок</h3>
    <p>И он красивый. Да, вот так вот нескромно с нашей стороны это заявлять, но посудите сами. Чего только стоит исходный код страницы, залюбоваться можно. Разметка всех и вся сделана по стандартам HTML5. Вы только посмотрите на структуру каждого поста!</p>
    <footer>
        <em><a href="#">Бекэнд</a>, <a href="#">Bitrix</a></em>
    </footer>
</article>

Тут тебе и <article>, и <header>, и <footer>, и <time> и всё-всё-всё на свете. А как замечательно оформлены у нас цитаты:

<blockquote cite="http://divasoft.ru/blog/">
    <p>А как замечательно оформлены у нас цитаты</p>
</blockquote>

Отдельно стоит упомянуть типографику. Вы обратили внимание, что мы используем верные кавычки, принятые в русском языке — «ёлочки»? И, конечно, длинные тире вместо дефисов и минусов, которые, между прочим, не так просто поставить. Приходится использовать либо цифровые коды, либо специальные символы html.

А ещё мы точно продумали расположение изображений. Их может быть четыре рядом, три, два или одно на весь блок записи. И при всём этом отступы между ними будут равными. Пришлось повозиться с полпикселями, появляющимися в разных браузерах. Но и эту проблему мы решили, использовав float: right; для крайнего правого изображения.

Кстати, о полпикселях. Это происходит потому, что мы используем собственную процентную сетку. Которая тоже была разработана путём долгих и кровавых споров. У нас вообще никогда без них не обходится. Сейчас, например, воюем за или против переносов слов.

Всё вышесказанное относилось к вёрстке, но и с натягом вышло не так просто. Изначально планировалось использовать стандартный компонент битрикс — блог. Но когда уже всё было готово и мы приступили к тестированию, возникли непредвиденные проблемы. Оказалось, что начиная с 12 версии, разработчики битрикса запретили использование HTML-редактора в блогах. Наверное, это связано с безопасностью. И наверное, важно. Но нам абсолютно не подходит. Так реализация проекта застопорилась на неопределённое время, пока мы искали пути обхода. Как вариант рассматривали использование другой площадки — wordpress, например. Заточенный именно для блогов, он мог бы стать решением всех проблем. Но оказалось нецелесообразным разбивать единую систему. Поэтому мы сделали блог на инфоблоках.

И увидел Бог всё, что Он создал, и вот, хорошо весьма. И был вечер, и было утро...

Возможно, потом у нас будут комментарии и подписка, и конечно «новое видение» основного сайта. Пока это всё в проекте и в необозримом будущем. Но уже сейчас вы можете читать о жизни компании, её проблемах, решениях, разработках, проектах и о многом другом. Присоединяйтесь!

Как мы побывали под запретом Роскомнадзора

, Ashe Gentle

Однажды, в студёную зимнюю пору клиент сообщил, что не может зайти на наш сайт. «Как так?!» — переполошились мы? Вот же он, родной divasoft.ru, открывается спокойно. Что же такое? Может антивирус косячит? А клиент, к слову, из Москвы будет. Запросили мы у человека скрин, что же ему там браузер молвит. Получили, смотрим, переглядываемся, репу чешем, не понимаем.

Скриншот сайта

Забанил нас Роскомнадзор в самом деле. Да уж, а ведь знали, что не стоит такие картинки на сайт выкладывать, да и видео ещё добавлять. Вот и поплатились.

Но шутки шутками, а работать надо. Понятное дело, что на нашем сайте запрещённого контента нет, ищем дальше. Весь трафик проходит через cloudflare.com для защиты и кеширования сайта на уровне DNS. И вот там то, за общим адресом и прячется зловредный контент. Собственно, уже на следующий день проблема была решена и сайт заработал в обычном режиме. А мы получили бесценный опыт пребывания под баном.

Картинка должна быть больше

, Ashe Gentle

Вот иногда работаешь себе, работаешь. А потом вдруг возникает у тебя глупая и элементарная проблема, но никак её решить не удаётся. Например: выводить изображения подкатегории больше. Казалось бы, чего уж проще? Заходишь в категорию, выбираешь «Параметры изображения материала», ставишь их. А ничего не меняется. Потому что внимательнее надо быть: это именно изображения для материалов. А для подкатегорий размеры указываются в настройках K2 на вкладке «Изображения» в поле «Ширина изображения категории (в пикселах)». Однако и после этого ничего не меняется. Потому что K2 уже загрузил картинки, и теперь придётся их перезалить. Все, да. Перезалить. Вручную.

Стать партнёром битрикс

, Ashe Gentle

Некоторое время назад мы поставили себе цель стать сертифицированным партнёром битрикс. Это дало бы нам дополнительные плюшки и корпоративный портал, к тому же. Битрикс предлагает два способа: набрать 100 и более баллов по продажам их продуктов или пройти сертификацию по определённым курсам. Мы решили пойти по более простому (на наш взгляд) второму пути. И я приступила к изучению и прохождению кучи тестов. День за днём я читала, отвечала, снова читала и получала сертификаты. В результате собралась внушительная стопка.

Сертификаты Битрикс

И вот, когда были сданы все тесты, оставалось дело за малым: рассказать на нашем сайте о продуктах битрикс. Дав себе справедливый отдых, я отложила написание статьи на недельку. Этого времени хватило, чтобы продать нужное количество лицензий и заработать статус «Сертифицированный партнёр» по первому пути.

Если у вас нет К2

, Ashe Gentle

На некоторых наших сайтах возникла необычная проблема: после успешной установки K2, он не появляется в соответствующем меню. То есть фактически он на сайте есть, материалы и категории добавлять можно, да и в остальном работать как обычно. А в меню «Компоненты» K2 нет. Нет, и всё тут. Единственный способ до него достучаться — перейти на панель быстрого доступа Joomla.

Интересно, что проблема возникла рандомно: как на сайтах с разной Joomla и разным K2, так и на сайтах с абсолютно одинаковыми параметрами.

Что мы только не пробовали: переустанавливать и обновлять K2, обновлять Joomla, удалять компоненты и ставить их заново, гуглить. Ничего не помогало. Пока на неизвестном забугорном сайте не нашли совет: отключить все плагины и включить заново. И вуаля — заработало! А потом методом долго тыка мы выяснили, что виноват во всём конкретный плагин Xmap — K2 Plugin. Он выключен по умолчанию, но стоит его включить, как K2 появляется в меню.

Панель адмнистрирования Joomla Панель адмнистрирования Joomla

Yet another Conference 2013

, Ashe Gentle

2 октября прошла Yet another Conference 2013 и мы там были. Зарегистрировались на сайте Яндекса, получили подтверждение, купили билеты, забронировали хостел, собрались и отправились в путь.

YaC 2013 или как всё начиналось

Приехали мы в Москву днём раньше. Во-первых, потому, что поезд приходил в 8:30 и нам хотелось привести себя в порядок до конференции. Во-вторых, были еще планы на Москву. А в-третьих, так исторически сложилось. До регистрации в хостеле у нас оставалось несколько часов и это время мы провели со смыслом. Для начала сытно и вкусно отзавтракали у Лебедева, параллельно закупившись в его магазине. Потратили там пару купонов, заботливо вырезанных мной из Каталогуса. И даже оставили визитку в неприметном месте прямо напротив туалета. Ах да, ещё сыграли в древний советский морской бой. В общем, хорошо провели время.

Большое кафе студии Артемия Лебедева Визитка Дивасофт

А уже к 12 поспешили в хостел. Свой выбор мы остановили на «Фабрике», расположенной в районе завода «Красный октябрь». В 10—15 минутах ходьбы до метро, практически в центре города, да ещё и по сходной цене, это место казалось нам идеальным вариантом.

У Хостела Фабрика

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

Обустроившись и освободившись от тяжелых вещей, мы отправились на чайную церемонию в «Железный феникс». Найти его тоже оказалось делом не из простых. Мы вышли не на той станции метро и достаточно долго блуждали, ориентируясь по 2gis. В неизвестной подворотне за тяжелой железной дверью скрывалась маленькая уютная чайная. В тепле и покое которой мы и провели следующие несколько часов. А после московский друг отвёл нас в китайскую закусочную и угостил вкуснейшими пафами и лапшой.

Чайная Железный феникс Рисунок чайника

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

YaC 2013 или как это было

И вот наступил день Х. Заранее продумав маршрут и рассчитав время пути, мы были готовы ко всем неожиданностям. Кстати, в путешествии нам очень помогали Яндекс.Метро с его расчётом, в какой вагон стоит садиться, 2gis с подробными и точными картами, и, конечно Whatsapp в качестве бесплатного переговорного приложения. День Х обещал быть солнечным и мы смогли оценить всю красоту местоположения нашего хостела.

Храм Христа Спасителя

Не успели мы выйти из метро и оглядеться, как к нам обратилась женщина с бейджиком УaС: «Вы на конференцию? 15 минут прямо, никуда не сворачивая.» «Хорошо,» — сказали мы и отправились в путь. А вместе с нами шли десятки, если не сотни людей. Суровые программисты и хрупкие барышни — все жаждали попасть на конференцию Яндекса.

Через обещанные 15 минут пешего хода мы оказались у зданий конференции. Красочные надписи и наклейки на стеклах манили нас «Войти».

Вход в здание конференции Здание конференции

Внутри нас встретили металлодетекторы и охранники, мирно просившие показать содержимое сумок. Как потом оказалось, такие же детекторы были во всех зданиях YaC. И каждый раз на входе нужно было проверяться. Понятно, что сиё действо было направлено на безопасность, но порядком утомляло скакать из здания в здание, на ходу расстёгивая сумку. К счастью, к середине дня уже достаточно было только показать бейдж.

Таки попав внутрь, мы первым делом направились к регистрационной стойке, где нас попросили назвать фамилии и выдали бейджики участников конференции (не пригодились даже высланные яндексом qr-коды). И тут выяснилось интересное: мы, то бишь три сотрудника «Дивасофт», работаем в разных компаниях. Вот так, урок нам на будущее: надо согласовывать данные анкет.

Стойка регистрации YaC Бейджики участников YaC

До начала конференции ещё оставалось время, и решено было осмотреться и определиться с планами. Подключившись к местному вайфаю, мы договорились держать связь через WhatsApp и разбрелись, кто куда. Недалеко от стойки регистрации я обнаружила гигантскую букву «Я» и совершенно очаровательного робота Марвина. Это создание не только отвечало на вопросы всех желающих, но и с успехом заводило новые знакомства.

— Марвин, давай сфотографируемся?
— Давай, только обними меня покрепче.
— Хорошо.
— Хочешь, я буду смотреть на тебя влюблёнными глазами?
— Хочу!
— Смотрю.
— Так он у тебя один!
— Зато красивый.

— Марвин, а как ты работаешь?
— У меня внутри маленький китаец. Постучи, и он тебе откроет.

— Марвин, подарить тебе книгу?
— Давай.
— А куда положить?
— Положи здесь. Сейчас выйдет человек и заберёт... Нет, не китаец. И не маленький.

*подъезжает к лысому парню*
— Знаешь, почему мы тут с тобой самые крутые?
— Потому что у нас бейджики?
— Нет, потому что у нас причёски одинаковые.

А позже в соседнем зале мне удалось обнаружить подругу Марвина, которая легко обвела меня вокруг пальца, представившись R2D2. Маруся (а именно так её звали на самом деле) общалась на философские темы и поддерживала сложные разговоры.

— Маруся, в чём смысл жизни?
— Сорок два.
— Почему?
— Потому что гладиолус.
— А почему гладиолус?
— Потому что они длинные и пушистые.
— А почему ты считаешь, что они пушистые?
— Видела у бабушки на даче.
— У тебя есть бабушка?
— Нет, у инженера есть.
— А как её зовут?
— Арина Родионовна.
— ...
— Вот, и никто не верит.

Яндекс Яндекс-робот Марвин Информационные стойки

Тем временем до секретного доклада оставалось всё меньше времени и пора было занимать места. Сам докладчик выступал в первом зале и большая часть нашей команды поспешила туда. Я же, ориентируясь по заранее составленному плану, оказалась в третьем. Трансляция с места событий уже началась, потому, сидя в полупустом помещении, мы могли наблюдать набитый под завязку первый зал. И по ту сторону экрана организаторы просили: «Если рядом с вами есть пустое место, поднимите руку, чтобы люди могли пройти». И в нашей пустой секции API люди оглядывались, поднимали руки и дружно смеялись.

Кстати, всего залов было шесть. Три из них находились в одном здании, четвёртая секция фронтенда расположилась в здании напротив, а ещё две спрятались у чёрта на куличиках. По ходу конференции мне предстояло быстро-быстро перемещаться, чтобы везде успеть.

Схема залов Программа докладов

И вот, началось. В полумраке зала под приятную мелодию и презентацию сотрудники Яндекса поведали нам о платформе Атом. Под лозунгом «Интернет — для каждого свой» скрывалась технология, которая позволит адаптировать интернет под конкретные интересы пользователя.

Презентация Атома

А затем, так как все сотрудники «Дивасофт» интересуются разным, мы окончательно разбрелись. Кто-то определил для себя одну интересующую секцию, а кто-то хотел побывать везде и сразу. Я старалась избегать англоязычных докладов и выбирать что поинтереснее на русском. Это было не просто, чем-то приходилось жертвовать. Были как доклады, на которые я пошла зря, так и те, на которые, к несчастью, не успела. Секцию «Mobile Camp» мне стоило пропустить полностью, а вот в «API» задержаться подольше. Очень порадовали докладчики из Яндекса. Все их презентации были оформлены в едином стиле и при том играючи удерживали внимание слушателей.

Самым «ожидаемым» для нашей команды стал доклад «Вклад Adobe в Web», на нём мы и встретились вместе. Шутка дня: лучший вклад Adobe в Web — это закрыть Adobe Flash. Дмитрий Барановский начал свою презентацию с юмора: «Я сам живу в Сиднее, и я летел тридцать часов сюда, чтобы прочитать вам доклад тридцать минут. Вопрос: не дебил ли я?» Но затем он рассказал достаточно интересные вещи о будущих css-фишках и библиотеках. И там есть, чего ждать, честное слово. Говорил Дмитрий со смешным акцентом, за что и поплатился вопросом из твиттера: «Не из Одессы ли Вы?» — «А шо такое?! Нет, я не из Одессы.»

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

Я пыталась пропустить Responsive Web Design: Clever Tips and Techniques, но всё равно на него успела. Когда я забежала в зал, на сцене всё ещё был Vitaly Friedman. Он говорил много и говорил на английском. Но как оказалось говорил нужно и интересно, и я уже начала вникать в суть происходящего, как доклад внезапно закончился. Ведущий объявил, что на вопросы не осталось времени, но докладчик ещё тут, и любой желающий может лично задать ему вопрос. И тут Виталий Фридман на чистом русском произнёс: «Мы можем поговорить по-русски, если что». Зал вымученно выдохнул.

Зал фронтэнда

Закончить конференцию я хотела в секции, посвящённой непосредственно поиску. Едва зайдя в зал, я сразу поняла, что здесь прямо таки эпицентр жизни. Удобно расположившись на пуфиках, десятки людей спали. Их, конечно, можно простить, день близился к завершению, а пуфики были в основном в этой секции. Может быть и организаторы предусмотрели подобное?

Я же собиралась на «Анализ неявных предпочтений пользователей» (что кстати, было достаточно скучно, жаль не удалось вовремя занять пуфик), но пришла к концу «Computational framework for an integrated technology stack». Почему-то человек на сцене говорил совсем не на английском. И рассказывал он о wolframalpha.com. Это просто невероятная база данных, которая, основываясь на собственных знаниях, может не только найти ответ на вопрос, но и рассчитать уравнение любой сложности. Невероятно.

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

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

YaC 2013 или чем всё закончилось

После окончания Yet another Conference 2013 у нас оставался ещё целый день в Москве. И на это время было запланировано посещение «выставки еды» (ах, как звучит). На самом деле это действо именовалось «ПИР — индустрия гостеприимства» и было посвящено отелям и ресторанному бизнесу. Нас интересовала конкретно выставка чая и кофе. Но, пробежавшись быстренько по залам, мы не нашли искомого. Тогда я предложила разделиться, побродить и встретиться через 15—20 минут, обсудить увиденное. За эти минуты web-студия «Дивасофт» успела превратиться в арт-кафе, а я поняла, что хочу тут задержаться подольше. Так что команда разделилась, и меньшая часть (1 человек) осталась дегустировать блюда. Чего я только там не попробовала, ммм: жареные каракатицы, паста от итальянского шеф-повара, рыбный суп, печенье, мороженое, булочки, чай и кофе в неограниченном количестве. Посмотрела интереснейшие мастер-классы и приобрела немного вкусностей.

ПИР — индустрия гостеприимства ПИР — индустрия гостеприимства ПИР — индустрия гостеприимства ПИР — индустрия гостеприимства

В это время большая часть команды (2 человека) покоряла «Москва-Сити».

Москва-Сити

Вечером команда «Дивасофт» собралась вместе, и отправилась, наконец, домой, полная новых впечатлений и знаний.