PageRenderTime 38ms CodeModel.GetById 1ms RepoModel.GetById 1ms app.codeStats 0ms

/Lib/Base/Service/User.php

https://github.com/ilyich/iqyou
PHP | 3618 lines | 2415 code | 501 blank | 702 comment | 469 complexity | 4367631cc482a0023f323c15ae1d1730 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. class Base_Service_User
  3. {
  4. const BAN_COOKIE_NAME = 'f_ubx';
  5. const COOKIE_CHECKSUM = 27;
  6. const USER_GENDER_FEMALE = 0;
  7. const USER_GENDER_MALE = 1;
  8. const USER_GENDER_UNKNOWN = 2;
  9. const SALT_TYPE_COOKIE = 'cookie';
  10. const SALT_TYPE_COOKIE_OLD = 'cookie_old';
  11. const SALT_TYPE_EMAIL = 'email';
  12. const SALT_TYPE_PASSWORD = 'password';
  13. /* Bitwise online/offline statuses */
  14. const SOURCE_OFFLINE = 0;
  15. // Online on site
  16. const SOURCE_FS_ONLINE = 1;
  17. const SOURCE_FS_AWAY = 2;
  18. // Online in messenger
  19. const SOURCE_FSM_ONLINE = 4;
  20. const SOURCE_FSM_AWAY = 8;
  21. // Online from mibile version of FS
  22. const SOURCE_MOBILE_ONLINE = 16;
  23. const SOURCE_MOBILE_AWAY = 32;
  24. // Online from iPhone
  25. const SOURCE_IPHONE_ONLINE = 64;
  26. const SOURCE_IPHONE_AWAY = 128;
  27. // Тут внимание! Добвлять новые сурсы можно но мы ограничены 16 битами (8 из которых уже заняты)
  28. const ONLINE_BIT_SHIFT = 16; // x bit shift
  29. const ONLINE_MAX_ONLINE_TIME = 86400; //24 * 60 * 60 - максимальное время онлайна
  30. const ONLINE_TIMEOUT_UPDATE = 180; // 3 * 60 - тамймаут, когда записывать юзера в up
  31. /*Sources*/
  32. const ONLINE_SOURCE_FS = 1;
  33. const ONLINE_SOURCE_FSM = 2;
  34. const ONLINE_SOURCE_MOBILE = 3;
  35. const ONLINE_SOURCE_IPHONE = 4;
  36. /*statuses*/
  37. const ONLINE_STATUS_ONLINE = 1;
  38. const ONLINE_STATUS_UPDATE = 2;
  39. const ONLINE_STATUS_OFFLINE = 3;
  40. const ACTIVITY_GROUP_ERROR = 5;
  41. const ACTIVITY_GROUP_NEWUSER = 1;
  42. const ACTIVITY_GROUP_ACTIVE = 2;
  43. const ACTIVITY_GROUP_INACTIVE = 3;
  44. const ACTIVITY_GROUP_LOST = 4;
  45. const M_GROUP_AGE_KID = 1;
  46. const M_GROUP_AGE_ADULT = 4;
  47. const M_GROUP_AGE_NO_AGE = 6;
  48. // const M_GROUP_AGE_TEEN = 2;
  49. // const M_GROUP_AGE_YOUNG = 3;
  50. // const M_GROUP_AGE_AGED = 5;
  51. const M_GROUP_MALE = 1;
  52. const M_GROUP_FEMALE = 2;
  53. const F_ACT_GROUP = 1;
  54. const F_AGE_GROUP = 2;
  55. const F_SEX_GROUP = 3;
  56. const ZODIAC_ARIES = 1; // Овен
  57. const ZODIAC_TAURUS = 2; // Телец
  58. const ZODIAC_GEMINI = 3; // Близнецы
  59. const ZODIAC_CANCER = 4; // Рак
  60. const ZODIAC_LEO = 5; // Лев
  61. const ZODIAC_VIRGO = 6; // Дева
  62. const ZODIAC_LIBRA = 7; // Весы
  63. const ZODIAC_SCORPIO = 8; // Скорпион
  64. const ZODIAC_SAGITTARIUS = 9; // Стрелец
  65. const ZODIAC_CAPRICORN = 10; // Козерог
  66. const ZODIAC_AQUARIUS = 11; // Водолей
  67. const ZODIAC_PISCES = 12; // Рыбы
  68. public static $zodiacs = array(
  69. self::ZODIAC_ARIES => array(
  70. 'name' => 'Овен',
  71. 'range' => array(321, 420),
  72. 'suitable_signs' => array(self::ZODIAC_GEMINI, self::ZODIAC_LEO, self::ZODIAC_CAPRICORN, self::ZODIAC_AQUARIUS, self::ZODIAC_PISCES),
  73. ),
  74. self::ZODIAC_TAURUS => array(
  75. 'name' => 'Телец',
  76. 'range' => array(421, 520),
  77. 'suitable_signs' => array(self::ZODIAC_CANCER, self::ZODIAC_LEO, self::ZODIAC_VIRGO, self::ZODIAC_LIBRA, self::ZODIAC_CAPRICORN, self::ZODIAC_PISCES),
  78. ),
  79. self::ZODIAC_GEMINI => array(
  80. 'name' => 'Близнецы',
  81. 'range' => array(521, 621),
  82. 'suitable_signs' => array(self::ZODIAC_ARIES, self::ZODIAC_LEO, self::ZODIAC_LIBRA, self::ZODIAC_AQUARIUS, self::ZODIAC_PISCES),
  83. ),
  84. self::ZODIAC_CANCER => array(
  85. 'name' => 'Рак',
  86. 'range' => array(622, 722),
  87. 'suitable_signs' => array(self::ZODIAC_ARIES, self::ZODIAC_LEO, self::ZODIAC_LIBRA, self::ZODIAC_AQUARIUS, self::ZODIAC_PISCES),
  88. ),
  89. self::ZODIAC_LEO => array(
  90. 'name' => 'Лев',
  91. 'range' => array(723, 823),
  92. 'suitable_signs' => array(self::ZODIAC_ARIES, self::ZODIAC_TAURUS, self::ZODIAC_GEMINI, self::ZODIAC_CANCER, self::ZODIAC_LEO, self::ZODIAC_LIBRA, self::ZODIAC_SAGITTARIUS),
  93. ),
  94. self::ZODIAC_VIRGO => array(
  95. 'name' => 'Дева',
  96. 'range' => array(824, 923),
  97. 'suitable_signs' => array(self::ZODIAC_TAURUS, self::ZODIAC_CANCER, self::ZODIAC_VIRGO, self::ZODIAC_SCORPIO, self::ZODIAC_CAPRICORN, self::ZODIAC_AQUARIUS),
  98. ),
  99. self::ZODIAC_LIBRA => array(
  100. 'name' => 'Весы',
  101. 'range' => array(924, 1022),
  102. 'suitable_signs' => array(self::ZODIAC_TAURUS, self::ZODIAC_GEMINI, self::ZODIAC_LEO, self::ZODIAC_LIBRA, self::ZODIAC_SAGITTARIUS, self::ZODIAC_AQUARIUS),
  103. ),
  104. self::ZODIAC_SCORPIO => array(
  105. 'name' => 'Скорпион',
  106. 'range' => array(1023, 1122),
  107. 'suitable_signs' => array(self::ZODIAC_CANCER, self::ZODIAC_VIRGO, self::ZODIAC_CAPRICORN, self::ZODIAC_PISCES),
  108. ),
  109. self::ZODIAC_SAGITTARIUS => array(
  110. 'name' => 'Стрелец',
  111. 'range' => array(1123, 1221),
  112. 'suitable_signs' => array(self::ZODIAC_LEO, self::ZODIAC_LIBRA, self::ZODIAC_AQUARIUS),
  113. ),
  114. self::ZODIAC_CAPRICORN => array(
  115. 'name' => 'Козерог',
  116. 'range' => array(1222, 120),
  117. 'suitable_signs' => array(self::ZODIAC_ARIES, self::ZODIAC_TAURUS, self::ZODIAC_VIRGO, self::ZODIAC_SCORPIO, self::ZODIAC_CAPRICORN, self::ZODIAC_PISCES),
  118. ),
  119. self::ZODIAC_AQUARIUS => array(
  120. 'name' => 'Водолей',
  121. 'range' => array(121, 219),
  122. 'suitable_signs' => array(self::ZODIAC_ARIES, self::ZODIAC_GEMINI, self::ZODIAC_VIRGO, self::ZODIAC_LIBRA, self::ZODIAC_SAGITTARIUS, self::ZODIAC_AQUARIUS),
  123. ),
  124. self::ZODIAC_PISCES => array(
  125. 'name' => 'Рыбы',
  126. 'range' => array(220, 320),
  127. 'suitable_signs' => array(self::ZODIAC_ARIES, self::ZODIAC_TAURUS, self::ZODIAC_GEMINI, self::ZODIAC_CANCER, self::ZODIAC_SCORPIO, self::ZODIAC_CAPRICORN),
  128. ),
  129. );
  130. /* Time outs by source */
  131. private static $onlineAwayTimeout = array(
  132. // online type => timeout in seconds
  133. self::SOURCE_FS_ONLINE => 900, // 60 * 15
  134. self::SOURCE_FSM_ONLINE => 900, // 60 * 15
  135. self::SOURCE_MOBILE_ONLINE => 900, // 60 * 15
  136. self::SOURCE_IPHONE_ONLINE => 900, // 60 * 15
  137. );
  138. private static $_systemUsersIds = array(
  139. 1, // Фотострана
  140. 2, // Служба поддержки
  141. 3, // Служба модерации
  142. 57267704, // Служба Поддержки ФотоЧата
  143. 63760510, // Тематические новости в ньюсфиде
  144. 63760585, // Тематические новости в ньюсфиде
  145. 63760651, // Тематические новости в ньюсфиде
  146. 63760698, // Тематические новости в ньюсфиде
  147. 63760742, // Тематические новости в ньюсфиде
  148. 63760791, // Тематические новости в ньюсфиде
  149. 63760836, // Тематические новости в ньюсфиде
  150. 63760932, // Тематические новости в ньюсфиде
  151. 65396826, // Социальные таргетинговые объявления
  152. 70388414, // Бот Новости Фотостраны
  153. );
  154. private static function getSalt($type)
  155. {
  156. $saltConfig = Base_Application::getInstance()->config['passwd']['salt'];
  157. return !empty($saltConfig[$type]) ? $saltConfig[$type] : '';
  158. }
  159. public static function getUserProfileUrl($user, $native = false)
  160. {
  161. if ($native) {
  162. $project = $user->getNativeProject();
  163. } else {
  164. $project = Base_Project_Manager::getProject();
  165. }
  166. $domain = $project->getDomain();
  167. if (Base_Service_Common::isStage(false)) {
  168. $domain = 'stage.' . $domain;
  169. }
  170. $isRambler = Base_Project_Manager::getProject()->isRamblerWL() || $project->isRamblerWl();
  171. // костыль для letitbit и pet.rambler.ru
  172. if (!Base_Project_Manager::isOurDomainId($project->getDomainModel()->getId()) ||
  173. $isRambler) {
  174. return 'http://' . $domain . '/user/' . $user['user_id'] . '/';
  175. }
  176. if (!isset($user['user_id']) || !$user['user_id']) {
  177. return 'http://' . $domain . '/user/deleteduser/';
  178. }
  179. if (PetApp_Base_Service_Project::isPetAppProject($project->getType())) {
  180. return $user['user_pet_id'] ? 'http://' . $domain . '/pet/' . $user['user_pet_id'] . '/' :
  181. 'http://' . $domain . '/user/'. $user['user_id'] .'/';
  182. }
  183. if (isset($user['user_pagename']) && $user['user_pagename']) {
  184. if (Base_Project_Fotostrana::fotostrana2Enabled()) {
  185. // для определения ссылок на пользователей в фс 2.0 добавляем /u/. для аякс-переходов
  186. // @todo найти нормальное решение, не требующее изменения урлов.
  187. return 'http://' . $domain . '/u/' . $user['user_pagename'] . '/';
  188. }
  189. return 'http://' . $domain . '/' . $user['user_pagename'] . '/';
  190. } else {
  191. return 'http://' . $domain . '/user/' . $user['user_id'] . '/';
  192. }
  193. }
  194. public static function getInterestPeople($user)
  195. {
  196. $userId = $user->getId();
  197. $cachedId = Base_Service_Memcache::get(Base_Dao_User::MC_USER_INTERESTPEOPLE.$userId, __METHOD__);
  198. if($cachedId!==false) {
  199. $result = Base_Dao_User::getUsersByIds($cachedId);
  200. } else {
  201. if(!($user instanceof Base_Model_User) || !$user) {
  202. return array();
  203. }
  204. $periods = array(Base_Service_Interest_Tracker::USERS_PERIOD_DAY, //0
  205. Base_Service_Interest_Tracker::USERS_PERIOD_WEEK, //1
  206. Base_Service_Interest_Tracker::USERS_PERIOD_MONTH); //2
  207. $result = array();
  208. $userFriendsIds = Friends_Service_New::getFriends($userId, 50, 0, true);
  209. $userFavIds = Usercontact_Dao_Favorite::getRight($userId, 10);
  210. $t = Base_Dao_User::getUsersByIds(array_unique($userFavIds[2]));
  211. $userFavs = array();
  212. foreach($t as $u){
  213. if ($u->isPetAppUser()) {
  214. Base_Service_Counter_Social::getInstance()->increment($u, 'test_stat_pet_only_in_favorite_left', 1);
  215. Base_Service_Counter_Social::getInstance()->increment($user, 'test_stat_pet_only_in_favorite_right', 1);
  216. }
  217. if($u->isOnline()) {
  218. $userFavs[] = $u;
  219. }
  220. }
  221. if(count($userFavs)==0 || count($userFriendsIds)==0) {
  222. $interests = array(15,10,5);
  223. } else {
  224. $interests = array(15,5,5);
  225. }
  226. $userInterestIds = array();
  227. foreach($periods as $p) {
  228. $t = Base_Service_Interest_Tracker::getUserContactsPopular($userId, $p, $interests[$p]);
  229. if(is_array($t)) {
  230. foreach($t as $u=>$c) {
  231. if($c>2 && $u!=0) {
  232. $userInterestIds[] = $u;
  233. }
  234. }
  235. }
  236. }
  237. $userInterest = Base_Dao_User::getUsersByIds(array_unique($userInterestIds));
  238. $userFriendsIds = array_diff($userFriendsIds, $userInterestIds, $userFavIds);
  239. $userFriends = Base_Dao_User::getUsersByIds($userFriendsIds);
  240. $users = array_merge($userInterest, $userFavs, $userFriends);
  241. //$usersIds = array_merge($userInterestIds, $userFavIds[2], $userFriendsIds);
  242. $resIds = array();
  243. foreach($users as $u) {
  244. if($u->hasMainPhoto() && $u->getId()>1 && !in_array($u, $result)) {
  245. $result[] = $u;
  246. $resIds[] = $u->getId();
  247. }
  248. if(count($result)>=20) break;
  249. }
  250. Base_Service_Memcache::set(Base_Dao_User::MC_USER_INTERESTPEOPLE . $userId, $resIds);
  251. }
  252. return $result;
  253. }
  254. public static function generateUserEmailHash($userOrEmail)
  255. {
  256. $salt = self::getSalt('email');
  257. if ($userOrEmail instanceof Base_Model_User) {
  258. return md5($userOrEmail->getEmail() . $salt);
  259. } else {
  260. return md5($userOrEmail . $salt);
  261. }
  262. }
  263. /**
  264. * convert user birthday to age
  265. *
  266. * @param $userBirthday
  267. * @param bool $asString
  268. * @param bool $baseTime
  269. *
  270. * @return bool|int|string
  271. */
  272. public static function getUserAge($userBirthday, $asString = false, $baseTime = false)
  273. {
  274. if (!$userBirthday || $userBirthday == '0000-00-00') {
  275. return false;
  276. }
  277. $baseTime = ($baseTime === false) ? TIME : $baseTime;
  278. list($year, $month, $day) = explode('-', $userBirthday);
  279. $yearDiff = date('Y', ($baseTime)) - $year;
  280. $monthDiff = date('m', ($baseTime)) - $month;
  281. $dayDiff = date('d', ($baseTime)) - $day;
  282. if ($monthDiff < 0) {
  283. $yearDiff--;
  284. } elseif (($monthDiff == 0) && ($dayDiff < 0)) {
  285. $yearDiff--;
  286. }
  287. return $asString ? _f('{plural|%d год|%d года|%d лет|%d лет}', $yearDiff) : $yearDiff;
  288. }
  289. /**
  290. * Получть хеш пароля из пароля. Он хранится в бд
  291. */
  292. public static function getPasswordHash($password)
  293. {
  294. return md5($password . self::getSalt(self::SALT_TYPE_PASSWORD));
  295. }
  296. public static function getPasswordCrypt($password)
  297. {
  298. $secretKey = Base_Application::getInstance()->config['passwd']['user']['crypt_key'];
  299. return Base_Service_Crypt::crypt($password, true, $secretKey);
  300. }
  301. public static function getPasswordDecrypt($password)
  302. {
  303. if(Base_Mailer_Service_Config::isPreviewMode()) {
  304. return 'password_hidden';
  305. }
  306. $secretKey = Base_Application::getInstance()->config['passwd']['user']['crypt_key'];
  307. return Base_Service_Crypt::decrypt($password, true, $secretKey);
  308. }
  309. /**
  310. * Получить хеш для авторизации
  311. *
  312. * @param $userId ID пользователя
  313. * @param $userEmail e-mail пользователя
  314. * @param $userPasswordHash хеш пароля пользователя
  315. * @param bool $includeASCode включать ли в хеш провайдера
  316. * @param bool $oldSalt использовать ли старую соль (для переходного периода)
  317. *
  318. * @return string
  319. */
  320. public static function getAuthHash($userId, $userEmail, $userPasswordHash, $includeASCode = false, $oldSalt = false)
  321. {
  322. $salt = self::getSalt($oldSalt ? self::SALT_TYPE_COOKIE_OLD : self::SALT_TYPE_COOKIE);
  323. $provider = '';
  324. if ($includeASCode) {
  325. $ip = Base_Service_Common::getRealIp();
  326. $location = Base_Dao_Geo::getLocationByIp($ip);
  327. if (!empty($location['provider'])) {
  328. $provider = crc32(Utf::trim($location['provider']));
  329. }
  330. }
  331. return md5($userEmail . $userPasswordHash . $provider . $salt) . $userId % self::COOKIE_CHECKSUM;
  332. }
  333. /**
  334. * @deprecated use Base_Service_User::getAuthHash
  335. */
  336. public static function getUserPasswordSalt($userId, $userEmail, $userPassword, $newEmail = '')
  337. {
  338. return md5($userEmail . $userPassword . $newEmail . self::getSalt(self::SALT_TYPE_COOKIE_OLD)) . $userId % self::COOKIE_CHECKSUM;
  339. }
  340. /**
  341. * Упрощенная авторизация. Используется там, где не нужна повышенная безопасность
  342. *
  343. * @param $userId
  344. * @param $cookieSecretWord
  345. *
  346. * @return bool
  347. */
  348. public static function checkPasswordHash($userId, $cookieSecretWord)
  349. {
  350. $uidHash = $userId % Base_Service_User::COOKIE_CHECKSUM;
  351. return ($uidHash == Utf::substr($cookieSecretWord, -Utf::strlen($uidHash), Utf::strlen($uidHash)));
  352. }
  353. /**
  354. * Авторизует пользователя на сайте
  355. *
  356. * @param Base_Model_User $user
  357. *
  358. * @return bool
  359. */
  360. public static function logIn($user)
  361. {
  362. if (!$user) {
  363. return false;
  364. }
  365. Service_Base::setCookie(
  366. 'uid',
  367. $user->getId(),
  368. 100, '/', true, 2
  369. );
  370. Service_Base::setCookie(
  371. 'hw',
  372. Base_Service_User::getAuthHash($user->getId(), $user->getEmail(), $user->getPasswordHash(), false),
  373. 100, '/', true, 2
  374. );
  375. Antispam_Service_Token::setToken();
  376. $trace = Base_Service_Log::getTrace(3);
  377. Pet_Dao_Trace::log($user->getId(), 'lgn: ' . implode(',', $trace));
  378. return true;
  379. }
  380. public static function logout()
  381. {
  382. Service_Base::setCookie('hw', '', -1, '/', true, 2);
  383. Service_Base::setCookie('uid', '', -1, '/', true, 2);
  384. }
  385. /**
  386. * @param Base_Model_User $user
  387. *
  388. * @return boolean
  389. */
  390. public static function checkUserNeedsActivation($user)
  391. {
  392. if (!$user) {
  393. return false;
  394. }
  395. if (!$user->getRefId() && !$user->isEmailApproved() && $user->isFsUser()) {
  396. return true;
  397. }
  398. return false;
  399. }
  400. /**
  401. * Check User for ban
  402. *
  403. * @param Base_Model_User $user
  404. *
  405. * @return bool
  406. */
  407. public static function checkUserBanCookie($user)
  408. {
  409. return;
  410. // if (!$user->isBanned() && isset($_COOKIE[self::BAN_COOKIE_NAME])) {
  411. // Service_Base::setCookie(self::BAN_COOKIE_NAME, '');
  412. // } elseif ($user->isBanned() && $user->isBanned() != Db_Moders::BAN_REASON_NEED_APPROVE_EMAIL && !isset($_COOKIE[self::BAN_COOKIE_NAME])) {
  413. // Service_Base::setCookie(self::BAN_COOKIE_NAME, 1, 1);
  414. // }
  415. }
  416. /**
  417. * Проверям, может ли юзер стать претендентом в эксперты
  418. *
  419. * @param Base_Model_User $user пользователь
  420. * @param $type (expert, moder, petgid, guide)
  421. * @param $reason Возвращает причину отказа (в виде кода)
  422. *
  423. * @return bool
  424. */
  425. public static function canBeExpert($user, $type, &$reason = '')
  426. {
  427. $reason = '';
  428. assert(in_array($type, array('expert', 'moder', 'petgid', 'guide')));
  429. if (!($user instanceof Base_Model_User)) {
  430. trigger_error("Please pass Base_Model_User, not an array", E_USER_WARNING);
  431. $user = Base_Dao_User::getUserById($user['user_id']);
  432. }
  433. /*if(!$user['user_identity_approved']) {
  434. if($type != 'petgid') {
  435. $reason = 'need_identity_approved';
  436. return false;
  437. }
  438. }*/
  439. if ($type == 'petgid' && $user->hasProfession()) {
  440. $reason = 'has_profession';
  441. return false;
  442. }
  443. // Требуем наличия подтвержденного телефона
  444. if ($user->getUserClass() !== Db_User::USER_CLASS_CITIZEN) {
  445. $reason = 'must_be_citizen';
  446. return false;
  447. }
  448. if(self::getUserAge($user['user_birthday'])<18 && $type!='petgid'){
  449. $reason = 'must_be_18';
  450. return false;
  451. }
  452. if ($type=='petgid') {
  453. if (self::getUserAge($user['user_birthday'])<16) {
  454. $reason = 'must_be_16';
  455. return false;
  456. }
  457. $pet = $user->getPet();
  458. if (!$pet) {
  459. $reason = 'no_pet';
  460. return false;
  461. }
  462. if ($pet->getDaltLevel() < 10) {
  463. $reason = 'pet_dalt_10';
  464. return false;
  465. }
  466. }
  467. $dbModers = new Admin_Dao_User();
  468. $bans = $dbModers->getUsersViolations(array($user['user_id']),'adminban',true);
  469. if(!empty($bans)) {
  470. $reason = 'has_bans';
  471. return false;
  472. }
  473. if(Support_Dao_Base::getExpertRequestByUserId($user['user_id'],$type)) {
  474. $reason = 'already_applied';
  475. return false;
  476. }
  477. return true;
  478. }
  479. /**
  480. * Возвращает HTML-текст с сообщением об ошибке для данного кода ошибки.
  481. */
  482. public static function getErrorText($errorCode)
  483. {
  484. $simpleCodes = array(
  485. 'need_identity_approved' => _g('Чтобы получить профессию, надо сначала подтвердить свою личность.'),
  486. 'need_more_reputation' => _g('У тебя пока недостаточно репутации, чтобы получить профессию.'), // deprecated
  487. 'must_be_18' => _g('Тебе должно быть не меньше 18 лет!'),
  488. 'must_be_16' => _g('Тебе должно быть не меньше 16 лет!'),
  489. 'has_profession' => _g('У тебя уже есть профессия на сайте!'),
  490. 'no_pet' => _g('У тебя нет питомца.'),
  491. 'pet_dalt_10' => _g('У твоего питомца недостаточно способностей.'),
  492. 'has_bans' => _g('Ты был наказан за нарушения.'),
  493. 'already_applied' => _g('Ты уже подавал заявку на получение профессии. Дождись ее рассмотрения.'),
  494. 'must_be_citizen' => _g('Тебе необходимо подтвердить свой номер телефона.')
  495. );
  496. if ($errorCode == '') {
  497. // No error
  498. return '';
  499. }
  500. if (isset($simpleCodes[$errorCode])) {
  501. return $simpleCodes[$errorCode];
  502. }
  503. throw new Base_Exception("Invalid error code: '$errorCode'");
  504. }
  505. public static function canBeWriter($user)
  506. {
  507. return false; // профессия выключена для всех
  508. if (Event_Service_Editor::userInBan($user['user_id'])) {
  509. return false;
  510. }
  511. return
  512. !$user['user_is_volunteer']
  513. && $user['user_class'] == Db_User::USER_CLASS_CITIZEN;
  514. }
  515. public static function getDeletedUserInto(&$user)
  516. {
  517. $user['is_deleted'] = true;
  518. $user['user_name'] = _g('Удаленный житель');
  519. $user['user_pagename'] = null;
  520. $user['user_phone'] = null;
  521. $user['user_phone_approved'] = '0';
  522. $user['user_sex'] = 'm';
  523. $user['user_inserted'] = '2009-01-01 00:00:00';
  524. $user['user_updated'] = '2009-01-01 00:00:00';
  525. $user['user_email'] = 'deleted@fotostrana.ru';
  526. $user['password_hash'] = '';
  527. $user['user_email_approved'] = null;
  528. $user['user_ip'] = null;
  529. $user['user_id_from'] = null;
  530. $user['user_about'] = null;
  531. $user['user_city'] = null;
  532. $user['user_last_mail'] = '2009-01-01 00:00:00';
  533. $user['user_is_hidden'] = null;
  534. $user['user_is_volunteer'] = null;
  535. $user['user_source'] = null;
  536. $user['user_source_result'] = null;
  537. $user['user_not_activated'] = null;
  538. $user['user_country_id'] = 0;
  539. $user['user_city_id'] = 0;
  540. $user['user_region_id'] = 0;
  541. $user['user_birthday'] = null;
  542. $user['user_ref_id'] = null;
  543. $user['user_ref_pa'] = '0';
  544. $user['user_deviz'] = null;
  545. $user['user_why'] = null;
  546. $user['user_galleries'] = null;
  547. $user['user_status'] = null;
  548. $user['user_bans'] = null;
  549. $user['user_warn'] = null;
  550. $user['user_cash'] = '0.00';
  551. $user['user_hold'] = '0.00';
  552. $user['user_class'] = '0';
  553. $user['terms_agree'] = '1';
  554. $user['time_in'] = 1;
  555. $user['time_out'] = 1;
  556. $user['user_pet_id'] = 0;
  557. $user['user_photo_id'] = -1;
  558. return $user;
  559. }
  560. public static function getDeletedUser($userId)
  561. {
  562. $user = array();
  563. $user['user_id'] = $userId;
  564. self::getDeletedUserInto($user);
  565. return new Base_Model_User($user);
  566. }
  567. public static function validateEmail($email)
  568. {
  569. return Utf::preg_match('/^[a-zA-Z0-9!#$%&\'*+\\/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&\'*+\\/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$/', $email);
  570. }
  571. /**
  572. * исправить ошибки в написании пароля:
  573. * убирает пробелы на краях и заменяет русские символы транслитом
  574. * (если забыл сменить язык)
  575. */
  576. public static function correctPassword($pswd)
  577. {
  578. $pswd = trim($pswd);
  579. // strtr получается в 4е раза быстрее чем str_replace
  580. return strtr($pswd, 'ёйцукенгшщзфывапролдячсмитьЁЙЦУКЕНГШЩЗФЫВАПРОЛДЯЧСМИТЬ', '`qwertyuiopasdfghjklzxcvbnm~QWERTYUIOPASDFGHJKLZXCVBNM');
  581. }
  582. /**
  583. * Валидатор для имени пользователя
  584. *
  585. * @param $name
  586. * @param string $error
  587. * @return bool
  588. */
  589. public static function validateUsername($name, &$error = '', &$debug = '')
  590. {
  591. // если нужно заблочить смешанное имя (кириллица + латиница),
  592. // можно добавлять в любой массив
  593. // разделение массивов сделано для увеличения производительности в
  594. // ф-циях preg_match
  595. if (Base_Service_Common::isOurIp()) {
  596. return true;
  597. }
  598. $name = str_replace(chr(173), '', $name);
  599. // stop words in Antifraud_Service_Badwords
  600. $cyrStopWords = Antifraud_Service_Badwords::getWords(Antifraud_Service_Badwords::CYR_NAMES);
  601. $latStopWords = Antifraud_Service_Badwords::getWords(Antifraud_Service_Badwords::LAT_NAMES);
  602. array_walk($cyrStopWords, function (&$value, $key) {
  603. $value = preg_quote($value);
  604. });
  605. array_walk($latStopWords, function (&$value, $key) {
  606. $value = preg_quote($value);
  607. });
  608. $error = '';
  609. $toCyrName = strtr($name, 'aA6cCeETmHoOpPkKxXBMbryYUunl3gt', 'аАбсСеЕТтНоОрРкКхХВМьгуУИип1Здт'); // переводим имя в кириллицу
  610. $toLatName = strtr($name, 'аАбсСеЕТтНоОрРкКхХВМьгуУИип1Здт', 'aA6cCeETmHoOpPkKxXBMbryYUunl3gt'); // переводим имя в латиницу
  611. $name = Utf::trim(mb_strtolower($name));
  612. $toCyrName = Utf::trim(mb_strtolower($toCyrName));
  613. $toLatName = Utf::trim(mb_strtolower($toLatName));
  614. // первый этап проверки, если юзер меняет на что-то вроде админ или admin (т.е. не использует замены символов на похожие)
  615. if (!$error && Utf::preg_match('/(' . implode('|', array_merge($cyrStopWords, $latStopWords)) . ')/', $name)) {
  616. $error = 'stop_word';
  617. $debug = 'lat_or_cyr_stop_word';
  618. }
  619. // второй этап, если юзер ввел имя, используя замену кириллических символов на похожие латинские
  620. if (!$error && Utf::preg_match('/(' . implode('|', $cyrStopWords) . ')/', $toCyrName)) {
  621. $error = 'stop_word';
  622. $debug = 'cyr_to_lat_change';
  623. }
  624. // третий этап, если юзер ввел имя, используя замену латинских символов на похожие кириллические
  625. if (!$error && Utf::preg_match('/(' . implode('|', $latStopWords) . ')/', $toLatName)) {
  626. $error = 'stop_word';
  627. $debug = 'lat_to_cyr_change';
  628. }
  629. // normalize urls
  630. $toLatName = str_replace(' ', '', $toLatName);
  631. $toLatName = preg_replace('#[.]+#', '.', $toLatName);
  632. if (!$error && Utf::preg_match(Base_Util_String::getUrlPreg(), $toLatName) > 0) {
  633. $error = 'stop_word';
  634. $debug = 'url_preg';
  635. }
  636. $statsClient = new Base_Service_Counter_Main();
  637. $defaultUser = Base_Dao_User::getUserById(1);
  638. $statsClient->increment($defaultUser, 'signup_time_name_check', 1);
  639. if ($error) {
  640. $statsClient->increment($defaultUser, 'signup_time_name_block', 1);
  641. return false;
  642. }
  643. return true;
  644. }
  645. public static function getRemainingDeleteRequestTime($userId)
  646. {
  647. $requestTime = Base_Dao_User::getDeleteRequestTime($userId);
  648. if (!$requestTime) {
  649. return false;
  650. }
  651. $requestTimeEnd = $requestTime + 60*60*24;
  652. $timeRemaining = $requestTimeEnd - time();
  653. // Пользователь за 5 дней не подтвердил желание удалиться, удаляем заявку
  654. $user = Base_Dao_User::getUserById($userId);
  655. if($timeRemaining < -60*60*24*5 && $user['user_is_hidden'] != Db_Moders::DELAYED_DELETED){
  656. Base_Dao_User::cancelDeleteRequest($userId);
  657. return false;
  658. }
  659. if ($timeRemaining < 0) {
  660. return -1;
  661. }
  662. $hours = (int)($timeRemaining / (60*60));
  663. $minutes = (int)($timeRemaining % (60*60) /60);
  664. $hoursStr = _f('{plural|%d час|%d часа|%d часов}', $hours);
  665. $minutesStr = _f('{plural|%d минуту|%d минуты|%d минут}', $minutes);
  666. return $hoursStr . ' ' . $minutesStr;
  667. }
  668. public static function getRemainingDeleteTime($userId)
  669. {
  670. $requestTime = Base_Dao_User::getDeleteRequestTime($userId);
  671. if (!$requestTime) {
  672. return false;
  673. }
  674. $deleteTime = $requestTime + 60*60*24*30;
  675. $timeRemaining = $deleteTime - time();
  676. if ($timeRemaining < 0) {
  677. return _g('еще одного дня');
  678. }
  679. $days = (int)($timeRemaining / (60*60*24));
  680. if ($days == 0) {
  681. $days = 1;
  682. }
  683. return _f('{plural|%d дня|%d дней|%d дней}', $days);
  684. }
  685. public static function getDeleteConfirmHash($user, $oldSalt = false)
  686. {
  687. $salt = self::getSalt($oldSalt ? self::SALT_TYPE_COOKIE_OLD : self::SALT_TYPE_COOKIE);
  688. return md5($user['user_inserted'] . $salt);
  689. }
  690. public static function processDelayedDelete()
  691. {
  692. $db = Base_Context::getInstance()->getDbConnection();
  693. $dbUser = new Db_User();
  694. $query = '
  695. SELECT user_id, reason
  696. FROM
  697. user_delete_request
  698. WHERE
  699. DATE_SUB(NOW(), INTERVAL 30 DAY) > request_time
  700. LIMIT 200';
  701. $usersData = $db->fetchAssoc($query, __METHOD__);
  702. if ($usersData) {
  703. $userIds = Base_Util_Array::extract($usersData, 'user_id');
  704. $usersToDelete = Base_Dao_User::getUsersByIds($userIds);
  705. foreach ($usersToDelete as $userToDelete) {
  706. if ($userToDelete->isHidden() != Db_Moders::DELAYED_DELETED) {
  707. continue;
  708. }
  709. $reason = isset($usersData[$userToDelete->getId()]) ? $usersData[$userToDelete->getId()]['reason'] : '';
  710. $dbUser->deleteUser($userToDelete, $reason);
  711. $userToDelete->getNativeProject()->getStatisticClient()->increment($userToDelete, 'delete_user', 1);
  712. // @analytics stats
  713. $analytics = new Base_Service_Counter_Analytics();
  714. $analytics->increment($userToDelete, 'delete_user', 1);
  715. sleep(1);
  716. }
  717. $db->delete('user_delete_request', $db->qq('user_id IN ('.join(',',$userIds).')'), __METHOD__);
  718. }
  719. // предупреждаем тех, кому осталось 3 дня до удаления
  720. $query = '
  721. SELECT user_id
  722. FROM
  723. user_delete_request
  724. WHERE
  725. DATE_SUB(NOW(), INTERVAL 60*24*27-10 MINUTE) > request_time
  726. AND DATE_SUB(NOW(), INTERVAL 60*24*27 MINUTE) <= request_time';
  727. $usersToNotifyIds = $db->fetchCol($query, __METHOD__);
  728. if ($usersToNotifyIds) {
  729. $usersToNotify = Base_Dao_User::getUsersByIds($usersToNotifyIds);
  730. foreach ($usersToNotify as $userToNotify) {
  731. if ($userToNotify->isHidden() != Db_Moders::DELAYED_DELETED || !$userToNotify->isEmailSet() ) {
  732. continue;
  733. }
  734. /* @var $userToNotify Base_Model_User */
  735. Base_Mailer_Base::addImmediateMail($userToNotify, Base_Mailer_NewTypes::TYPE_DELETE_NOTIFY, __METHOD__, array('subject' => _f('Удаление с {string}', $userToNotify->getNativeProject()->getTitle(2))));
  736. }
  737. }
  738. }
  739. /**
  740. * @param Base_Model_User $user
  741. * @param string $wLetter
  742. * @param string $mLetter
  743. *
  744. * @return string
  745. */
  746. public static function getSexLetter($user, $wLetter = 'а', $mLetter = '')
  747. {
  748. return $user->isFemale() ? $wLetter : $mLetter;
  749. }
  750. /**
  751. * Проверяет, кто из списка айдишников онлайн
  752. *
  753. * @param array $ids массив айдишников пользователй для проверки
  754. * @param bool $keepOffline
  755. *
  756. * @return array|bool массив с ключами $ids и значениями 1|0 (онлайн/офлайн)
  757. */
  758. public static function getAutoOnlineUsers(array $ids, $keepOffline = true)
  759. {
  760. if(!is_array($ids)){
  761. return false;
  762. }
  763. $db = Base_Context::getInstance()->getDbConnection();
  764. $select = $db->select()->from('auto_online','user_id')->where('user_id IN (?)', $ids);
  765. $result = $db->fetchCol($select, __METHOD__);
  766. $online = $keepOffline ? array_fill_keys($ids, 0) : array();
  767. foreach($result as $id){
  768. $online[$id]=1;
  769. }
  770. return $online;
  771. }
  772. /**
  773. * По прошествию 4-х дней переносятся все пользователи кроме:
  774. * 1) забанен и не имеет фин активности
  775. * 2) не имеет подтвержденного е-мела
  776. * в таблицу user. Остальные пользователи удаляются.
  777. */
  778. public static function moveNewUsers()
  779. {
  780. $date4Day = date('Y-m-d H:i:s', time() - 86400 * 4);
  781. /**
  782. * Если мы в окружении для тестирования, необходимо
  783. * скопировать пользователей которые были добавлены через modelFactory
  784. */
  785. if (defined('TESTING') && constant('TESTING') === true) {
  786. // $whereNotActive = "user_email = '".Db_User::DEFAULT_EMAIL."'";
  787. // $whereActive = "user_email != '".Db_User::DEFAULT_EMAIL."'";
  788. $whereActive = "1=1";
  789. } else {
  790. // $whereNotActive = "user_inserted < '$date4Day' AND user_email = '".Db_User::DEFAULT_EMAIL."'";
  791. // $whereActive = "user_inserted < '$date4Day' AND user_email != '".Db_User::DEFAULT_EMAIL."'";
  792. $whereActive = "user_inserted < '$date4Day'";
  793. }
  794. /* ---------------------------------------------------------------------------------------------------------- */
  795. $db = Base_Context::getInstance()->getDbConnection();
  796. /* $countNew = (int) $db->fetchOne("SELECT COUNT(*) FROM `user_new` WHERE $whereActive", __METHOD__);
  797. $countBefore = (int) $db->fetchOne("SELECT COUNT(*) FROM `user`", __METHOD__);
  798. $query = "INSERT INTO `user` (SELECT * FROM `user_new` WHERE $whereActive) ON DUPLICATE KEY UPDATE user.user_email = user_new.user_email, user.user_is_hidden = user_new.user_is_hidden, user.user_not_activated = user_new.user_not_activated";
  799. $db->writequery('user', $query, __METHOD__);
  800. $countAfter = $db->fetchOneMaster("SELECT COUNT(*) FROM `user`", __METHOD__);
  801. if ($countAfter >= $countBefore + $countNew) {
  802. $queryDelete = "DELETE FROM `user_new` WHERE $whereActive";
  803. Driver_Db::writequery('user_new', $queryDelete, __METHOD__);
  804. }*/
  805. // получим активных пользователей, для переноса
  806. $activeIds = $db->fetchCol("SELECT `user_id` FROM `user_new` WHERE $whereActive", __METHOD__);
  807. $activeTotal = count($activeIds);
  808. foreach (array_chunk($activeIds, 5000) as $idsPack) {
  809. $activeIdsImploded = implode(', ', $idsPack);
  810. // перенесем их в `users` табличку
  811. $query = "
  812. INSERT INTO `user` (SELECT * FROM `user_new` WHERE `user_id` IN($activeIdsImploded))
  813. ON DUPLICATE KEY UPDATE
  814. `user`.`user_email` = `user_new`.`user_email`,
  815. `user`.`user_is_hidden` = `user_new`.`user_is_hidden`,
  816. `user`.`user_not_activated` = `user_new`.`user_not_activated`
  817. ";
  818. $db->writequery('user', $query, __METHOD__);
  819. // уберем перенесенных из `user_new`
  820. $db->writequery('user_new', "DELETE FROM `user_new` WHERE `user_id` IN($activeIdsImploded)", __METHOD__);
  821. }
  822. unset($activeIds, $activeIdsImploded);
  823. /* ---------------------------------------------------------------------------------------------------------- */
  824. // полюбому удаляем, тех, кто были незабанены :)
  825. $dbUser = new Db_User();
  826. $dbUser->getTb(1, true); // обновляем кеш по ид на данном сервере.
  827. $usersToDelete = array(); // $db->fetchAll($db->select()->from('user_new')->where($whereNotActive), __METHOD__);
  828. $phonesToBlacklist = array();
  829. // foreach ($usersToDelete as $user) {
  830. // // забанен, не платил, номер телефона активирован
  831. //// if($user['user_is_hidden'] >= 1 && $user['user_cash'] === null && $user['user_phone_approved'] == 1 && $user['user_phone']){
  832. //// $phonesToBlacklist[] = $user['user_phone'];
  833. //// }
  834. //
  835. // /**
  836. // * Слишком долго выполняется в тестах
  837. // */
  838. // if (!defined('TESTING')) {
  839. // $dbUser->deleteUser($user, "not-active-4day");
  840. // }
  841. // }
  842. //
  843. // // добавляем телефоны в blacklist. Отключено по просьбе Германова Евгения.
  844. //// $blacklistDao = Antifraud_Dao_Blacklist::getInstance();
  845. //// foreach($phonesToBlacklist as $phone){
  846. //// $blacklistDao->addToBlackList(Antifraud_Dao_Blacklist::TYPE_PHONE, $phone, 'not-active-4day-banned');
  847. //// }
  848. //
  849. // $dbUser->getTb(1, true);
  850. return array(
  851. 'transferred' => $activeTotal, //$countNew,
  852. 'deleted' => count($usersToDelete),
  853. 'phonesToBlacklist' => count($phonesToBlacklist)
  854. );
  855. }
  856. /**
  857. * @depracated - оставлю для истории. Сейчас этот крон работает в демоне. Код портировали с небольшими измениями.
  858. *
  859. * Обновления онлайн. Запускается раз в 1 минуту.
  860. *
  861. * Как эта хренота работает:
  862. * - забираем юзеров из SQ, туда записываются из разных мест массивчики вида
  863. array(
  864. 'user_id' => id юзера, который произвёл действие,
  865. 'time' => время, когда это действие было совершено,
  866. 'r_id' => реферрер - откуда пришёл юзер,
  867. 'r_data' => что-то (не вкурсе, присутствовало в старом кроне),
  868. 'src_id' => сурс - с какого типа ФС юзер вышел онлайн (FS, FSm, Mobile version, iPhone app, ...)
  869. );
  870. * - группируем эти действия по user_id и src_id
  871. * - берём время последнего действия для каждого типа
  872. * - достаём все записи из auto_online
  873. * - путём нехитрых манипуляций вычисляем по time_update юзеров, которые проэкспайрились
  874. * - исходя из данных из SQ определяем юзеров которые только что стали онлайн (тоесть отсутствуют в auto_online)
  875. * и тех, кто уже был онлайн на момент запуска крона (присутствуют в auto_online)
  876. * - и затем апдейтим группы этих юзеров
  877. * ... В общем коллективный разум нам подсказал сложную и интересную реализацию онлайнов.
  878. * В табличках user и user_new у нас есть (появилось) поле online_mask, которое содержит в себе побитовую маску
  879. * сурсов, в которых юзер онлайн/оффлайн. Битики лежат в константах этого класса (с префиксом SOURCE_*).
  880. * Масочка может принимать значение 0 - когда юзер вообще оффлайн или иметь установленные битики онлайн/эвэй для каждого их сурсов.
  881. * Под каждый сурс занято 2 бита - бит online и бит away (это когда юзер ещё где-то онлайн, но по данному сурсу уже оффлайн),
  882. * выглядит это как-то так (на момент запуска - 16 бит, определяется константой ONLINE_BIT_SHIFT):
  883. * |0|0|0|0|0|0|0|0|
  884. * | | | | FS: away, online
  885. * | | | FSm: away, online
  886. * | | Mobile: away, online
  887. * | iPhone: away, online
  888. * Если юзер не имеет ни одного установленного бита online - ему вставляется 0.
  889. * Апдейтится эта штука так: создаётся маска, имеющая 32 бит:
  890. * 16 младших бит - это те биты, которые мы хотим сбросить
  891. * 16 старших бит - это те биты, которые мы хотим поставить
  892. * Весь апдейт происходит сбрасыванием полной маски, и установкой со сдвигом маски на 16 бит вправо
  893. * Собственно вот :)
  894. * - и соответственно апдейтим мемкэш для этих юзеров
  895. * - удаляем юзеров из auto_online
  896. * Тут ещё есть немного кода, который был перенесён из старого крона, и коммент соответсвенно для него:
  897. * в) статистика логинов для Gameleads
  898. *
  899. */
  900. public static function updateAutoOnline($qnum)
  901. {
  902. $timeNow = time();
  903. $currDayTS = strtotime(date('Y-m-d 00:00:00'));
  904. $prevDayTS = strtotime(date('Y-m-d 00:00:00', strtotime('-1 day')));
  905. // забираем все из очереди
  906. if (PRODUCTION) {
  907. $queueNum = ($qnum == 1) ? Base_Service_SharedQueue::INDEX_ONLINE : Base_Service_SharedQueue::INDEX_ONLINE_EVEN;
  908. $sqData = Base_Service_SharedQueue::popAll($queueNum);
  909. } else {
  910. if ($qnum != 1) {
  911. return true;
  912. }
  913. $sqData = array_merge(
  914. Base_Service_SharedQueue::popAll(Base_Service_SharedQueue::INDEX_ONLINE),
  915. Base_Service_SharedQueue::popAll(Base_Service_SharedQueue::INDEX_ONLINE_EVEN)
  916. );
  917. }
  918. // берём время последнего действия с группировкой по юзеру и сурсу
  919. $byUserSrc = array();
  920. $usersLocation = array();
  921. /*$loggedOutUsers = array();*/
  922. foreach ($sqData as $k => $item) {
  923. if (!empty($item['from_logout'])) {
  924. /*$loggedOutUsers[$item['user_id']] = 1;*/
  925. continue;
  926. }
  927. // временная затычка, на время перехода со старого крона на новый
  928. if (!isset($item['src_id'])) {
  929. $item['src_id'] = self::SOURCE_FS_ONLINE;
  930. }
  931. /* Example
  932. $item = array(
  933. 'user_id' => $userId,
  934. 'time' => $timeNow,
  935. 'r_id' => $referrerId,
  936. 'r_data' => $returnData,
  937. 'src_id' => $sourceId
  938. );
  939. */
  940. // отсеиваем события старше таймаута для данного сурса (ибо нахх не нужны)
  941. if (($timeNow - $item['time']) > Base_Service_User::$onlineAwayTimeout[$item['src_id']]) {
  942. continue;
  943. }
  944. //Сохраним ip и время для обновления счетчиков логинов
  945. if (isset($item['long_ip'])) {
  946. $usersLocation[$item['user_id']] = $item['long_ip'];
  947. }
  948. if (!isset($byUserSrc[$item['user_id']])) {
  949. $byUserSrc[$item['user_id']] = array();
  950. } elseif (!isset($byUserSrc[$item['user_id']][$item['src_id']])) {
  951. $byUserSrc[$item['user_id']][$item['src_id']] = array();
  952. }
  953. // сделаем ссылочку, для более удобной работы с элементом массива
  954. $link = &$byUserSrc[$item['user_id']][$item['src_id']];
  955. // обновляем время последнего действия
  956. if (!isset($link['time']) || $item['time'] > $link['time']) {
  957. $link['time'] = $item['time'];
  958. }
  959. // Записываем только первый refererId для юзера
  960. if (!empty($item['r_id']) && !isset($link['r_id'])) {
  961. $link['r_id'] = $item['r_id'];
  962. }
  963. if (!empty($item['r_data']) && empty($link['r_data'])) {
  964. $link['r_data'] = $item['r_data'];
  965. }
  966. unset($link);
  967. }
  968. unset($sqData);
  969. $_new = $_up = $_off = array();
  970. // выбираем пользователей которые были онлайн
  971. $autoOnlineByUser = array();
  972. $db = Base_Context::getInstance()->getDbConnection();
  973. $autoOnlineData = $db->selectAll('auto_online', "SELECT user_id, source_id, time_update, time_in FROM auto_online", __METHOD__);
  974. foreach ($autoOnlineData as $k => $row) {
  975. // фильтруем чётные / нечётные
  976. if (PRODUCTION && (($qnum == 1 && $row['user_id'] % 2 == 0) || ($qnum == 2 && $row['user_id'] % 2 != 0))) {
  977. continue;
  978. }
  979. $timeOut = Base_Service_User::$onlineAwayTimeout[$row['source_id']];
  980. if (!isset($autoOnlineByUser[$row['user_id']])) {
  981. $autoOnlineByUser[$row['user_id']] = array();
  982. }
  983. $autoOnlineByUser[$row['user_id']][$row['source_id']] = array(
  984. 'time_update' => $row['time_update'],
  985. 'time_in' => $row['time_in']
  986. );
  987. /* Вынес условия в переменные, иначе код нечитаемый */
  988. // если юзер проэкспайрился по табличке
  989. $dbTimeout = ($timeNow - $row['time_update']) > $timeOut;
  990. // отсутствует в SQ или имеет в SQ проэкспайреное время
  991. $sqTimeout = (!isset($byUserSrc[$row['user_id']][$row['source_id']]) || ($timeNow - $byUserSrc[$row['user_id']][$row['source_id']]['time']) > self::ONLINE_TIMEOUT_UPDATE);
  992. // если юзер олайн больше чем максимальное время онлайна
  993. $maxTimeout = ($timeNow - $row['time_in']) > self::ONLINE_MAX_ONLINE_TIME;
  994. if

Large files files are truncated, but you can click here to view the full file