Your IP : 3.22.79.165


Current Path : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/main/classes/general/
Upload File :
Current File : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/main/classes/general/group.php

<?php

/**
 * Bitrix Framework
 * @package bitrix
 * @subpackage main
 * @copyright 2001-2023 Bitrix
 */

use Bitrix\Main\Authentication\Internal\ModuleGroupTable;
use Bitrix\Main\Authentication\Internal\GroupSubordinateTable;
use Bitrix\Main\Type\Collection;
use Bitrix\Main\GroupTable;

class CAllGroup
{
	public $LAST_ERROR;

	public function Add($arFields)
	{
		/** @global CMain $APPLICATION */
		global $DB, $APPLICATION;

		if (!$this->CheckFields($arFields))
		{
			return false;
		}

		foreach (GetModuleEvents("main", "OnBeforeGroupAdd", true) as $arEvent)
		{
			$bEventRes = ExecuteModuleEventEx($arEvent, [&$arFields]);
			if ($bEventRes === false)
			{
				if ($err = $APPLICATION->GetException())
				{
					$this->LAST_ERROR .= $err->GetString() . "<br>";
				}
				else
				{
					$this->LAST_ERROR .= "Unknown error in OnBeforeGroupAdd handler." . "<br>";
				}
				return false;
			}
		}

		if (is_set($arFields, "ACTIVE") && $arFields["ACTIVE"] != "Y")
		{
			$arFields["ACTIVE"] = "N";
		}

		$ID = $DB->Add("b_group", $arFields);

		if (is_array($arFields["USER_ID"]) && !empty($arFields["USER_ID"]))
		{
			if (is_array($arFields["USER_ID"][0]) && !empty($arFields["USER_ID"][0]))
			{
				$arTmp = [];
				foreach ($arFields["USER_ID"] as $userId)
				{
					if (intval($userId["USER_ID"]) > 0
						&& !in_array(intval($userId["USER_ID"]), $arTmp))
					{
						$arInsert = $DB->PrepareInsert("b_user_group", $userId);

						$strSql =
							"INSERT INTO b_user_group(GROUP_ID, " . $arInsert[0] . ") " .
							"VALUES(" . $ID . ", " . $arInsert[1] . ")";
						$DB->Query($strSql);

						$arTmp[] = intval($userId["USER_ID"]);
					}
				}
			}
			else
			{
				$strUsers = "0";
				foreach ($arFields["USER_ID"] as $userId)
				{
					$strUsers .= "," . intval($userId);
				}

				$strSql =
					"INSERT INTO b_user_group(GROUP_ID, USER_ID) " .
					"SELECT " . $ID . ", ID " .
					"FROM b_user " .
					"WHERE ID in (" . $strUsers . ")";

				$DB->Query($strSql);
			}
			CUser::clearUserGroupCache();
		}

		$arFields["ID"] = $ID;

		foreach (GetModuleEvents("main", "OnAfterGroupAdd", true) as $arEvent)
		{
			ExecuteModuleEventEx($arEvent, [&$arFields]);
		}

		GroupTable::cleanCache();

		return $ID;
	}

	public static function GetDropDownList($strSqlSearch = "and ACTIVE='Y'", $strSqlOrder = "ORDER BY C_SORT, NAME, ID")
	{
		global $DB;
		$connection = \Bitrix\Main\Application::getConnection();
		$helper = $connection->getSqlHelper();

		$strSql = "
			SELECT
				ID as REFERENCE_ID,
				" . $helper->getConcatFunction("NAME", "' ['", "ID", "']'") . " as REFERENCE
			FROM
				b_group
			WHERE
				1=1
			$strSqlSearch
			$strSqlOrder
			";
		$res = $DB->Query($strSql);

		return $res;
	}

	public static function GetList($by = 'c_sort', $order = 'asc', $arFilter = [], $SHOW_USERS_AMOUNT = "N")
	{
		global $DB;
		$connection = \Bitrix\Main\Application::getConnection();
		$helper = $connection->getSqlHelper();

		$arSqlSearch = $arSqlSearch_h = [];
		$strSqlSearch_h = "";
		if (is_array($arFilter))
		{
			foreach ($arFilter as $key => $val)
			{
				if (is_array($val))
				{
					if (empty($val))
					{
						continue;
					}
				}
				else
				{
					if ((string)$val == '' || $val == "NOT_REF")
					{
						continue;
					}
				}
				$key = strtoupper($key);
				$match_value_set = array_key_exists($key . "_EXACT_MATCH", $arFilter);
				switch ($key)
				{
					case "ID":
						$match = ($match_value_set && $arFilter[$key . "_EXACT_MATCH"] == "N") ? "Y" : "N";
						$arSqlSearch[] = GetFilterQuery("G.ID", $val, $match);
						break;
					case "TIMESTAMP_1":
						$arSqlSearch[] = "G.TIMESTAMP_X >= FROM_UNIXTIME('" . MkDateTime(FmtDate($val, "D.M.Y"), "d.m.Y") . "')";
						break;
					case "TIMESTAMP_2":
						$arSqlSearch[] = "G.TIMESTAMP_X <= FROM_UNIXTIME('" . MkDateTime(FmtDate($val, "D.M.Y") . " 23:59:59", "d.m.Y") . "')";
						break;
					case "ACTIVE":
						$arSqlSearch[] = ($val == "Y") ? "G.ACTIVE='Y'" : "G.ACTIVE='N'";
						break;
					case "ADMIN":
						if (COption::GetOptionString("main", "controller_member", "N") == "Y" && COption::GetOptionString("main", "~controller_limited_admin", "N") == "Y")
						{
							if ($val == "Y")
							{
								$arSqlSearch[] = "G.ID=0";
							}
							break;
						}
						else
						{
							$arSqlSearch[] = ($val == "Y") ? "G.ID=1" : "G.ID>1";
						}
						break;
					case "NAME":
						$match = ($match_value_set && $arFilter[$key . "_EXACT_MATCH"] == "Y") ? "N" : "Y";
						$arSqlSearch[] = GetFilterQuery("G.NAME", $val, $match);
						break;
					case "STRING_ID":
						$match = ($match_value_set && $arFilter[$key . "_EXACT_MATCH"] == "N") ? "Y" : "N";
						$arSqlSearch[] = GetFilterQuery("G.STRING_ID", $val, $match);
						break;
					case "DESCRIPTION":
						$match = ($match_value_set && $arFilter[$key . "_EXACT_MATCH"] == "Y") ? "N" : "Y";
						$arSqlSearch[] = GetFilterQuery("G.DESCRIPTION", $val, $match);
						break;
					case "USERS_1":
						$SHOW_USERS_AMOUNT = "Y";
						$arSqlSearch_h[] = "USERS>=" . intval($val);
						break;
					case "USERS_2":
						$SHOW_USERS_AMOUNT = "Y";
						$arSqlSearch_h[] = "USERS<=" . intval($val);
						break;
					case "ANONYMOUS":
						if ($val == 'Y' || $val == 'N')
						{
							$arSqlSearch[] = "G.ANONYMOUS='" . $val . "'";
						}
						break;
				}
			}
			foreach ($arSqlSearch_h as $condition)
			{
				$strSqlSearch_h .= " and (" . $condition . ") ";
			}
		}

		$by = strtolower($by);

		if ($by == "id")
		{
			$strSqlOrder = " ORDER BY G.ID ";
		}
		elseif ($by == "active")
		{
			$strSqlOrder = " ORDER BY G.ACTIVE ";
		}
		elseif ($by == "timestamp_x")
		{
			$strSqlOrder = " ORDER BY G.TIMESTAMP_X ";
		}
		elseif ($by == "c_sort")
		{
			$strSqlOrder = " ORDER BY G.C_SORT ";
		}
		elseif ($by == "sort")
		{
			$strSqlOrder = " ORDER BY G.C_SORT, G.NAME, G.ID ";
		}
		elseif ($by == "name")
		{
			$strSqlOrder = " ORDER BY G.NAME ";
		}
		elseif ($by == "string_id")
		{
			$strSqlOrder = " ORDER BY G.STRING_ID ";
		}
		elseif ($by == "description")
		{
			$strSqlOrder = " ORDER BY G.DESCRIPTION ";
		}
		elseif ($by == "anonymous")
		{
			$strSqlOrder = " ORDER BY G.ANONYMOUS ";
		}
		elseif ($by == "dropdown")
		{
			$strSqlOrder = " ORDER BY C_SORT, NAME ";
		}
		elseif ($by == "users")
		{
			$strSqlOrder = " ORDER BY USERS ";
			$SHOW_USERS_AMOUNT = "Y";
		}
		else
		{
			$strSqlOrder = " ORDER BY G.C_SORT ";
		}

		if (strtolower($order) == "desc")
		{
			$strSqlOrder .= " desc ";
		}
		else
		{
			$strSqlOrder .= " asc ";
		}

		$str_USERS = $str_TABLE = "";
		if ($SHOW_USERS_AMOUNT == "Y")
		{
			$str_USERS = "count(distinct U.USER_ID)						USERS,";
			$str_TABLE = "LEFT JOIN b_user_group U ON (U.GROUP_ID=G.ID AND ((U.DATE_ACTIVE_FROM IS NULL) OR (U.DATE_ACTIVE_FROM <= " . $DB->CurrentTimeFunction() . ")) AND ((U.DATE_ACTIVE_TO IS NULL) OR (U.DATE_ACTIVE_TO >= " . $DB->CurrentTimeFunction() . ")))";
		}
		$strSqlSearch = GetFilterSqlSearch($arSqlSearch);
		$strSql = "
			SELECT
				G.ID, G.ACTIVE, G.C_SORT, G.ANONYMOUS, G.NAME, G.DESCRIPTION, G.STRING_ID,
				" . $str_USERS . "
				G.ID REFERENCE_ID,
				" . $helper->getConcatFunction("G.NAME", "' ['", "G.ID", "']'") . " REFERENCE,
				" . $DB->DateToCharFunction("G.TIMESTAMP_X") . " TIMESTAMP_X
			FROM
				b_group G
			" . $str_TABLE . "
			WHERE
			" . $strSqlSearch . "
			GROUP BY
				G.ID, G.ACTIVE, G.C_SORT, G.TIMESTAMP_X, G.ANONYMOUS, G.NAME, G.STRING_ID, G.DESCRIPTION
			HAVING
				1=1
				" . $strSqlSearch_h . "
			" . $strSqlOrder;

		$res = $DB->Query($strSql);
		$res->is_filtered = (IsFiltered($strSqlSearch) || $strSqlSearch_h <> '');
		return $res;
	}

	//*************** COMMON UTILS *********************/
	public static function GetFilterOperation($key)
	{
		$strNegative = "N";
		if (str_starts_with($key, "!"))
		{
			$key = substr($key, 1);
			$strNegative = "Y";
		}

		$strOrNull = "N";
		if (str_starts_with($key, "+"))
		{
			$key = substr($key, 1);
			$strOrNull = "Y";
		}

		if (str_starts_with($key, ">="))
		{
			$key = substr($key, 2);
			$strOperation = ">=";
		}
		elseif (str_starts_with($key, ">"))
		{
			$key = substr($key, 1);
			$strOperation = ">";
		}
		elseif (str_starts_with($key, "<="))
		{
			$key = substr($key, 2);
			$strOperation = "<=";
		}
		elseif (str_starts_with($key, "<"))
		{
			$key = substr($key, 1);
			$strOperation = "<";
		}
		elseif (str_starts_with($key, "@"))
		{
			$key = substr($key, 1);
			$strOperation = "IN";
		}
		elseif (str_starts_with($key, "~"))
		{
			$key = substr($key, 1);
			$strOperation = "LIKE";
		}
		elseif (str_starts_with($key, "%"))
		{
			$key = substr($key, 1);
			$strOperation = "QUERY";
		}
		else
		{
			$strOperation = "=";
		}

		return ["FIELD" => $key, "NEGATIVE" => $strNegative, "OPERATION" => $strOperation, "OR_NULL" => $strOrNull];
	}

	public static function PrepareSql($arFields, $arOrder, $arFilter, $arGroupBy, $arSelectFields)
	{
		global $DB;

		$strSqlSelect = "";
		$strSqlFrom = "";
		$strSqlWhere = "";
		$strSqlGroupBy = "";

		$arGroupByFunct = ["COUNT", "AVG", "MIN", "MAX", "SUM"];

		$arAlreadyJoined = [];

		// GROUP BY -->
		if (is_array($arGroupBy) && !empty($arGroupBy))
		{
			$arSelectFields = $arGroupBy;
			foreach ($arGroupBy as $key => $val)
			{
				$val = strtoupper($val);
				$key = strtoupper($key);
				if (array_key_exists($val, $arFields) && !in_array($key, $arGroupByFunct))
				{
					if ($strSqlGroupBy <> '')
					{
						$strSqlGroupBy .= ", ";
					}
					$strSqlGroupBy .= $arFields[$val]["FIELD"];

					if (isset($arFields[$val]["FROM"])
						&& $arFields[$val]["FROM"] <> ''
						&& !in_array($arFields[$val]["FROM"], $arAlreadyJoined))
					{
						if ($strSqlFrom <> '')
						{
							$strSqlFrom .= " ";
						}
						$strSqlFrom .= $arFields[$val]["FROM"];
						$arAlreadyJoined[] = $arFields[$val]["FROM"];
					}
				}
			}
		}
		// <-- GROUP BY

		// SELECT -->
		$arFieldsKeys = array_keys($arFields);

		if (is_array($arGroupBy) && empty($arGroupBy))
		{
			$strSqlSelect = "COUNT(%%_DISTINCT_%% " . $arFields[$arFieldsKeys[0]]["FIELD"] . ") as CNT ";
		}
		else
		{
			if (isset($arSelectFields) && is_string($arSelectFields) && $arSelectFields <> '' && array_key_exists($arSelectFields, $arFields))
			{
				$arSelectFields = [$arSelectFields];
			}

			if (empty($arSelectFields)
				|| !is_array($arSelectFields)
				|| in_array("*", $arSelectFields)
			)
			{
				foreach ($arFields as $FIELD_ID => $arField)
				{
					if (isset($arField["WHERE_ONLY"])
						&& $arField["WHERE_ONLY"] == "Y")
					{
						continue;
					}

					if ($strSqlSelect <> '')
					{
						$strSqlSelect .= ", ";
					}

					if ($arField["TYPE"] == "datetime")
					{
						$strSqlSelect .= $DB->DateToCharFunction($arField["FIELD"], "FULL") . " as " . $FIELD_ID;
					}
					elseif ($arField["TYPE"] == "date")
					{
						$strSqlSelect .= $DB->DateToCharFunction($arField["FIELD"], "SHORT") . " as " . $FIELD_ID;
					}
					else
					{
						$strSqlSelect .= $arField["FIELD"] . " as " . $FIELD_ID;
					}

					if (isset($arField["FROM"])
						&& $arField["FROM"] <> ''
						&& !in_array($arField["FROM"], $arAlreadyJoined))
					{
						if ($strSqlFrom <> '')
						{
							$strSqlFrom .= " ";
						}
						$strSqlFrom .= $arField["FROM"];
						$arAlreadyJoined[] = $arField["FROM"];
					}
				}
			}
			else
			{
				foreach ($arSelectFields as $key => $val)
				{
					$val = strtoupper($val);
					$key = strtoupper($key);
					if (array_key_exists($val, $arFields))
					{
						if ($strSqlSelect <> '')
						{
							$strSqlSelect .= ", ";
						}

						if (in_array($key, $arGroupByFunct))
						{
							$strSqlSelect .= $key . "(" . $arFields[$val]["FIELD"] . ") as " . $val;
						}
						else
						{
							if ($arFields[$val]["TYPE"] == "datetime")
							{
								$strSqlSelect .= $DB->DateToCharFunction($arFields[$val]["FIELD"], "FULL") . " as " . $val;
							}
							elseif ($arFields[$val]["TYPE"] == "date")
							{
								$strSqlSelect .= $DB->DateToCharFunction($arFields[$val]["FIELD"], "SHORT") . " as " . $val;
							}
							else
							{
								$strSqlSelect .= $arFields[$val]["FIELD"] . " as " . $val;
							}
						}

						if (isset($arFields[$val]["FROM"])
							&& $arFields[$val]["FROM"] <> ''
							&& !in_array($arFields[$val]["FROM"], $arAlreadyJoined))
						{
							if ($strSqlFrom <> '')
							{
								$strSqlFrom .= " ";
							}
							$strSqlFrom .= $arFields[$val]["FROM"];
							$arAlreadyJoined[] = $arFields[$val]["FROM"];
						}
					}
				}
			}

			if ($strSqlGroupBy <> '')
			{
				if ($strSqlSelect <> '')
				{
					$strSqlSelect .= ", ";
				}
				$strSqlSelect .= "COUNT(%%_DISTINCT_%% " . $arFields[$arFieldsKeys[0]]["FIELD"] . ") as CNT";
			}
			else
			{
				$strSqlSelect = "%%_DISTINCT_%% " . $strSqlSelect;
			}
		}
		// <-- SELECT

		// WHERE -->
		$arSqlSearch = [];

		if (is_array($arFilter))
		{
			foreach ($arFilter as $key => $vals)
			{
				if (!is_array($vals))
				{
					$vals = [$vals];
				}

				$key_res = static::GetFilterOperation($key);
				$key = $key_res["FIELD"];
				$strNegative = $key_res["NEGATIVE"];
				$strOperation = $key_res["OPERATION"];
				$strOrNull = $key_res["OR_NULL"];

				if (array_key_exists($key, $arFields))
				{
					$arSqlSearch_tmp = [];
					foreach ($vals as $val)
					{
						if (isset($arFields[$key]["WHERE"]))
						{
							$arSqlSearch_tmp1 = call_user_func_array(
								$arFields[$key]["WHERE"],
								[$val, $key, $strOperation, $strNegative, $arFields[$key]["FIELD"], $arFields, $arFilter]
							);
							if ($arSqlSearch_tmp1 !== false)
							{
								$arSqlSearch_tmp[] = $arSqlSearch_tmp1;
							}
						}
						else
						{
							if ($arFields[$key]["TYPE"] == "int")
							{
								if (intval($val) <= 0)
								{
									$arSqlSearch_tmp[] = ($strNegative == "Y" ? "NOT" : "") . "(" . $arFields[$key]["FIELD"] . " IS NULL OR " . $arFields[$key]["FIELD"] . " <= 0)";
								}
								else
								{
									$arSqlSearch_tmp[] = ($strNegative == "Y" ? " " . $arFields[$key]["FIELD"] . " IS NULL OR NOT " : "") . "(" . $arFields[$key]["FIELD"] . " " . $strOperation . " " . intval($val) . " )";
								}
							}
							elseif ($arFields[$key]["TYPE"] == "double")
							{
								$val = str_replace(",", ".", $val);
								if (DoubleVal($val) <= 0)
								{
									$arSqlSearch_tmp[] = ($strNegative == "Y" ? "NOT" : "") . "(" . $arFields[$key]["FIELD"] . " IS NULL OR " . $arFields[$key]["FIELD"] . " <= 0)";
								}
								else
								{
									$arSqlSearch_tmp[] = ($strNegative == "Y" ? " " . $arFields[$key]["FIELD"] . " IS NULL OR NOT " : "") . "(" . $arFields[$key]["FIELD"] . " " . $strOperation . " " . DoubleVal($val) . " )";
								}
							}
							elseif ($arFields[$key]["TYPE"] == "string" || $arFields[$key]["TYPE"] == "char")
							{
								if ($strOperation == "QUERY")
								{
									$arSqlSearch_tmp[] = GetFilterQuery($arFields[$key]["FIELD"], $val, "Y");
								}
								else
								{
									if ((string)$val == '')
									{
										$arSqlSearch_tmp[] = ($strNegative == "Y" ? "NOT" : "") . "(" . $arFields[$key]["FIELD"] . " IS NULL OR LENGTH(" . $arFields[$key]["FIELD"] . ")<=0)";
									}
									else
									{
										$arSqlSearch_tmp[] = ($strNegative == "Y" ? " " . $arFields[$key]["FIELD"] . " IS NULL OR NOT " : "") . "(" . $arFields[$key]["FIELD"] . " " . $strOperation . " '" . $DB->ForSql($val) . "' )";
									}
								}
							}
							elseif ($arFields[$key]["TYPE"] == "datetime")
							{
								if ((string)$val == '')
								{
									$arSqlSearch_tmp[] = ($strNegative == "Y" ? "NOT" : "") . "(" . $arFields[$key]["FIELD"] . " IS NULL)";
								}
								else
								{
									$arSqlSearch_tmp[] = ($strNegative == "Y" ? " " . $arFields[$key]["FIELD"] . " IS NULL OR NOT " : "") . "(" . $arFields[$key]["FIELD"] . " " . $strOperation . " " . $DB->CharToDateFunction($DB->ForSql($val), "FULL") . ")";
								}
							}
							elseif ($arFields[$key]["TYPE"] == "date")
							{
								if ((string)$val == '')
								{
									$arSqlSearch_tmp[] = ($strNegative == "Y" ? "NOT" : "") . "(" . $arFields[$key]["FIELD"] . " IS NULL)";
								}
								else
								{
									$arSqlSearch_tmp[] = ($strNegative == "Y" ? " " . $arFields[$key]["FIELD"] . " IS NULL OR NOT " : "") . "(" . $arFields[$key]["FIELD"] . " " . $strOperation . " " . $DB->CharToDateFunction($DB->ForSql($val), "SHORT") . ")";
								}
							}
						}
					}

					if (isset($arFields[$key]["FROM"])
						&& $arFields[$key]["FROM"] <> ''
						&& !in_array($arFields[$key]["FROM"], $arAlreadyJoined))
					{
						if ($strSqlFrom <> '')
						{
							$strSqlFrom .= " ";
						}
						$strSqlFrom .= $arFields[$key]["FROM"];
						$arAlreadyJoined[] = $arFields[$key]["FROM"];
					}

					$strSqlSearch_tmp = "";
					foreach ($arSqlSearch_tmp as $condition)
					{
						if ($strSqlSearch_tmp != "")
						{
							$strSqlSearch_tmp .= ($strNegative == "Y" ? " AND " : " OR ");
						}
						$strSqlSearch_tmp .= "(" . $condition . ")";
					}
					if ($strOrNull == "Y")
					{
						if ($strSqlSearch_tmp != "")
						{
							$strSqlSearch_tmp .= ($strNegative == "Y" ? " AND " : " OR ");
						}
						$strSqlSearch_tmp .= "(" . $arFields[$key]["FIELD"] . " IS " . ($strNegative == "Y" ? "NOT " : "") . "NULL)";
					}

					if ($strSqlSearch_tmp != "")
					{
						$arSqlSearch[] = "(" . $strSqlSearch_tmp . ")";
					}
				}
			}
		}

		foreach ($arSqlSearch as $condition)
		{
			if ($strSqlWhere != "")
			{
				$strSqlWhere .= " AND ";
			}
			$strSqlWhere .= "(" . $condition . ")";
		}
		// <-- WHERE

		// ORDER BY -->
		$arSqlOrder = [];
		foreach ($arOrder as $by => $order)
		{
			$by = strtoupper($by);
			$order = strtoupper($order);
			if ($order != "ASC")
			{
				$order = "DESC";
			}

			if (array_key_exists($by, $arFields))
			{
				$arSqlOrder[] = " " . $arFields[$by]["FIELD"] . " " . $order . " ";

				if (isset($arFields[$by]["FROM"])
					&& $arFields[$by]["FROM"] <> ''
					&& !in_array($arFields[$by]["FROM"], $arAlreadyJoined))
				{
					if ($strSqlFrom <> '')
					{
						$strSqlFrom .= " ";
					}
					$strSqlFrom .= $arFields[$by]["FROM"];
					$arAlreadyJoined[] = $arFields[$by]["FROM"];
				}
			}
		}

		$strSqlOrderBy = implode(", ", $arSqlOrder);
		// <-- ORDER BY

		return [
			"SELECT" => $strSqlSelect,
			"FROM" => $strSqlFrom,
			"WHERE" => $strSqlWhere,
			"GROUPBY" => $strSqlGroupBy,
			"ORDERBY" => $strSqlOrderBy,
		];
	}

	public static function GetListEx($arOrder = ["ID" => "DESC"], $arFilter = [], $arGroupBy = false, $arNavStartParams = false, $arSelectFields = [])
	{
		global $DB;

		if (empty($arSelectFields))
		{
			$arSelectFields = ["ID", "TIMESTAMP_X", "ACTIVE", "C_SORT", "ANONYMOUS", "NAME", "DESCRIPTION"];
		}

		// FIELDS -->
		$arFields = [
			"ID" => ["FIELD" => "G.ID", "TYPE" => "int"],
			"TIMESTAMP_X" => ["FIELD" => "G.TIMESTAMP_X", "TYPE" => "datetime"],
			"ACTIVE" => ["FIELD" => "G.ACTIVE", "TYPE" => "char"],
			"C_SORT" => ["FIELD" => "G.C_SORT", "TYPE" => "int"],
			"ANONYMOUS" => ["FIELD" => "G.ANONYMOUS", "TYPE" => "char"],
			"NAME" => ["FIELD" => "G.NAME", "TYPE" => "string"],
			"STRING_ID" => ["FIELD" => "G.STRING_ID", "TYPE" => "string"],
			"DESCRIPTION" => ["FIELD" => "G.DESCRIPTION", "TYPE" => "string"],
			"USER_USER_ID" => ["FIELD" => "UG.USER_ID", "TYPE" => "int", "FROM" => "INNER JOIN b_user_group UG ON (G.ID = UG.GROUP_ID)"],
			"USER_GROUP_ID" => ["FIELD" => "UG.GROUP_ID", "TYPE" => "string", "FROM" => "INNER JOIN b_user_group UG ON (G.ID = UG.GROUP_ID)"],
			"USER_DATE_ACTIVE_FROM" => ["FIELD" => "UG.DATE_ACTIVE_FROM", "TYPE" => "datetime", "FROM" => "INNER JOIN b_user_group UG ON (G.ID = UG.GROUP_ID)"],
			"USER_DATE_ACTIVE_TO" => ["FIELD" => "UG.DATE_ACTIVE_TO", "TYPE" => "datetime", "FROM" => "INNER JOIN b_user_group UG ON (G.ID = UG.GROUP_ID)"],
		];
		// <-- FIELDS

		$arSqls = static::PrepareSql($arFields, $arOrder, $arFilter, $arGroupBy, $arSelectFields);

		$arSqls["SELECT"] = str_replace("%%_DISTINCT_%%", "DISTINCT", $arSqls["SELECT"]);

		if (is_array($arGroupBy) && empty($arGroupBy))
		{
			$strSql =
				"SELECT " . $arSqls["SELECT"] . " " .
				"FROM b_group G " .
				"	" . $arSqls["FROM"] . " ";
			if ($arSqls["WHERE"] <> '')
			{
				$strSql .= "WHERE " . $arSqls["WHERE"] . " ";
			}
			if ($arSqls["GROUPBY"] <> '')
			{
				$strSql .= "GROUP BY " . $arSqls["GROUPBY"] . " ";
			}

			$dbRes = $DB->Query($strSql);
			if ($arRes = $dbRes->Fetch())
			{
				return $arRes["CNT"];
			}
			else
			{
				return false;
			}
		}

		$strSql =
			"SELECT " . $arSqls["SELECT"] . " " .
			"FROM b_group G " .
			"	" . $arSqls["FROM"] . " ";
		if ($arSqls["WHERE"] <> '')
		{
			$strSql .= "WHERE " . $arSqls["WHERE"] . " ";
		}
		if ($arSqls["GROUPBY"] <> '')
		{
			$strSql .= "GROUP BY " . $arSqls["GROUPBY"] . " ";
		}
		if ($arSqls["ORDERBY"] <> '')
		{
			$strSql .= "ORDER BY " . $arSqls["ORDERBY"] . " ";
		}

		if (is_array($arNavStartParams) && intval($arNavStartParams["nTopCount"]) <= 0)
		{
			$strSql_tmp =
				"SELECT COUNT('x') as CNT " .
				"FROM b_group G " .
				"	" . $arSqls["FROM"] . " ";
			if ($arSqls["WHERE"] <> '')
			{
				$strSql_tmp .= "WHERE " . $arSqls["WHERE"] . " ";
			}
			if ($arSqls["GROUPBY"] <> '')
			{
				$strSql_tmp .= "GROUP BY " . $arSqls["GROUPBY"] . " ";
			}

			$dbRes = $DB->Query($strSql_tmp);
			$cnt = 0;
			if ($arSqls["GROUPBY"] == '')
			{
				if ($arRes = $dbRes->Fetch())
				{
					$cnt = $arRes["CNT"];
				}
			}
			else
			{
				$cnt = $dbRes->SelectedRowsCount();
			}

			$dbRes = new CDBResult();
			$dbRes->NavQuery($strSql, $cnt, $arNavStartParams);
		}
		else
		{
			if (is_array($arNavStartParams) && intval($arNavStartParams["nTopCount"]) > 0)
			{
				$strSql .= "LIMIT " . intval($arNavStartParams["nTopCount"]);
			}
			$dbRes = $DB->Query($strSql);
		}

		return $dbRes;
	}

	public static function GetByID($ID, $SHOW_USERS_AMOUNT = "N")
	{
		global $DB;

		$ID = intval($ID);

		$strSql = "SELECT G.ID, G.ACTIVE, G.C_SORT, G.ANONYMOUS, G.NAME, G.STRING_ID, G.DESCRIPTION, " . $DB->DateToCharFunction("G.TIMESTAMP_X") . " as TIMESTAMP_X ";

		if ($SHOW_USERS_AMOUNT == "Y")
		{
			$strSql .= ", count(distinct U.USER_ID) USERS ";
		}
		else
		{
			$strSql .= ", G.SECURITY_POLICY ";
		}

		$strSql .= "FROM b_group G ";

		if ($SHOW_USERS_AMOUNT == "Y")
		{
			$strSql .= "LEFT JOIN b_user_group U ON (U.GROUP_ID=G.ID AND ((U.DATE_ACTIVE_FROM IS NULL) OR (U.DATE_ACTIVE_FROM <= " . $DB->CurrentTimeFunction() . ")) AND ((U.DATE_ACTIVE_TO IS NULL) OR (U.DATE_ACTIVE_TO >= " . $DB->CurrentTimeFunction() . "))) ";
		}

		$strSql .= "WHERE G.ID = " . $ID . " ";

		if ($SHOW_USERS_AMOUNT == "Y")
		{
			$strSql .= "GROUP BY G.ID, G.ACTIVE, G.C_SORT, G.TIMESTAMP_X, G.ANONYMOUS, G.NAME, G.STRING_ID, G.DESCRIPTION";
		}

		$z = $DB->Query($strSql);
		return $z;
	}

	public function CheckFields($arFields, $ID = false)
	{
		global $DB;
		$this->LAST_ERROR = "";

		if (is_set($arFields, "NAME") && $arFields["NAME"] == '')
		{
			$this->LAST_ERROR .= GetMessage("BAD_GROUP_NAME") . "<br>";
		}

		if (is_array($arFields["USER_ID"]) && !empty($arFields["USER_ID"]))
		{
			if (is_array($arFields["USER_ID"][0]) && !empty($arFields["USER_ID"][0]))
			{
				foreach ($arFields["USER_ID"] as $arUser)
				{
					if ($arUser["DATE_ACTIVE_FROM"] <> '' && !CheckDateTime($arUser["DATE_ACTIVE_FROM"]))
					{
						$error = str_replace("#USER_ID#", $arUser["USER_ID"], GetMessage("WRONG_USER_DATE_ACTIVE_FROM"));
						$this->LAST_ERROR .= $error . "<br>";
					}

					if ($arUser["DATE_ACTIVE_TO"] <> '' && !CheckDateTime($arUser["DATE_ACTIVE_TO"]))
					{
						$error = str_replace("#USER_ID#", $arUser["USER_ID"], GetMessage("WRONG_USER_DATE_ACTIVE_TO"));
						$this->LAST_ERROR .= $error . "<br>";
					}
				}
			}
		}
		if (isset($arFields['STRING_ID']) && $arFields['STRING_ID'] <> '')
		{
			$sql_str = "SELECT G.ID
					FROM b_group G
					WHERE G.STRING_ID='" . $DB->ForSql($arFields['STRING_ID']) . "'";
			$z = $DB->Query($sql_str);
			if ($r = $z->Fetch())
			{
				if ($ID === false || $ID != $r['ID'])
				{
					$this->LAST_ERROR .= GetMessage('MAIN_ERROR_STRING_ID') . "<br>";
				}
			}
		}
		if ($this->LAST_ERROR <> '')
		{
			return false;
		}

		return true;
	}

	public function Update($ID, $arFields)
	{
		/** @global CMain $APPLICATION */
		global $DB, $APPLICATION;

		$ID = intval($ID);

		if (!$this->CheckFields($arFields, $ID))
		{
			return false;
		}

		foreach (GetModuleEvents("main", "OnBeforeGroupUpdate", true) as $arEvent)
		{
			$bEventRes = ExecuteModuleEventEx($arEvent, [$ID, &$arFields]);
			if ($bEventRes === false)
			{
				if ($err = $APPLICATION->GetException())
				{
					$this->LAST_ERROR .= $err->GetString() . "<br>";
				}
				else
				{
					$this->LAST_ERROR .= "Unknown error in OnBeforeGroupUpdate handler." . "<br>";
				}
				return false;
			}
		}

		if ($ID <= 2)
		{
			unset($arFields["ACTIVE"]);
		}

		if (is_set($arFields, "ACTIVE") && $arFields["ACTIVE"] != "Y")
		{
			$arFields["ACTIVE"] = "N";
		}

		$strUpdate = $DB->PrepareUpdate("b_group", $arFields);

		if (!is_set($arFields, "TIMESTAMP_X"))
		{
			$strUpdate .= ", TIMESTAMP_X = " . $DB->GetNowFunction();
		}

		$strSql = "UPDATE b_group SET $strUpdate WHERE ID=" . $ID;
		if (is_set($arFields, "SECURITY_POLICY"))
		{
			if (COption::GetOptionString("main", "event_log_group_policy", "N") === "Y")
			{
				//get old security policy
				$aPrevPolicy = [];
				$res = $DB->Query("SELECT SECURITY_POLICY FROM b_group WHERE ID=" . $ID);
				if (($res_arr = $res->Fetch()) && $res_arr["SECURITY_POLICY"] <> '')
				{
					$aPrevPolicy = unserialize($res_arr["SECURITY_POLICY"], ['allowed_classes' => false]);
				}
				//compare with new one
				$aNewPolicy = [];
				if ($arFields["SECURITY_POLICY"] <> '')
				{
					$aNewPolicy = unserialize($arFields["SECURITY_POLICY"], ['allowed_classes' => false]);
				}
				$aDiff = array_diff_assoc($aNewPolicy, $aPrevPolicy);
				if (empty($aDiff))
				{
					$aDiff = array_diff_assoc($aPrevPolicy, $aNewPolicy);
				}
				if (!empty($aDiff))
				{
					CEventLog::Log("SECURITY", "GROUP_POLICY_CHANGED", "main", $ID, print_r($aPrevPolicy, true) . " => " . print_r($aNewPolicy, true));
				}
			}
			$DB->QueryBind($strSql, ["SECURITY_POLICY" => $arFields["SECURITY_POLICY"]]);
		}
		else
		{
			$DB->Query($strSql);
		}

		if (is_set($arFields, "USER_ID") && is_array($arFields["USER_ID"]))
		{
			$log = (COption::GetOptionString("main", "event_log_user_groups", "N") === "Y");
			if ($log)
			{
				//remember users in the group
				$aPrevUsers = [];
				$res = $DB->Query("SELECT USER_ID FROM b_user_group WHERE GROUP_ID=" . $ID . ($ID == "1" ? " AND USER_ID<>1" : ""));
				while ($res_arr = $res->Fetch())
				{
					$aPrevUsers[] = $res_arr["USER_ID"];
				}
			}

			$DB->Query("DELETE FROM b_user_group WHERE GROUP_ID=" . $ID . ($ID == "1" ? " AND USER_ID<>1" : ""));

			$arUsers = $arFields["USER_ID"];
			$arTmp = [];
			foreach ($arUsers as $user)
			{
				if (!is_array($user))
				{
					$user = ["USER_ID" => $user];
				}

				$user_id = intval($user["USER_ID"]);
				if (
					$user_id > 0
					&& !isset($arTmp[$user_id])
					&& ($ID != 1 || $user_id != 1)
				)
				{
					$arInsert = $DB->PrepareInsert("b_user_group", $user);
					$strSql = "
						INSERT INTO b_user_group (
							GROUP_ID, " . $arInsert[0] . "
						) VALUES (
							" . $ID . ", " . $arInsert[1] . "
						)
					";
					$DB->Query($strSql);
					$arTmp[$user_id] = true;
				}
			}
			$aNewUsers = array_keys($arTmp);
			CUser::clearUserGroupCache();

			if ($log)
			{
				foreach ($aPrevUsers as $user_id)
				{
					if (!in_array($user_id, $aNewUsers))
					{
						$UserName = '';
						$rsUser = CUser::GetByID($user_id);
						if ($arUser = $rsUser->GetNext())
						{
							$UserName = ($arUser["NAME"] != "" || $arUser["LAST_NAME"] != "") ? trim($arUser["NAME"] . " " . $arUser["LAST_NAME"]) : $arUser["LOGIN"];
						}
						$res_log = [
							"groups" => "-(" . $ID . ")",
							"user" => $UserName,
						];
						CEventLog::Log("SECURITY", "USER_GROUP_CHANGED", "main", $user_id, serialize($res_log));
					}
				}

				foreach ($aNewUsers as $user_id)
				{
					if (!in_array($user_id, $aPrevUsers))
					{
						$UserName = '';
						$rsUser = CUser::GetByID($user_id);
						if ($arUser = $rsUser->GetNext())
						{
							$UserName = ($arUser["NAME"] != "" || $arUser["LAST_NAME"] != "") ? trim($arUser["NAME"] . " " . $arUser["LAST_NAME"]) : $arUser["LOGIN"];
						}
						$res_log = [
							"groups" => "+(" . $ID . ")",
							"user" => $UserName,
						];
						CEventLog::Log("SECURITY", "USER_GROUP_CHANGED", "main", $user_id, serialize($res_log));
					}
				}
			}
		}

		foreach (GetModuleEvents("main", "OnAfterGroupUpdate", true) as $arEvent)
		{
			ExecuteModuleEventEx($arEvent, [$ID, &$arFields]);
		}

		GroupTable::cleanCache();
		ModuleGroupTable::cleanCache();

		return true;
	}

	public static function Delete($ID)
	{
		/** @global CMain $APPLICATION */
		global $APPLICATION, $DB;

		$ID = intval($ID);
		if ($ID <= 2)
		{
			return false;
		}

		@set_time_limit(600);

		foreach (GetModuleEvents("main", "OnBeforeGroupDelete", true) as $arEvent)
		{
			if (ExecuteModuleEventEx($arEvent, [$ID]) === false)
			{
				$err = GetMessage("MAIN_BEFORE_DEL_ERR1") . ' ' . $arEvent['TO_NAME'];
				if ($ex = $APPLICATION->GetException())
				{
					$err .= ': ' . $ex->GetString();
				}
				$APPLICATION->throwException($err);
				return false;
			}
		}

		foreach (GetModuleEvents("main", "OnGroupDelete", true) as $arEvent)
		{
			ExecuteModuleEventEx($arEvent, [$ID]);
		}

		CMain::DelGroupRight("", [$ID]);

		if (!$DB->Query("DELETE FROM b_user_group WHERE GROUP_ID=" . $ID . " AND GROUP_ID>2", true))
		{
			return false;
		}
		CUser::clearUserGroupCache();

		$res = $DB->Query("DELETE FROM b_group WHERE ID=" . $ID . " AND ID>2", true);

		GroupTable::cleanCache();

		return $res;
	}

	public static function GetGroupUser($ID)
	{
		global $DB;
		$ID = intval($ID);

		if ($ID == 2)
		{
			$strSql = "SELECT U.ID as USER_ID FROM b_user U ";
		}
		else
		{
			$strSql =
				"SELECT UG.USER_ID " .
				"FROM b_user_group UG " .
				"WHERE UG.GROUP_ID = " . $ID . " " .
				"	AND ((UG.DATE_ACTIVE_FROM IS NULL) OR (UG.DATE_ACTIVE_FROM <= " . $DB->CurrentTimeFunction() . ")) " .
				"	AND ((UG.DATE_ACTIVE_TO IS NULL) OR (UG.DATE_ACTIVE_TO >= " . $DB->CurrentTimeFunction() . ")) ";
		}

		$res = $DB->Query($strSql);
		$arr = [];
		while ($r = $res->Fetch())
		{
			$arr[] = $r["USER_ID"];
		}

		return $arr;
	}

	public static function GetGroupUserEx($ID)
	{
		global $DB;
		$ID = intval($ID);

		if ($ID == 2)
		{
			$strSql = "SELECT U.ID as USER_ID, NULL as DATE_ACTIVE_FROM, NULL as DATE_ACTIVE_TO FROM b_user U ";
		}
		else
		{
			$strSql =
				"SELECT UG.USER_ID, " .
				"	" . $DB->DateToCharFunction("UG.DATE_ACTIVE_FROM", "FULL") . " as DATE_ACTIVE_FROM, " .
				"	" . $DB->DateToCharFunction("UG.DATE_ACTIVE_TO", "FULL") . " as DATE_ACTIVE_TO " .
				"FROM b_user_group UG " .
				"WHERE UG.GROUP_ID = " . $ID . " " .
				"	AND ((UG.DATE_ACTIVE_FROM IS NULL) OR (UG.DATE_ACTIVE_FROM <= " . $DB->CurrentTimeFunction() . ")) " .
				"	AND ((UG.DATE_ACTIVE_TO IS NULL) OR (UG.DATE_ACTIVE_TO >= " . $DB->CurrentTimeFunction() . ")) ";
		}
		$res = $DB->Query($strSql);

		return $res;
	}

	public static function GetSubordinateGroups($grId)
	{
		if (!is_array($grId))
		{
			$grId = [$grId];
		}

		Collection::normalizeArrayValuesByInt($grId, false);

		$result = ['2'];
		if (!empty($grId))
		{
			$groups = GroupSubordinateTable::query()
				->setSelect(['ID', 'AR_SUBGROUP_ID'])
				->setCacheTtl(86400)
				->exec()
			;

			$cache = [];
			while ($group = $groups->fetch())
			{
				$cache[$group['ID']] = explode(',', $group['AR_SUBGROUP_ID']);
			}

			foreach ($cache as $groupId => $subordinateGroups)
			{
				if (in_array($groupId, $grId))
				{
					$result = array_merge($result, $subordinateGroups);
				}
			}
		}

		Collection::normalizeArrayValuesByInt($result, false);

		return $result;
	}

	public static function SetSubordinateGroups($grId, $arSubGroups = false)
	{
		$grId = (int)$grId;

		GroupSubordinateTable::delete($grId);

		if (is_array($arSubGroups))
		{
			GroupSubordinateTable::add([
				'ID' => $grId,
				'AR_SUBGROUP_ID' => implode(',', $arSubGroups),
			]);
		}
	}

	public static function GetTasks($ID, $onlyMainTasks = true, $module_id = false)
	{
		global $DB;

		$sql_str = 'SELECT GT.TASK_ID,T.MODULE_ID,GT.EXTERNAL_ID
			FROM b_group_task GT
			INNER JOIN b_task T ON (T.ID=GT.TASK_ID)
			WHERE GT.GROUP_ID=' . intval($ID);
		if ($module_id !== false)
		{
			$sql_str .= ' AND T.MODULE_ID="' . $DB->ForSQL($module_id) . '"';
		}

		$z = $DB->Query($sql_str);
		$arr = [];
		$ex_arr = [];
		while ($r = $z->Fetch())
		{
			if (!$r['EXTERNAL_ID'])
			{
				$arr[$r['MODULE_ID']] = $r['TASK_ID'];
			}
			else
			{
				$ex_arr[] = $r;
			}
		}
		if ($onlyMainTasks)
		{
			return $arr;
		}
		else
		{
			return [$arr, $ex_arr];
		}
	}

	public static function SetTasks($ID, $arr)
	{
		global $DB;
		$ID = intval($ID);

		if (COption::GetOptionString("main", "event_log_module_access", "N") === "Y")
		{
			//get old values
			$arOldTasks = [];
			$rsTask = $DB->Query("SELECT TASK_ID FROM b_group_task WHERE GROUP_ID=" . $ID);
			while ($arTask = $rsTask->Fetch())
			{
				$arOldTasks[] = $arTask["TASK_ID"];
			}
			//compare with new ones
			$aNewTasks = [];
			foreach ($arr as $task_id)
			{
				if ($task_id > 0)
				{
					$aNewTasks[] = $task_id;
				}
			}
			$aDiff = array_diff($arOldTasks, $aNewTasks);
			if (empty($aDiff))
			{
				$aDiff = array_diff($aNewTasks, $arOldTasks);
			}
			if (!empty($aDiff))
			{
				CEventLog::Log("SECURITY", "MODULE_RIGHTS_CHANGED", "main", $ID, "(" . implode(", ", $arOldTasks) . ") => (" . implode(", ", $aNewTasks) . ")");
			}
		}

		$sql_str = "DELETE FROM b_group_task WHERE GROUP_ID=" . $ID .
			" AND (EXTERNAL_ID IS NULL OR EXTERNAL_ID = '')";
		$DB->Query($sql_str);

		$sID = "0";
		if (is_array($arr))
		{
			foreach ($arr as $task_id)
			{
				$sID .= "," . intval($task_id);
			}
		}

		$DB->Query(
			"INSERT INTO b_group_task (GROUP_ID, TASK_ID, EXTERNAL_ID) " .
			"SELECT '" . $ID . "', ID, '' " .
			"FROM b_task " .
			"WHERE ID IN (" . $sID . ") "
		);
	}

	public static function GetTasksForModule($module_id, $onlyMainTasks = true)
	{
		global $DB;

		$sql_str = "SELECT GT.TASK_ID,GT.GROUP_ID,GT.EXTERNAL_ID,T.NAME
			FROM b_group_task GT
			INNER JOIN b_task T ON (T.ID=GT.TASK_ID)
			WHERE T.MODULE_ID='" . $DB->ForSQL($module_id) . "'";

		$z = $DB->Query($sql_str);

		$main_arr = [];
		$ext_arr = [];
		while ($r = $z->Fetch())
		{
			if (!$r['EXTERNAL_ID'])
			{
				$main_arr[$r['GROUP_ID']] = ['ID' => $r['TASK_ID'], 'NAME' => $r['NAME']];
			}
			elseif (!$onlyMainTasks)
			{
				if (!isset($ext_arr[$r['GROUP_ID']]))
				{
					$ext_arr[$r['GROUP_ID']] = [];
				}
				$ext_arr[$r['GROUP_ID']][] = ['ID' => $r['TASK_ID'], 'NAME' => $r['NAME'], 'EXTERNAL_ID' => $r['EXTERNAL_ID']];
			}
		}
		if ($onlyMainTasks)
		{
			return $main_arr;
		}
		else
		{
			return [$main_arr, $ext_arr];
		}
	}

	public static function SetTasksForModule($module_id, $arGroupTask)
	{
		global $DB;

		$module_id = $DB->ForSql($module_id);
		$sql_str = "SELECT T.ID
			FROM b_task T
			WHERE T.MODULE_ID='" . $module_id . "'";
		$r = $DB->Query($sql_str);
		$arIds = [];
		while ($arR = $r->Fetch())
		{
			$arIds[] = $arR['ID'];
		}

		if (COption::GetOptionString("main", "event_log_module_access", "N") === "Y")
		{
			//get old values
			$arOldTasks = [];
			if (!empty($arIds))
			{
				$rsTask = $DB->Query("SELECT GROUP_ID, TASK_ID FROM b_group_task WHERE TASK_ID IN (" . implode(",", $arIds) . ")");
				while ($arTask = $rsTask->Fetch())
				{
					$arOldTasks[$arTask["GROUP_ID"]] = $arTask["TASK_ID"];
				}
			}
			//compare with new ones
			foreach ($arOldTasks as $gr_id => $task_id)
			{
				if ($task_id <> $arGroupTask[$gr_id]['ID'])
				{
					CEventLog::Log("SECURITY", "MODULE_RIGHTS_CHANGED", "main", $gr_id, $module_id . ": (" . $task_id . ") => (" . $arGroupTask[$gr_id]['ID'] . ")");
				}
			}
			foreach ($arGroupTask as $gr_id => $oTask)
			{
				if (intval($oTask['ID']) > 0 && !array_key_exists($gr_id, $arOldTasks))
				{
					CEventLog::Log("SECURITY", "MODULE_RIGHTS_CHANGED", "main", $gr_id, $module_id . ": () => (" . $oTask['ID'] . ")");
				}
			}
		}

		if (!empty($arIds))
		{
			$sql_str = "DELETE FROM b_group_task WHERE TASK_ID IN (" . implode(",", $arIds) . ")";
			$DB->Query($sql_str);
		}

		foreach ($arGroupTask as $gr_id => $oTask)
		{
			if (intval($oTask['ID']) > 0)
			{
				$DB->Query(
					"INSERT INTO b_group_task (GROUP_ID, TASK_ID, EXTERNAL_ID) " .
					"SELECT G.ID, T.ID, '' " .
					"FROM b_group G, b_task T " .
					"WHERE G.ID = " . intval($gr_id) . " AND
					T.ID = " . intval($oTask['ID'])
				);
			}
		}
	}

	public static function GetModulePermission($group_id, $module_id)
	{
		/** @global CMain $APPLICATION */
		global $APPLICATION, $DB;

		// check module permissions mode
		$strSql = "SELECT T.ID, GT.TASK_ID FROM b_task T LEFT JOIN b_group_task GT ON T.ID=GT.TASK_ID AND GT.GROUP_ID=" . intval($group_id) . " WHERE T.MODULE_ID='" . $DB->ForSql($module_id) . "'";
		$dbr_tasks = $DB->Query($strSql);
		if ($ar_task = $dbr_tasks->Fetch())
		{
			do
			{
				if ($ar_task["TASK_ID"] > 0)
				{
					return $ar_task["TASK_ID"];
				}
			}
			while ($ar_task = $dbr_tasks->Fetch());

			return false;
		}

		return $APPLICATION->GetGroupRight($module_id, [$group_id], "N", "N");
	}

	public static function SetModulePermission($group_id, $module_id, $permission)
	{
		/** @global CMain $APPLICATION */
		global $DB, $APPLICATION;

		if (intval($permission) <= 0 && $permission !== false)
		{
			$strSql = "SELECT T.ID FROM b_task T WHERE T.MODULE_ID='" . $DB->ForSql($module_id) . "' AND NAME='" . $DB->ForSql($permission) . "'";
			$db_task = $DB->Query($strSql);
			if ($ar_task = $db_task->Fetch())
			{
				$permission = $ar_task['ID'];
			}
		}

		$permission_letter = '';
		if (intval($permission) > 0 || $permission === false)
		{
			$strSql = "SELECT T.ID FROM b_task T WHERE T.MODULE_ID='" . $DB->ForSql($module_id) . "'";
			$dbr_tasks = $DB->Query($strSql);
			$arIds = [];
			while ($arTask = $dbr_tasks->Fetch())
			{
				$arIds[] = $arTask['ID'];
			}

			if (!empty($arIds))
			{
				$strSql = "DELETE FROM b_group_task WHERE GROUP_ID=" . intval($group_id) . " AND TASK_ID IN (" . implode(",", $arIds) . ")";
				$DB->Query($strSql);
			}

			if (intval($permission) > 0)
			{
				$DB->Query(
					"INSERT INTO b_group_task (GROUP_ID, TASK_ID, EXTERNAL_ID) " .
					"SELECT G.ID, T.ID, '' " .
					"FROM b_group G, b_task T " .
					"WHERE G.ID = " . intval($group_id) . " AND T.ID = " . intval($permission)
				);

				$permission_letter = CTask::GetLetter($permission);
			}
		}
		else
		{
			$permission_letter = $permission;
		}

		if ($permission_letter <> '')
		{
			$APPLICATION->SetGroupRight($module_id, $group_id, $permission_letter);
		}
		else
		{
			$APPLICATION->DelGroupRight($module_id, [$group_id]);
		}
	}

	public static function GetIDByCode($code)
	{
		if (strval(intval($code)) == $code && $code > 0)
		{
			return $code;
		}

		if (strtolower($code) == 'administrators')
		{
			return 1;
		}

		if (strtolower($code) == 'everyone')
		{
			return 2;
		}

		global $DB;

		$strSql = "SELECT G.ID FROM b_group G WHERE G.STRING_ID='" . $DB->ForSQL($code) . "'";
		$db_res = $DB->Query($strSql);

		if ($ar_res = $db_res->Fetch())
		{
			return $ar_res["ID"];
		}

		return false;
	}
}

class CGroup extends CAllGroup
{
}