Strict Standards: Declaration of PluginAceadminpanel_ModuleLang::Get() should be compatible with ModuleLang::Get($sName, $aReplace = Array, $bDelete = true) in /home/deer/pages.org.ua/blog/plugins/aceadminpanel/classes/modules/lang/Lang.class.php on line 21 Strict Standards: Declaration of PluginAceadminpanel_ModuleViewer::_SortBlocks() should be compatible with ModuleViewer::_SortBlocks($a, $b) in /home/deer/pages.org.ua/blog/plugins/aceadminpanel/classes/modules/viewer/Viewer.class.php on line 24 TOP топиков / Рейтинг / BPOU - Bitrix blog

Bitrix: предыдущий с последующий элементы для news.detail (через запросы)

Замечательно работавший код без кеша, с кешем компонентов, превратился в «говно», довольно часто стало такое происходить, с этим битриксом.

Посмотрев исходник и запрос, решено было его переписать в рабочий вариант (код стал легче и быстрее)
$iid = $arParams["IBLOCK_ID"];
$dpu = $arParams["DETAIL_URL"];

$q = $DB->query("SET @rank_prev = 0, @rank_cur = 0");
$q = "SELECT prev.ID as ID, prev.NAME as NAME, REPLACE('$dpu', '#ELEMENT_CODE#', prev.CODE) as LINK
FROM (SELECT @rank_cur:=@rank_cur+1 AS rank, ID, NAME, CODE FROM b_iblock_element WHERE ACTIVE = 'Y' AND IBLOCK_ID = '$iid' ORDER BY ACTIVE_FROM DESC) as cur
INNER JOIN (SELECT @rank_prev:=@rank_prev+1 AS rank, ID, NAME, CODE FROM b_iblock_element WHERE ACTIVE = 'Y' AND IBLOCK_ID = '$iid' ORDER BY ACTIVE_FROM DESC) as prev
ON prev.rank + 1 = cur.rank
WHERE cur.ID = ".$arResult["ID"];
$q = $DB->query($q);
while ($ob = $q->getNext()) $arResult["LEFT_ARROW"] = $ob;

$q = $DB->query("SET @rank_cur = 0, @rank_next = 0");
$q = "SELECT next.ID as ID, next.NAME as NAME, REPLACE('$dpu', '#ELEMENT_CODE#', next.CODE) as LINK
FROM (SELECT @rank_cur:=@rank_cur+1 AS rank, ID, NAME, CODE FROM b_iblock_element WHERE ACTIVE = 'Y' AND IBLOCK_ID = '$iid' ORDER BY ACTIVE_FROM DESC) as cur
INNER JOIN (SELECT @rank_next:=@rank_next+1 AS rank, ID, NAME, CODE FROM b_iblock_element WHERE ACTIVE = 'Y' AND IBLOCK_ID = '$iid' ORDER BY ACTIVE_FROM DESC) as next
ON cur.rank + 1 = next.rank
WHERE cur.ID = ".$arResult["ID"];
$q = $DB->query($q);
while ($ob = $q->getNext()) $arResult["RIGHT_ARROW"] = $ob;

Битрикс подкидывает проблем: b_cache_tag занимает много места, сайт тормозит

В версии 12.5 (в 14 возможно исправили, не смотрел ещё), при включенном кешировании компонентов, и большой посещаемости, табличка b_cache_tag начала набирать 15к записей в полчаса, а там одно и тоже, уникальных записей от силы 20. Из-за этого «говна», сайт начал дико тупить, на генерацию страницы уходило по 2 секунды, из-за очень многих инсертов на каждом шагу. Нормальные люди, делают инсерт — если такой записи нет, в битриксе решили не парится и народ везде на форумах, предлагает по крону транкейтить таблицу (это тупо пиздец).

Пришлось переписать функцию «виновника торжества» EndTagCache, запись добавляется только при уникальности составляющих (ибо если её просто отключить, кеширование исходит на говно). Тупой инсерт превратился в умный и красивый

function EndTagCache() {
	global $DB;
	$this->InitCompSalt();

	if($this->bWasTagged) {
		$sqlSITE_ID = $DB->ForSQL(SITE_ID, 2);
		$sqlCACHE_SALT = $this->SALT;

		$strSqlPrefix = "INSERT ".($this->isMySql? "IGNORE": "").
				" INTO b_cache_tag (SITE_ID, CACHE_SALT, RELATIVE_PATH, TAG) VALUES";
		$strSqlPrefix2 = "INSERT ".($this->isMySql? "IGNORE": "").
				" INTO b_cache_tag (SITE_ID, CACHE_SALT, RELATIVE_PATH, TAG) ";

		$maxValuesLen = $this->isMySql? 2048: 0;
		$strSqlValues = $strSqlValues2 = "";

		foreach($this->comp_cache_stack as $arCompCache) {
			$path = $arCompCache[0];
			if(strlen($path)) {
				$this->InitDBCache($path);
				$sqlRELATIVE_PATH = $DB->ForSQL($path, 255);

				$sql = ",\n('".$sqlSITE_ID."', '".$sqlCACHE_SALT."', '".$sqlRELATIVE_PATH."',";
				$sql2 = " SELECT * FROM (SELECT '".$sqlSITE_ID."', '".$sqlCACHE_SALT."', '".$sqlRELATIVE_PATH."', '#TAG#') AS tmp ".
"WHERE NOT EXISTS ( ".
"    SELECT SITE_ID FROM b_cache_tag ".
"    WHERE CACHE_SALT = '".$sqlCACHE_SALT."' ".
"    AND RELATIVE_PATH = '".$sqlRELATIVE_PATH."' ".
"    AND TAG = '#TAG#' ".
") LIMIT 1; \n";

				foreach($arCompCache[1] as $tag => $t) {
					if(!isset($this->DBCacheTags[$path][$tag])) {
						$strSqlValues .= $sql." '".$DB->ForSQL($tag, 50)."')";
						$strSqlValues2 = $strSqlPrefix2.str_replace("#TAG#",$DB->ForSQL($tag, 50), $sql2);
						$DB->Query($strSqlValues2);
						if(strlen($strSqlValues) > $maxValuesLen) {
							//$DB->Query($strSqlPrefix.substr($strSqlValues, 2));
							$strSqlValues = "";
							
						}
						$this->DBCacheTags[$path][$tag] = true;
					}
				}
			}
		}
		if($strSqlValues <> '') {
			//$DB->Query($strSqlPrefix.substr($strSqlValues, 2));
		}
	}

	array_shift($this->comp_cache_stack);
}

В итоге
INSERT IGNORE INTO b_cache_tag (SITE_ID, CACHE_SALT, RELATIVE_PATH, TAG) 
VALUES('ar', '/e25', '/ar/bitrix/news.list/06f', 'iblock_id_5')

Превращается в
INSERT IGNORE INTO b_cache_tag (SITE_ID, CACHE_SALT, RELATIVE_PATH, TAG)  
SELECT * FROM (SELECT 'ar', '/e25', '/ar/bitrix/news.list/06f', 'iblock_id_5') AS tmp 
WHERE NOT EXISTS (
     SELECT SITE_ID FROM b_cache_tag
     WHERE CACHE_SALT = '/e25'
     AND RELATIVE_PATH = '/ar/bitrix/news.list/06f'
     AND TAG = 'iblock_id_5' ) 
LIMIT 1;


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

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

$tarParams = array("replace_space"=>"-","replace_other"=>"-");
$translit = Cutil::translit($arFields["NAME"],"ru",$tarParams);

Bitrix: вывод мета заголовков и описаний (meta-title, meta-description) при кешировании компонентов

Добрый день мне (все равно никто не читает)

Частенько, при включённом кешировании компонент, с помощью «магик» метода
$APPLICATION->SetPageProperty("description", $desc);
в result_modifier.php и template.php не получается ничего вывести/изменить, потому что кеш просто будет выводить первое закешированное для всех записей.

Что бы этого избежать, нужно завсети друга для result_modifier.php, под именем component_epilog.php, который выполняется мимо кеширования, каждый раз. А для того, что бы ему что-то передать, нужно в result_modifier.php добавить
global $APPLICATION;
$cp = $this->__component; 
if (is_object($cp)){
	$cp->arResult['DESC'] = implode(' ', array_slice(explode(' ', strip_tags($arResult["PREVIEW_TEXT"])), 0, 20));
	$cp->SetResultCacheKeys(array('DESC'));
	$arResult['DESC'] = $cp->arResult['DESC'];
}

а с component_epilog.php уже вывести в нужное место
global $APPLICATION;
$APPLICATION->SetPageProperty("description", $arResult["DESC"]);


зы: читать подробности тут dev.1c-bitrix.ru/learning/course/?COURSE_ID=43&LESSON_ID=2975

Bitrix: предыдущий с последующий элементы для news.detail

<?
// файл result_modifier.php news.detail

// упорядочиваем как и в разделе. (получить автоматом нельзя)
$arOrder = array("ACTIVE_FROM" => "DESC"); 
$arSelect = Array("ID", "NAME", "DETAIL_PAGE_URL");
$arFilter = Array("IBLOCK_ID"=>$arParams["IBLOCK_ID"], "ACTIVE_DATE"=>"Y", "ACTIVE"=>"Y");
$res = CIBlockElement::GetList($arOrder, $arFilter, false, array("nPageSize" => "1","nElementID" => $arResult["ID"]), $arSelect);
//"nPageSize" - количество элементов на странице при постраничной навигации 
//"nElementID" - ID элемента который будет выбран вместе со своими соседями. Количество соседей определяется параметром nPageSize.
while($ob = $res->GetNext()) $arFields[] = $ob;

// упорядочиваем соседей, если нет левого или правого
if (count($arFields) == 2 && $arResult["ID"] == $arFields["0"]["ID"]) {	
	$arFields["2"] = $arFields["1"]; 
	unset($arFields["0"]); 
}

if (isset($arFields["0"])) 
	$arResult["LEFT_ARROW"] = array(
		"LINK" => $arFields["0"]["DETAIL_PAGE_URL"],
		"NAME" => $arFields["0"]["NAME"]
	);

if (isset($arFields["2"])) 
	$arResult["RIGHT_ARROW"] = array(
		"LINK" => $arFields["2"]["DETAIL_PAGE_URL"],
		"NAME" => $arFields["2"]["NAME"]
	);
?>

Bitrix конфиг для nginx+php_fpm (fastcgi)

server {
	listen       80;
	server_name  bitrixnginxcgi.com;
	
	location / {
		root  /var/www/bitrixnginxcgi.com;

                index  index.php index.html index.htm;
                if (!-e $request_filename) {
            	    rewrite  ^(.*)$  /bitrix/urlrewrite.php last;
                }
	}
	
	location ~ \.(php|phtml|txt)$ { 
		root /var/www/bitrixnginxcgi.com;
		access_log  /var/log/nginx/bitrixnginxcgi.log;
		if (!-f $request_filename) {
			rewrite  ^(.*)/index.php$  $1/ redirect;
		}
                # если на сайте пользователь может загрузить картинку, то сможет и загрузить эксплойт
                # который фастсиджиай с радостью выполнит. для аплоада не будем включать фастсиджиай
		if ($uri !~ "^/upload/") {
			fastcgi_pass   127.0.0.1:9000;
		}
		fastcgi_index  index.php;
		#fastcgi_param  SCRIPT_FILENAME  /var/www/bitrixnginxcgi.com$fastcgi_script_name;
		include        /etc/nginx/fastcgi_params;
	}
        
        # время жизни для картинок и прочей статики на 7 дней 
	location ~* ^.+\.(bmp|gif|jpg|jpeg|ico|png|swf|tiff|css|js|xml)$ {
		root  /var/www/bitrixnginxcgi.com;
		expires 7d;
	}

        # закрываем старые htaccess и сюда можно включить другие папки (типа гит/свн)
	location ~ /\.hta {
		deny  all;
	}

        # выведем барахлишко в сжатом виде 
	gzip on;
	gzip_types text/plain text/css text/javascript text/x-javascript application/x-javascript;
	gzip_vary on;
	gzip_http_version 1.0;
	gzip_proxied any;
        # IE6 и ниже сжатие не поймут, им как есть отдаём
        # поэтому хакеры притворяются ИЕ6, что бы не разжимать исходные данные 
	gzip_disable "MSIE [1-6]\.";
}

Subscribe: подписываем/отписываем пользователя

if(CModule::IncludeModule("subscribe")){
	if ($_POST["SUBSCRIBE"] == "Y") {
		$arFields = Array(
			"USER_ID" => $USER->GetID(),
			"FORMAT" => "html",
			"EMAIL" => $USER->GetEmail(),
			"ACTIVE" => "Y",
			"RUB_ID" => array()
		);
		$subscr = new CSubscription;
		$ID = $subscr->Add($arFields);
		if($ID>0) {
			CSubscription::Authorize($ID);
		}
	} else {
		$subscription = CSubscription::GetByEmail($USER->GetEmail());
		if($subscription->ExtractFields("str_")) {
			$ID = (integer)$str_ID;
		}
		if ($ID >0) {
			$res = CSubscription::Delete($ID);
		}
	}
}

Получаем UF свойства пользователя (отдельная функция)

function get_user_prop ($id, $prop = "*") {
	if (!($id > 0)) return false;
	if (!is_array($prop)) $prop = array($prop);
	global $DB;
	$res = $DB->query("SELECT ".implode(",", array_map("mysql_real_escape_string", $prop))." FROM `b_uts_user` WHERE VALUE_ID = $id");
	$res = $res->Fetch();
	if (!empty($res)) {
		foreach ($res as $key => $value) {
			if (preg_match("/^a:\d+:{.*?}$/", $value)) {
				$res[$key] = unserialize($value);
			}
		}
	}
	return $res;
}

У анонимных пользователей не работает поиск/теги

Уже второй раз эта проблема, забыл как решил первый раз и пришлось шишку набивать опять.
В модуле поиска в файле search.php в функции проверки прав CheckPermissions грабли с правами, потому мы просто делаем ее обрезанной

function CheckPermissions($FIELD = "sc.ID"){return "((1=1))";}

Где и как эти права задаются в админке, не ясно

Как вывести капчу в Bitrix (работа с custom captcha)

<?
$cpt = new CCaptcha();
$cpt->SetCode();
?>
<label>
	<input type="hidden" name="captcha_sid" value="<?= $cpt->GetSID() ?>">
	<img src="/bitrix/tools/captcha.php?captcha_code=<?=$cpt->sid;?>" alt="" width="135" height="40"/>
	<abbr class="refresh">обновить
изображение</abbr>
</label>
<label>
	<span>Введите символы с картинки: <b>*</b></span> 
	<input type="text" size="12" name="captcha"/>
	<small>Не верно введены символы с картинки</small>
</label>

That's all folks

Bitrix: как сделать пейджер, если выбрал данные через $db->query

Для начала вам нужно сделать запрос с cnt, что бы знать сколько всего записей, дальше вы делаете запрос с лимитом (выводите нужную страницу), а дальше простая магия:

$cnt = new CDBResult;
$cnt->InitFromArray(range(1,$allItemsCnt));
$cnt->NavStart($itemsPerPage, false, (isset($_GET["PAGEN_1"]) ? $_GET["PAGEN_1"] : false));
$arResult["Nav"] = $cnt->GetPageNavStringEx($navComponentObject);
$arResult["NavExt"] = $navComponentObject->arResult;

Почему слетает цена, которая изменена через handler OnBeforeBasketAdd

В битриксе, при обработке корзины, у каждого товара есть своя callback функция (по умолчанию это CatalogBasketCallback).
В фунции CatalogBasketCallback для определения цены, вызывается функция GetOptimalPrice, которая выбирает самую мелкую цену товара. Иногда это нужно, но иногда нет. Что же делать? Нужно прописать в функции OnBeforeBasketAdd:

$arFields["CALLBACK_FUNC"] = "";

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

Отправить файл на скачивание

Что бы отправить файл на скачивание, нужно создать промежуточный файл, который будет выдавать заголовок «Атача». Джаваскрипт этого сделать не может тоже. Что же делать?

У битрикса есть такой стандартный файл, можно все делать через него, ссылку можно сделать так
"/bitrix/admin/fileman_file_download.php?path=".urlencode($filepath)
правда нужно быть залогиненым и с правами на файлманагер, потому мы сделаем свой файл по мотивам fileman_file_download.php.
назовем его getfile.php и положим в корень
<?
require_once($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_before.php");
CModule::IncludeModule("fileman");

$strWarning = "";
$site = CFileMan::__CheckSite($site);
$DOC_ROOT = CSite::GetSiteDocRoot($site);

$io = CBXVirtualIo::GetInstance();
$path = $io->CombinePath("/", $path);

if (preg_match("/bitrix/i",$path))
	die("its wrong");

$arPath = Array($site, $path);
$arParsedPath = CFileMan::ParsePath(Array($site, htmlspecialcharsex($path)));
$abs_path = $DOC_ROOT.$path;

if(!$io->FileExists($abs_path))
	$strWarning = GetMessage("FILEMAN_FILENOT_FOUND")." ";

if(strlen($strWarning) <= 0)
{
	$flTmp = $io->GetFile($abs_path);
	$fsize = $flTmp->GetFileSize();
	header("Content-Type: application/force-download; name=\"".$io->GetPhysicalName($arParsedPath["LAST"])."\"");
	header("Content-Transfer-Encoding: binary");
	header("Content-Length: ".$fsize);
	header("Content-Disposition: attachment; filename=\"".$io->GetPhysicalName($arParsedPath["LAST"])."\"");
	header("Expires: 0");
	header("Cache-Control: no-cache, must-revalidate");
	header("Pragma: no-cache");
	$flTmp->ReadFile();
	die();
}

$APPLICATION->SetTitle(GetMessage("FILEMAN_FILEDOWNLOAD")." \"".$arParsedPath["LAST"]."\"");
?>
<font class="text"><?=$arParsedPath["HTML"]?></font>

<?
ShowError($strWarning);
require_once($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/epilog_after.php"); 
?>


Теперь можно сделать скриптовую заглушку и даже ссылки не менять
<script type="text/javascript">
	$("a[href$=.pdf]").click(function(){
		var url = $(this).attr("href");
		window.open("/getfile.php?path="+encodeURIComponent(url));
		return false;
	});
</script>
Код выше, все файлы pdf отправляет на диалог для скачивания :)

Множественное добавление в корзину bitrix

В битриксе его нет, лол ок.

Можно сделать через массив айди и кодовую фразу с редиректом в корень.
Что бы не лезть в компоненту, запишем все в афтер_коннект.пхп
if (@$_GET["action"] == "ADD2BASKET" && is_array(@$_GET["id"])) {
	if (CModule::IncludeModule("catalog")){
		foreach ($_GET["id"] as $id)
			Add2BasketByProductID($id,1);
	}
	header ("Location: ?");die();
}


По желанию в Add2BasketByProductID можно передать кастомные параметры

Получить файлы с базы одним запросом

$res = CFile::GetList(array("ID"=>"asc"), array("@ID" => implode(",",$collect_imgs)));

while($res_arr = $res->GetNext()) {
	$res_arr["SRC"] = "/upload/".$res_arr["SUBDIR"]."/".$res_arr["FILE_NAME"];
	$arResult["YOURIMAGES"][$res_arr["ID"]] = $res_arr;
}

Где $collect_imgs — массив айдишников картинок

Табличные шаблоны компонент в Битриксе

Как же я хуею, от этого убожества.
Столько лет на рынке, они все на месте, все убогие таблички, во всех компонентах БЛЯ.

Буду сюда выкладывать компоненты, в которых буду удалять таблицы, и их стили.
Со временем.

Везде форма логина/меню заблокированы

Вы перенесли сайт на новое место, а везде для анонимуса показывается форма логина, все меню заблокированы? Не беда! :)

Первым делом в настройках сайта убираете/прописываете наново путь к сайту на диске.
Не помогло, руки не стали расти выше? :)

Тогда вам нужно в корне сайта, создать/отредактировать файл .access.php! Да, все правильно, начинается на точку (стандартный фтп-манагер такие файлы может не видеть, так как в UNIX системах они называются скрытые — копните настройки)

А в файле должна появится строчка:
$PERM["/"]["*"]="R";

Не забываем, что это пыхапы файл ;)
<?
$PERM["/"]["*"]="R";
?>

Файл авторизации auth.php

<? define("NEED_AUTH", true);
require($_SERVER["DOCUMENT_ROOT"]."/bitrix/header.php");

if (isset($_REQUEST["backurl"]) && strlen($_REQUEST["backurl"])>0) LocalRedirect($backurl);

$APPLICATION->SetTitle("Authorization"); ?>

<script type="text/javascript">
	location.href="/";
</script>
<p><a href="<?=SITE_DIR?>">Go to main page</a></p>

<?require($_SERVER["DOCUMENT_ROOT"]."/bitrix/footer.php");?>

Самый обычный .htaccess для битрикса

Options -Indexes 
ErrorDocument 404 /404.php

#php_flag allow_call_time_pass_reference 1
#php_flag session.use_trans_sid off

#php_value display_errors 1

php_value mbstring.func_overload 2
php_value mbstring.internal_encoding UTF-8

<IfModule mod_rewrite.c>
  Options +FollowSymLinks
  RewriteEngine On
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-l
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteCond %{REQUEST_FILENAME} !/bitrix/urlrewrite.php$
  RewriteRule ^(.*)$ /bitrix/urlrewrite.php [L]
</IfModule>

<IfModule mod_dir.c>
  DirectoryIndex index.php index.html
</IfModule>

<IfModule mod_expires.c>
  ExpiresActive on
  ExpiresByType image/jpeg "access plus 3 day"
  ExpiresByType image/gif "access plus 3 day"
</IfModule>

Обычная 404 страница

<? # заголовки 404, что бы поисковики были умничками
CHTTP::SetStatus("404 Not Found"); // может убивать некоторые серваки
@define("ERROR_404","Y");

# для 404 картинок отдаём пустышку
$arrImage = array("jpg","bmp","jpeg","jpe","gif","png");
$arrPath = pathinfo($_SERVER["REQUEST_URI"]);
if (in_array($arrPath["extension"],$arrImage)) die();

# делаем редирект, если он есть в правилах
include_once($_SERVER['DOCUMENT_ROOT'].'/bitrix/modules/main/include/urlrewrite.php');

require($_SERVER["DOCUMENT_ROOT"]."/bitrix/header.php");
$APPLICATION->SetTitle("404 - страница не найдена"); ?>

<? $APPLICATION->IncludeComponent("bitrix:main.map", ".default", array(
	"CACHE_TYPE" => "N",
	"CACHE_TIME" => "3600",
	"SET_TITLE" => "Y",
	"LEVEL" => "3",
	"COL_NUM" => "2",
	"SHOW_DESCRIPTION" => "Y"
	),
	false
);?>

<? require($_SERVER["DOCUMENT_ROOT"]."/bitrix/footer.php"); ?>