BxDolAccount.php 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110
  1. <?php defined('BX_DOL') or die('hack attempt');
  2. /**
  3. * Copyright (c) UNA, Inc - https://una.io
  4. * MIT License - https://opensource.org/licenses/MIT
  5. *
  6. * @defgroup UnaCore UNA Core
  7. * @{
  8. */
  9. bx_import('BxDolAcl');
  10. class BxDolAccount extends BxDolFactory implements iBxDolSingleton
  11. {
  12. protected $_iAccountID;
  13. protected $_aInfo;
  14. protected $_oQuery;
  15. protected $_sImageField;
  16. protected $_aImageTranscoders;
  17. /**
  18. * Constructor
  19. */
  20. protected function __construct ($iAccountId)
  21. {
  22. $iAccountId = (int)$iAccountId;
  23. $sClass = get_class($this) . '_' . $iAccountId;
  24. if (isset($GLOBALS['bxDolClasses'][$sClass]))
  25. trigger_error ('Multiple instances are not allowed for the class: ' . get_class($this), E_USER_ERROR);
  26. parent::__construct();
  27. $this->_iAccountID = $iAccountId; // since constructor is protected $iAccountId is always valid
  28. $this->_oQuery = BxDolAccountQuery::getInstance();
  29. $this->_sImageField = 'picture';
  30. $this->_aImageTranscoders = [
  31. 'icon' => 'sys_accounts_icon',
  32. 'thumb' => 'sys_accounts_thumb',
  33. 'ava' => 'sys_accounts_avatar',
  34. 'ava_big' => 'sys_accounts_avatar_big'
  35. ];
  36. }
  37. /**
  38. * Prevent cloning the instance
  39. */
  40. public function __clone()
  41. {
  42. $sClass = get_class($this) . '_' . $this->_iProfileID;
  43. if (isset($GLOBALS['bxDolClasses'][$sClass]))
  44. trigger_error('Clone is not allowed for the class: ' . get_class($this), E_USER_ERROR);
  45. }
  46. /**
  47. * Get singleton instance of the class
  48. */
  49. public static function getInstance($mixedAccountId = false, $bClearCache = false)
  50. {
  51. if (!$mixedAccountId)
  52. $mixedAccountId = getLoggedId();
  53. $iAccountId = self::getID($mixedAccountId);
  54. if (!$iAccountId)
  55. return false;
  56. $sClass = __CLASS__ . '_' . $iAccountId;
  57. if ($bClearCache && isset($GLOBALS['bxDolClasses'][$sClass]))
  58. unset($GLOBALS['bxDolClasses'][$sClass]);
  59. if(!isset($GLOBALS['bxDolClasses'][$sClass]))
  60. $GLOBALS['bxDolClasses'][$sClass] = new BxDolAccount($iAccountId);
  61. return $GLOBALS['bxDolClasses'][$sClass];
  62. }
  63. /**
  64. * Get studio operator account singleton instance on the class
  65. */
  66. public static function getInstanceStudioOperator()
  67. {
  68. $oQuery = BxDolAccountQuery::getInstance();
  69. if (!($iId = $oQuery->getStudioOperatorId()))
  70. return false;
  71. return self::getInstance($iId);
  72. }
  73. /**
  74. * Get account id
  75. */
  76. public function id()
  77. {
  78. $a = $this->getInfo($this->_iAccountID);
  79. return isset($a['id']) ? $a['id'] : false;
  80. }
  81. /**
  82. * Check if account is confirmed, it is checked by email confirmation
  83. */
  84. public function isConfirmed($iAccountId = false)
  85. {
  86. $iId = (int)$iAccountId ? (int)$iAccountId : $this->_iAccountID;
  87. $a = $this->getInfo($iId);
  88. $bResult = false;
  89. $sConfirmationType = getParam('sys_account_confirmation_type');
  90. switch($sConfirmationType) {
  91. case BX_ACCOUNT_CONFIRMATION_NONE:
  92. $bResult = true;
  93. break;
  94. case BX_ACCOUNT_CONFIRMATION_EMAIL:
  95. if($a['email_confirmed'])
  96. $bResult = true;
  97. break;
  98. case BX_ACCOUNT_CONFIRMATION_PHONE:
  99. if($a['phone_confirmed'])
  100. $bResult = true;
  101. break;
  102. case BX_ACCOUNT_CONFIRMATION_EMAIL_PHONE:
  103. if($a['email_confirmed'] && $a['phone_confirmed'])
  104. $bResult = true;
  105. break;
  106. case BX_ACCOUNT_CONFIRMATION_EMAIL_OR_PHONE:
  107. if($a['email_confirmed'] || $a['phone_confirmed'])
  108. $bResult = true;
  109. break;
  110. }
  111. /**
  112. * @hooks
  113. * @hookdef hook-account-is_confirmed 'account', 'is_confirmed' - hook in $oAccount->isConfirmed check
  114. * - $unit_name - equals `account`
  115. * - $action - equals `is_confirmed`
  116. * - $object_id - account id
  117. * - $sender_id - not used
  118. * - $extra_params - array of additional params with the following array keys:
  119. * - `type` - [string] confirmation type can be none/phone/email/email_and_phone/email_or_phone
  120. * - `override_result` - [bool] by ref, if account confirmed = true, otherwise false, can be overridden in hook processing
  121. * @hook @ref hook-account-is_confirmed
  122. */
  123. bx_alert('account', 'is_confirmed', $iId, false, array('type' => $sConfirmationType, 'override_result' => &$bResult));
  124. return $bResult;
  125. }
  126. public function getCurrentConfirmationStatusValue($iAccountId = false)
  127. {
  128. $a = $this->getInfo((int)$iAccountId);
  129. $sTmp = $a['email_confirmed'] . $a['phone_confirmed'];
  130. switch ($sTmp) {
  131. case '01':
  132. return BX_ACCOUNT_CONFIRMATION_PHONE;
  133. case '10':
  134. return BX_ACCOUNT_CONFIRMATION_EMAIL;
  135. case '11':
  136. return BX_ACCOUNT_CONFIRMATION_EMAIL_PHONE;
  137. }
  138. return BX_ACCOUNT_CONFIRMATION_NONE;
  139. }
  140. public function isConfirmedEmail($iAccountId = false)
  141. {
  142. $iId = (int)$iAccountId ? (int)$iAccountId : $this->_iAccountID;
  143. $bResult = false;
  144. if(self::isNeedConfirmEmail()) {
  145. $a = $this->getInfo($iId);
  146. $bResult = $a['email_confirmed'] ? true : false;
  147. }
  148. else
  149. $bResult = true;
  150. /**
  151. * @hooks
  152. * @hookdef hook-account-is_confirmed 'account', 'is_confirmed_email' - hook in $oAccount->isConfirmedEmail check
  153. * - $unit_name - equals `account`
  154. * - $action - equals `is_confirmed_email`
  155. * - $object_id - account id
  156. * - $sender_id - not used
  157. * - $extra_params - array of additional params with the following array keys:
  158. * - `override_result` - [bool] by ref, if email confirmed = true, otherwise false, can be overridden in hook processing
  159. * @hook @ref hook-account-is_confirmed_email
  160. */
  161. bx_alert('account', 'is_confirmed_email', $iId, false, array('override_result' => &$bResult));
  162. return $bResult;
  163. }
  164. public function isConfirmedPhone($iAccountId = false)
  165. {
  166. $iId = (int)$iAccountId ? (int)$iAccountId : $this->_iAccountID;
  167. $bResult = false;
  168. if(self::isNeedConfirmPhone()) {
  169. $a = $this->getInfo((int)$iAccountId);
  170. $bResult = $a['phone_confirmed'] ? true : false;
  171. }
  172. else
  173. $bResult = true;
  174. /**
  175. * @hooks
  176. * @hookdef hook-account-is_confirmed_phone 'account', 'is_confirmed_phone' - hook in $oAccount->isConfirmedPhone check
  177. * - $unit_name - equals `account`
  178. * - $action - equals `is_confirmed_phone`
  179. * - $object_id - account id
  180. * - $sender_id - not used
  181. * - $extra_params - array of additional params with the following array keys:
  182. * - `override_result` - [bool] by ref, if phone confirmed = true, otherwise false, can be overridden in hook processing
  183. * @hook @ref hook-account-is_confirmed_phone
  184. */
  185. bx_alert('account', 'is_confirmed_phone', $iId, false, array('override_result' => &$bResult));
  186. return $bResult;
  187. }
  188. static public function isNeedConfirmEmail()
  189. {
  190. if(in_array(getParam('sys_account_confirmation_type'), array(BX_ACCOUNT_CONFIRMATION_EMAIL, BX_ACCOUNT_CONFIRMATION_EMAIL_PHONE, BX_ACCOUNT_CONFIRMATION_EMAIL_OR_PHONE)))
  191. return true;
  192. return false;
  193. }
  194. static public function isNeedConfirmPhone()
  195. {
  196. if(in_array(getParam('sys_account_confirmation_type'), array(BX_ACCOUNT_CONFIRMATION_PHONE, BX_ACCOUNT_CONFIRMATION_EMAIL_PHONE, BX_ACCOUNT_CONFIRMATION_EMAIL_OR_PHONE)))
  197. return true;
  198. return false;
  199. }
  200. public function isLocked($iAccountId = false)
  201. {
  202. $a = $this->getInfo((int)$iAccountId);
  203. return $a['locked'] ? true : false;
  204. }
  205. //
  206. /**
  207. * Set account email to confirmed or unconfirmed
  208. * @param int $isConfirmed - false: mark email as unconfirmed, true: as confirmed
  209. * @param int $iAccountId - optional account id
  210. * @return true on success or false on error
  211. */
  212. public function updateEmailConfirmed($isConfirmed, $isAutoSendConfrmationEmail = true, $iAccountId = false)
  213. {
  214. $iId = (int)$iAccountId ? (int)$iAccountId : $this->_iAccountID;
  215. if (!$isConfirmed && $isAutoSendConfrmationEmail && self::isNeedConfirmEmail()) // if email_confirmation procedure is enabled - send email confirmation letter
  216. $this->sendConfirmationEmail($iId);
  217. if ($this->_oQuery->updateEmailConfirmed($isConfirmed, $iId)) {
  218. $this->_aInfo = false;
  219. /**
  220. * @hooks
  221. * @hookdef hook-account-confirm 'account', 'confirm' - hook in email confirmation $oAccount->updateEmailConfirmed
  222. * - $unit_name - equals `account`
  223. * - $action - can be confirm/unconfirm
  224. * - $object_id - account id
  225. * - $sender_id - not used
  226. * - $extra_params - not used
  227. * @hook @ref hook-account-confirm
  228. */
  229. bx_alert('account', $this->isConfirmed() ? 'confirm' : 'unconfirm', $iId);
  230. return true;
  231. }
  232. return false;
  233. }
  234. /**
  235. * Set account phone to confirmed or unconfirmed
  236. * @param int $isConfirmed - false: mark phone as unconfirmed, true: as confirmed
  237. * @param int $iAccountId - optional account id
  238. * @return true on success or false on error
  239. */
  240. public function updatePhoneConfirmed($isConfirmed, $iAccountId = false)
  241. {
  242. $iId = (int)$iAccountId ? (int)$iAccountId : $this->_iAccountID;
  243. if ($this->_oQuery->updatePhoneConfirmed($isConfirmed, $iId)) {
  244. $this->_aInfo = false;
  245. bx_alert('account', $this->isConfirmed() ? 'confirm' : 'unconfirm', $iId);
  246. return true;
  247. }
  248. return false;
  249. }
  250. /**
  251. * Change account password
  252. * @param string $sPassword - new password
  253. * @param int $iAccountId - optional account id
  254. * @return true on success or false on error
  255. */
  256. public function updatePassword($sPassword, $iAccountId = false)
  257. {
  258. $iId = (int)$iAccountId ? (int)$iAccountId : $this->_iAccountID;
  259. $sSalt = genRndSalt();
  260. $sPasswordHash = encryptUserPwd($sPassword, $sSalt);
  261. $this->_oQuery->logPassword($iId);
  262. if((int)$this->_oQuery->updatePassword($sPasswordHash, $sSalt, $iId) > 0) {
  263. /**
  264. * @hooks
  265. * @hookdef hook-account-edited 'account', 'edited' - hook on account edited $oAccount->updatePassword
  266. * - $unit_name - equals `account`
  267. * - $action - equals edited
  268. * - $object_id - account id
  269. * - $sender_id - account sender id
  270. * - $extra_params - array of additional params with the following array keys:
  271. * - `action` - [string] action's name, can be reset_password
  272. * @hook @ref hook-account-edited
  273. */
  274. bx_alert('account', 'edited', $iId, ($iSenderId = getLoggedId()) != 0 ? $iSenderId : $iId, [
  275. 'action' => 'reset_password'
  276. ]);
  277. $this->doAudit($iId, '_sys_audit_action_account_reset_password');
  278. return true;
  279. }
  280. return false;
  281. }
  282. /**
  283. * Set account phone
  284. * @param string $sPhone - phone number
  285. * @param int $iAccountId - optional account id
  286. * @return true on success or false on error
  287. */
  288. public function updatePhone($sPhone, $iAccountId = false)
  289. {
  290. $iId = (int)$iAccountId ? (int)$iAccountId : $this->_iAccountID;
  291. if ($this->_oQuery->updatePhone($sPhone, $iId)) {
  292. /**
  293. * @hooks
  294. * @hookdef hook-account-set_phone 'account', 'set_phone' - hook after accout password changed
  295. * - $unit_name - equals `account`
  296. * - $action - equals `set_phone`
  297. * - $object_id - account id
  298. * - $sender_id - not used
  299. * - $extra_params - not used
  300. * @hook @ref hook-account-set_phone
  301. */
  302. bx_alert('account', 'set_phone', $iId);
  303. return true;
  304. }
  305. return false;
  306. }
  307. /**
  308. * Switch context automatically to the first available profile
  309. * @param $iProfileIdFilterOut profile ID to exclude from the list of possible profiles
  310. * @param $iAccountId account ID to use istead of current account
  311. * @return true on success or false on error
  312. */
  313. public function updateProfileContextAuto($iProfileIdFilterOut = false, $iAccountId = false)
  314. {
  315. $oAccount = (!$iAccountId || $iAccountId == $this->_iAccountID ? $this : BxDolAccount::getInstance ($iAccountId));
  316. if (!$oAccount)
  317. return false;
  318. $aAccountInfo = $oAccount->getInfo();
  319. $aProfiles = $oAccount->getProfiles();
  320. $oProfileAccount = BxDolProfile::getInstanceAccountProfile($oAccount->id());
  321. // unset deleted profile and account profile
  322. if ($iProfileIdFilterOut)
  323. unset($aProfiles[$iProfileIdFilterOut]);
  324. unset($aProfiles[$oProfileAccount->id()]);
  325. if ($aProfiles) {
  326. // try to use another profile
  327. reset($aProfiles);
  328. $iProfileId = key($aProfiles);
  329. }
  330. else {
  331. // if no profiles exist, use account profile
  332. $iProfileId = $oProfileAccount->id();
  333. }
  334. return $oAccount->updateProfileContext($iProfileId);
  335. }
  336. public function updateProfileContext($iSwitchToProfileId, $iAccountId = false)
  337. {
  338. $iId = (int)$iAccountId ? (int)$iAccountId : $this->_iAccountID;
  339. $aInfo = $this->getInfo((int)$iId);
  340. if (!$aInfo)
  341. return false;
  342. $ret = null;
  343. /**
  344. * @hooks
  345. * @hookdef hook-account-before_switch_context 'account', 'before_switch_context' - hook before switch profile_id frof current logged user
  346. * - $unit_name - equals `account`
  347. * - $action - equals `before_switch_context`
  348. * - $object_id - account id
  349. * - $sender_id - profile_id to switch to
  350. * - $extra_params - array of additional params with the following array keys:
  351. * - `profile_id_current` - [int] current profile_id
  352. * - `override_result` - [int] by ref, profile_id to switch to, can be overridden in hook processing
  353. * @hook @ref hook-account-before_switch_context
  354. */
  355. bx_alert('account', 'before_switch_context', $iId, $iSwitchToProfileId, array('profile_id_current' => $aInfo['profile_id'], 'override_result' => &$ret));
  356. if ($ret !== null)
  357. return $ret;
  358. if (!$this->_oQuery->updateCurrentProfile($iId, $iSwitchToProfileId))
  359. return false;
  360. $this->_aInfo = false;
  361. /**
  362. * @hooks
  363. * @hookdef hook-account-switch_context 'account', 'switch_context' - hook before switch profile_id frof current logged user
  364. * - $unit_name - equals `account`
  365. * - $action - equals `switch_context`
  366. * - $object_id - account id
  367. * - $sender_id - profile_id to switch to
  368. * - $extra_params - array of additional params with the following array keys:
  369. * - `profile_id_old` - [int] old profile_id
  370. * @hook @ref hook-account-switch_context
  371. */
  372. bx_alert('account', 'switch_context', $iId, $iSwitchToProfileId, array('profile_id_old' => $aInfo['profile_id']));
  373. return true;
  374. }
  375. /**
  376. * Send "confirmation" email
  377. */
  378. public function sendConfirmationEmail($iAccountId = false)
  379. {
  380. $iAccountId = (int)$iAccountId;
  381. if(!$iAccountId)
  382. $iAccountId = $this->_iAccountID;
  383. $aAccountInfo = $this->getInfo($iAccountId);
  384. if(empty($aAccountInfo) || !is_array($aAccountInfo))
  385. return false;
  386. $oKey = BxDolKey::getInstance();
  387. $sConfirmationCode = $oKey->getNewKeyNumeric(['account_id' => $iAccountId]);
  388. $sConfirmationLink = bx_append_url_params(BX_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink('page.php?i=confirm-email'), ['code' => $sConfirmationCode]);
  389. $sEmailTemplate = 't_Confirmation';
  390. $aEmailReplaceVars = [
  391. 'name' => $this->getDisplayName($iAccountId),
  392. 'email' => $aAccountInfo['email'],
  393. 'conf_code' => $sConfirmationCode,
  394. 'conf_link' => $sConfirmationLink,
  395. 'conf_form_link' => BX_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink('page.php?i=confirm-email')
  396. ];
  397. $bResult = sendMailTemplate($sEmailTemplate, $iAccountId, (int)$aAccountInfo['profile_id'], $aEmailReplaceVars, BX_EMAIL_SYSTEM);
  398. if($bResult)
  399. $this->doAudit($iAccountId, '_sys_audit_action_account_resend_confirmation_email');
  400. return $bResult;
  401. }
  402. public function sendResetPasswordEmail($iAccountId = false)
  403. {
  404. $iAccountId = (int)$iAccountId;
  405. if(!$iAccountId)
  406. $iAccountId = $this->_iAccountID;
  407. $aAccountInfo = $this->getInfo($iAccountId);
  408. if(empty($aAccountInfo) || !is_array($aAccountInfo))
  409. return false;
  410. $sKey = bx_get_reset_password_key($aAccountInfo['email']);
  411. $aReplaceVars = array(
  412. 'name' => $this->getDisplayName($iAccountId),
  413. 'email' => $aAccountInfo['email'],
  414. 'key' => $sKey,
  415. 'forgot_password_url' => bx_get_reset_password_link_by_key($sKey)
  416. );
  417. return sendMailTemplate('t_Forgot', $iAccountId, (int)$aAccountInfo['profile_id'], $aReplaceVars, BX_EMAIL_SYSTEM);
  418. }
  419. /**
  420. * Get account info
  421. */
  422. public function getInfo($iAccountId = false)
  423. {
  424. if ($iAccountId && $iAccountId != $this->_iAccountID)
  425. return $this->_oQuery->getInfoById((int)$iAccountId ? (int)$iAccountId : $this->_iAccountID);
  426. if ($this->_aInfo)
  427. return $this->_aInfo;
  428. $this->_aInfo = $this->_oQuery->getInfoById($this->_iAccountID);
  429. return $this->_aInfo;
  430. }
  431. /**
  432. * Get account display name
  433. */
  434. public function getDisplayName($iAccountId = false)
  435. {
  436. $aInfo = $this->getInfo($iAccountId);
  437. $sDisplayName = !empty($aInfo['name']) ? $aInfo['name'] : _t('_sys_txt_user_n', $aInfo['id']);
  438. /**
  439. * @hooks
  440. * @hookdef hook-account-account_name 'account', 'account_name' - hook on get account display name
  441. * - $unit_name - equals `account`
  442. * - $action - equals `account_name`
  443. * - $object_id - account id
  444. * - $sender_id - not used
  445. * - $extra_params - array of additional params with the following array keys:
  446. * - `info` - [array] contains account info from $oAccount->getInfo()
  447. * - `display_name` - [string] by ref, account display name, can be overridden in hook processing
  448. * @hook @ref hook-account-account_name
  449. */
  450. bx_alert('account', 'account_name', $iAccountId, 0, array('info' => $aInfo, 'display_name' => &$sDisplayName));
  451. return bx_process_output($sDisplayName);
  452. }
  453. /**
  454. * Get account url
  455. */
  456. public function getUrl($iAccountId = false)
  457. {
  458. return 'javascript:void(0);';
  459. }
  460. /**
  461. * Get account url
  462. */
  463. public function getUnit($iAccountId = false, $aParams = array())
  464. {
  465. $sTemplate = 'unit';
  466. $sTemplateSize = false;
  467. $aTemplateVars = array();
  468. if(!empty($aParams['template'])) {
  469. if(is_string($aParams['template']))
  470. $sTemplate = $aParams['template'];
  471. else if(is_array($aParams['template'])) {
  472. if(!empty($aParams['template']['name']))
  473. $sTemplate = $aParams['template']['name'];
  474. if(!empty($aParams['template']['size']))
  475. $sTemplateSize = $aParams['template']['size'];
  476. if(!empty($aParams['template']['vars']))
  477. $aTemplateVars = $aParams['template']['vars'];
  478. }
  479. }
  480. $sTemplate = 'account_' . $sTemplate . '.html';
  481. if(empty($sTemplateSize))
  482. $sTemplateSize = 'thumb';
  483. $sTitle = $this->getDisplayName($iAccountId);
  484. $aInfo = $this->getInfo($iAccountId);
  485. $sImageUrl = $this->_getImageUrl($sTemplateSize, $aInfo);
  486. $bImageUrl = !empty($sImageUrl);
  487. $aTmplVars = [
  488. 'size' => $sTemplateSize,
  489. 'content_url' => $this->getUrl($iAccountId),
  490. 'title' => $sTitle,
  491. 'title_attr' => bx_html_attribute($sTitle),
  492. 'bx_if:show_thumb_image' => [
  493. 'condition' => $bImageUrl,
  494. 'content' => [
  495. 'size' => $sTemplateSize,
  496. 'url' => $sImageUrl
  497. ]
  498. ],
  499. 'bx_if:show_thumb_letter' => [
  500. 'condition' => !$bImageUrl,
  501. 'content' => [
  502. 'size' => $sTemplateSize,
  503. 'color' => implode(', ', BxDolTemplate::getColorCode(($iAccountId ? $iAccountId : $this->_iAccountID), 1.0)),
  504. 'letter' => mb_strtoupper(mb_substr($sTitle, 0, 1)),
  505. ]
  506. ],
  507. 'bx_if:show_online' => [
  508. 'condition' => $this->isOnline($iAccountId),
  509. 'content' => []
  510. ]
  511. ];
  512. if(!empty($aTemplateVars) && is_array($aTemplateVars))
  513. $aTmplVars = array_merge ($aTmplVars, $aTemplateVars);
  514. return BxDolTemplate::getInstance()->parseHtmlByName($sTemplate, $aTmplVars);
  515. }
  516. /**
  517. * Get picture url
  518. */
  519. public function getPicture($iAccountId = false)
  520. {
  521. return BxDolTemplate::getInstance()->getImageUrl('account.svg');
  522. }
  523. /**
  524. * Get big (2x) avatar url
  525. */
  526. public function getAvatarBig($iAccountId = false)
  527. {
  528. return BxDolTemplate::getInstance()->getImageUrl('account.svg');
  529. }
  530. /**
  531. * Get avatar url
  532. */
  533. public function getAvatar($iAccountId = false)
  534. {
  535. return BxDolTemplate::getInstance()->getImageUrl('account.svg');
  536. }
  537. /**
  538. * Get thumb picture url
  539. */
  540. public function getThumb($iAccountId = false)
  541. {
  542. return BxDolTemplate::getInstance()->getImageUrl('account.svg');
  543. }
  544. /**
  545. * Get icon picture url
  546. */
  547. public function getIcon($iAccountId = false)
  548. {
  549. return BxDolTemplate::getInstance()->getImageUrl('account.svg');
  550. }
  551. /**
  552. * Get account email
  553. */
  554. public function getEmail($iAccountId = false)
  555. {
  556. $iAccountId = (int)$iAccountId ? (int)$iAccountId : $this->_iAccountID;
  557. $aAccountInfo = $this->getInfo($iAccountId);
  558. return $aAccountInfo['email'];
  559. }
  560. /**
  561. * Get language ID
  562. */
  563. public function getLanguageId($iAccountId = false)
  564. {
  565. $iAccountId = (int)$iAccountId ? (int)$iAccountId : $this->_iAccountID;
  566. $aAccountInfo = $this->getInfo($iAccountId);
  567. return $aAccountInfo['lang_id'];
  568. }
  569. /**
  570. * Is account online
  571. */
  572. public function isOnline($iAccountId = false)
  573. {
  574. $iAccountId = (int)$iAccountId ? (int)$iAccountId : $this->_iAccountID;
  575. return $this->_oQuery->isOnline($iAccountId);
  576. }
  577. /**
  578. * Validate account.
  579. * @param $s - account identifier (id or email)
  580. * @return account id or false if account was not found
  581. */
  582. static public function getID($s)
  583. {
  584. $oQuery = BxDolAccountQuery::getInstance();
  585. bx_import('BxDolForm');
  586. if (BxDolFormCheckerHelper::checkEmail($s)) {
  587. $iId = (int)$oQuery->getIdByEmail($s);
  588. return $iId ? $iId : false;
  589. }
  590. if (preg_match("/^\+[0-9\s]+$/", $s)) {
  591. $iId = (int)$oQuery->getIdByPhone($s);
  592. return $iId ? $iId : false;
  593. }
  594. $iId = $oQuery->getIdById((int)$s);
  595. return $iId ? $iId : false;
  596. }
  597. /**
  598. * Check if profiles limit reached
  599. */
  600. public function isProfilesLimitReached ()
  601. {
  602. $iProfilesLimit = (int)getParam('sys_account_limit_profiles_number');
  603. /**
  604. * @hooks
  605. * @hookdef hook-account-get_limit_profiles_number 'account', 'get_limit_profiles_number' - hook on get account limit on the number of profiles
  606. * - $unit_name - equals `account`
  607. * - $action - equals `get_limit_profiles_number`
  608. * - $object_id - not used
  609. * - $sender_id - not used
  610. * - $extra_params - array of additional params with the following array keys:
  611. * - `account_id` - [int] account id
  612. * - `number` - [int] by ref, account limit on the number of profiles, can be overridden in hook processing
  613. * @hook @ref hook-account-get_limit_profiles_number
  614. */
  615. bx_alert('account', 'get_limit_profiles_number', 0, 0, array('account_id' => $this->_iAccountID, 'number' => &$iProfilesLimit));
  616. if (!isAdmin() && $iProfilesLimit && ($iProfilesNum = $this->getProfilesNumber()) && $iProfilesNum >= $iProfilesLimit)
  617. return true;
  618. return false;
  619. }
  620. /**
  621. * Get number of profiles associated with the account
  622. */
  623. public function getProfilesNumber ($isFilterNonSwitchableProfiles = true)
  624. {
  625. $a = $this->getProfilesIds($isFilterNonSwitchableProfiles);
  626. return count($a);
  627. }
  628. /**
  629. * Get all profile ids associated with the account
  630. */
  631. public function getProfilesIds ($isFilterNonSwitchableProfiles = true, $isFilterSystemProfiles = true)
  632. {
  633. $a = $this->getProfiles ($isFilterNonSwitchableProfiles, $isFilterSystemProfiles);
  634. return $a ? array_keys($a) : array();
  635. }
  636. /**
  637. * Get all profiles associated with the account
  638. */
  639. public function getProfiles ($isFilterNonSwitchableProfiles = true, $isFilterSystemProfiles = true)
  640. {
  641. $oProfileQuery = BxDolProfileQuery::getInstance();
  642. $aProfiles = $oProfileQuery->getProfilesByAccount($this->_iAccountID);
  643. if ($isFilterNonSwitchableProfiles) {
  644. foreach ($aProfiles as $iProfileId => $aProfile) {
  645. if ('system' == $aProfile['type'])
  646. continue;
  647. if (!BxDolService::call($aProfile['type'], 'act_as_profile'))
  648. unset($aProfiles[$iProfileId]);
  649. }
  650. }
  651. if ($isFilterSystemProfiles) {
  652. foreach ($aProfiles as $iProfileId => $aProfile) {
  653. if ('system' == $aProfile['type'])
  654. unset($aProfiles[$iProfileId]);
  655. }
  656. }
  657. return $aProfiles;
  658. }
  659. /**
  660. * Delete profile.
  661. * @param $bDeleteWithContent - delete associated profiles with all its contents
  662. * @param $bScheduled - delete account using background jobs.
  663. */
  664. function delete($bDeleteWithContent = false, $bScheduled = false)
  665. {
  666. if ($bScheduled)
  667. return BxDolBackgroundJobs::getInstance()->add('account_delete_' . $this->_iAccountID, ['system', 'account_delete', [$this->_iAccountID, $bDeleteWithContent], 'TemplServiceAccount']);
  668. $aAccountInfo = $this->_oQuery->getInfoById($this->_iAccountID);
  669. if (!$aAccountInfo)
  670. return false;
  671. // create system event before deletion
  672. $isStopDeletion = false;
  673. /**
  674. * @hooks
  675. * @hookdef hook-account-before_delete 'account', 'before_delete' - hook on before delete account,
  676. * - $unit_name - equals `account`
  677. * - $action - equals `before_delete`
  678. * - $object_id - account id
  679. * - $sender_id - not used
  680. * - $extra_params - array of additional params with the following array keys:
  681. * - `delete_with_content` - [bool] if account will delete with content = true, otherwise = false
  682. * - `stop_deletion` - [bool] by ref, if it set to true account deletion will stopped, can be overridden in hook processing
  683. * @hook @ref hook-account-before_delete
  684. */
  685. bx_alert('account', 'before_delete', $this->_iAccountID, 0, array('delete_with_content' => $bDeleteWithContent, 'stop_deletion' => &$isStopDeletion));
  686. if ($isStopDeletion)
  687. return false;
  688. $oAccountQuery = BxDolAccountQuery::getInstance();
  689. $oProfileQuery = BxDolProfileQuery::getInstance();
  690. $aProfiles = $oProfileQuery->getProfilesByAccount($this->_iAccountID);
  691. foreach ($aProfiles as $iProfileId => $aRow) {
  692. $oProfile = BxDolProfile::getInstance($iProfileId);
  693. if (!$oProfile)
  694. continue;
  695. $oProfile->delete(false, $bDeleteWithContent, true);
  696. }
  697. // delete profile
  698. if (!$oAccountQuery->delete($this->_iAccountID))
  699. return false;
  700. // unset class instance to prevent creating the instance again
  701. $sClass = __CLASS__ . '_' . $this->_iAccountID;
  702. unset($GLOBALS['bxDolClasses'][$sClass]);
  703. /**
  704. * @hooks
  705. * @hookdef hook-account-delete 'account', 'delete' - hook on after delete account
  706. * - $unit_name - equals `account`
  707. * - $action - equals `delete`
  708. * - $object_id - account id
  709. * - $sender_id - not used
  710. * - $extra_params - array of additional params with the following array keys:
  711. * - `delete_with_content` - [bool] if account will delete with content = true, otherwise = false
  712. * @hook @ref hook-account-delete
  713. */
  714. bx_alert('account', 'delete', $this->_iAccountID, 0, array ('delete_with_content' => $bDeleteWithContent));
  715. $this->doAudit($this->_iAccountID, $bDeleteWithContent ? '_sys_audit_action_account_deleted_with_content' : '_sys_audit_action_account_deleted');
  716. return true;
  717. }
  718. /**
  719. * Add permament messages.
  720. */
  721. public function addInformerPermanentMessages ($oInformer)
  722. {
  723. if (!$this->isConfirmed()) {
  724. if (!$this->isConfirmedEmail()) {
  725. $sUrl = BxDolPermalinks::getInstance()->permalink('page.php?i=confirm-email');
  726. $aAccountInfo = $this->getInfo();
  727. $oInformer->add('sys-account-unconfirmed-email', _t('_sys_txt_account_unconfirmed_email', $sUrl . '&resend=1', $aAccountInfo['email'], $sUrl), BX_INFORMER_ALERT);
  728. }
  729. if (!$this->isConfirmedPhone()) {
  730. $sUrl = BxDolPermalinks::getInstance()->permalink('page.php?i=confirm-phone') . '';
  731. $aAccountInfo = $this->getInfo();
  732. $oInformer->add('sys-account-unconfirmed-phone', _t('_sys_txt_account_unconfirmed_phone', $sUrl), BX_INFORMER_ALERT);
  733. }
  734. }
  735. $this->isNeedChangePassword(false, $oInformer);
  736. }
  737. public function addInformerDeletionScheduled($oInformer)
  738. {
  739. if(BxDolBackgroundJobs::getInstance()->exists('account_delete_' . $this->_iAccountID))
  740. $oInformer->add('sys-account-deletion-scheduled', _t('_sys_txt_account_deletion_scheduled'), BX_INFORMER_ALERT);
  741. }
  742. /**
  743. * Get unsubscribe link for the specified mesage type
  744. */
  745. public function getUnsubscribeLink($iEmailType, $iAccountId = false)
  746. {
  747. $iAccountId = (int)$iAccountId ? (int)$iAccountId : $this->_iAccountID;
  748. $sUrl = '';
  749. switch ($iEmailType) {
  750. case BX_EMAIL_NOTIFY:
  751. $sUrl = 'page.php?i=unsubscribe-notifications';
  752. break;
  753. case BX_EMAIL_MASS:
  754. $sUrl = 'page.php?i=unsubscribe-news';
  755. break;
  756. default:
  757. return '';
  758. }
  759. return BxDolPermalinks::getInstance()->permalink($sUrl) . '&id=' . $iAccountId . '&code=' . $this->getEmailHash();
  760. }
  761. public function getEmailHash($iAccountId = false)
  762. {
  763. $iAccountId = (int)$iAccountId ? (int)$iAccountId : $this->_iAccountID;
  764. $a = $this->getInfo();
  765. return md5($a['email'] . $a['salt'] . BX_DOL_SECRET);
  766. }
  767. public function getPasswordChangedDate($mixedAccount = false)
  768. {
  769. if(($bEmpty = empty($mixedAccount)) || !is_array($mixedAccount))
  770. $mixedAccount = $this->_oQuery->getInfoById(!$bEmpty ? (int)$mixedAccount : $this->_iAccountID);
  771. $iLastChanged = (int)$mixedAccount['password_changed'];
  772. return $iLastChanged ? $iLastChanged : (int)$mixedAccount['added'];
  773. }
  774. public function getPasswordExpiredDate($iPasswordExpiredForMembership, $mixedAccount = false)
  775. {
  776. if($iPasswordExpiredForMembership == 0)
  777. return 0;
  778. return $iPasswordExpiredForMembership * 86400 + $this->getPasswordChangedDate($mixedAccount);
  779. }
  780. public function isNeedChangePassword($iAccountId = false, $oInformer = false)
  781. {
  782. $iAccountId = (int)$iAccountId ? (int)$iAccountId : $this->_iAccountID;
  783. list($sPageLink, $aPageParams) = bx_get_base_url_inline();
  784. $sChangePasswordUri = 'account-settings-password';
  785. $bNeedRedirectToChangePassword = true;
  786. if(isset($aPageParams['i']) && $aPageParams['i'] == $sChangePasswordUri)
  787. $bNeedRedirectToChangePassword = false;
  788. /**
  789. * @hooks
  790. * @hookdef hook-account-is_need_to_change_password 'account', 'is_need_to_change_password' - hook on after delete account
  791. * - $unit_name - equals `account`
  792. * - $action - equals `is_need_to_change_password`
  793. * - $object_id - account id
  794. * - $sender_id - not used
  795. * - $extra_params - array of additional params with the following array keys:
  796. * - `override_result` - [bool] by ref, if Need Redirect To Change Password = true, otherwise = false, can be overridden in hook processing
  797. * @hook @ref hook-account-is_need_to_change_password
  798. */
  799. bx_alert('account', 'is_need_to_change_password', $iAccountId, false, [
  800. 'override_result' => &$bNeedRedirectToChangePassword
  801. ]);
  802. if(!$bNeedRedirectToChangePassword)
  803. return;
  804. $aAccountInfo = $this->getInfo();
  805. $aMembershipInfo = BxDolAcl::getInstance()->getMemberMembershipInfo($aAccountInfo['profile_id']);
  806. $sChangePasswordUrl = BX_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink('page.php?i=' . $sChangePasswordUri);
  807. if(($iPasswordExpiredDate = $this->getPasswordExpiredDate($aMembershipInfo['password_expired'], $aAccountInfo)) && $iPasswordExpiredDate < time()) {
  808. if(getParam('sys_account_accounts_force_password_change_after_expiration') == 'on') {
  809. header('Location: ' . $sChangePasswordUrl);
  810. exit;
  811. }
  812. else {
  813. if(!$oInformer)
  814. $oInformer = BxDolInformer::getInstance();
  815. $oInformer->add('sys-account-need-to-change-password', _t('_sys_txt_account_need_to_change_password', $sChangePasswordUrl), BX_INFORMER_ALERT);
  816. }
  817. }
  818. }
  819. public function doAudit($iAccountId, $sAction, $aData = [])
  820. {
  821. $iAccountId = (int)$iAccountId ? (int)$iAccountId : $this->_iAccountID;
  822. bx_audit($iAccountId, 'bx_accounts', $sAction, [
  823. 'content_title' => $this->getEmail(),
  824. 'data' => $aData
  825. ]);
  826. }
  827. /**
  828. * @return CHECK_ACTION_RESULT_ALLOWED if access is granted or error message if access is forbidden.
  829. */
  830. static public function isAllowedCreate ($iProfileId, $isPerformAction = false)
  831. {
  832. $aCheck = checkActionModule($iProfileId, 'create account', 'system', $isPerformAction);
  833. if ($aCheck[CHECK_ACTION_RESULT] !== CHECK_ACTION_RESULT_ALLOWED)
  834. return MsgBox($aCheck[CHECK_ACTION_MESSAGE]);
  835. return CHECK_ACTION_RESULT_ALLOWED;
  836. }
  837. static public function isAllowedCreateMultiple ($iProfileId, $isPerformAction = false)
  838. {
  839. $iLimit = (int)getParam('sys_account_limit_profiles_number');
  840. $bResult = false;
  841. if(isAdmin() || $iLimit != 1)
  842. $bResult = true;
  843. $oProfile = BxDolProfile::getInstance($iProfileId);
  844. if($iLimit == 1 && $oProfile->getModule() == 'system')
  845. $bResult = true;
  846. if($oProfile && $oProfile->getAccountId() != getLoggedId())
  847. $bResult = false;
  848. /**
  849. * @hooks
  850. * @hookdef hook-account-allow_create_another_profile 'profile', 'allow_create_another_profile' - hook on check allow create profile
  851. * - $unit_name - equals `profile`
  852. * - $action - equals `allow_create_another_profile`
  853. * - $object_id - profile id
  854. * - $sender_id - not used
  855. * - $extra_params - array of additional params with the following array keys:
  856. * - `override_result` - [bool] by ref, if allow create another profile = true, otherwise = false, can be overridden in hook processing
  857. * @hook @ref hook-account-allow_create_another_profile
  858. */
  859. bx_alert('profile', 'allow_create_another_profile', $iProfileId, 0, [
  860. 'override_result' => &$bResult
  861. ]);
  862. return $bResult;
  863. }
  864. /**
  865. * @return CHECK_ACTION_RESULT_ALLOWED if access is granted or error message if access is forbidden.
  866. */
  867. static public function isAllowedEdit ($iProfileId, $aContentInfo, $isPerformAction = false)
  868. {
  869. $oProfile = BxDolProfile::getInstance($iProfileId);
  870. if (!$oProfile)
  871. return _t('_sys_txt_access_denied');
  872. $aProfileInfo = $oProfile->getInfo();
  873. if (!$aProfileInfo || getLoggedId() != $aProfileInfo['account_id'])
  874. return _t('_sys_txt_access_denied');
  875. return CHECK_ACTION_RESULT_ALLOWED;
  876. }
  877. /**
  878. * @return CHECK_ACTION_RESULT_ALLOWED if access is granted or error message if access is forbidden.
  879. */
  880. static public function isAllowedDelete ($iProfileId, $aContentInfo, $isPerformAction = false)
  881. {
  882. $iAccountId = (int)BxDolProfile::getInstance($iProfileId)->getAccountId();
  883. if(isAdmin($iAccountId) && $iAccountId == (int)$aContentInfo['id'])
  884. return _t('_sys_txt_account_cannot_delete');
  885. $aCheck = checkActionModule($iProfileId, 'delete account', 'system', $isPerformAction);
  886. if ($aCheck[CHECK_ACTION_RESULT] !== CHECK_ACTION_RESULT_ALLOWED)
  887. return $aCheck[CHECK_ACTION_MESSAGE];
  888. return CHECK_ACTION_RESULT_ALLOWED;
  889. }
  890. static public function pruning ()
  891. {
  892. $iCount = 0;
  893. $iTime = time() - getParam('sys_account_accounts_pruning_interval') * 86400;
  894. $oAccountQuery = BxDolAccountQuery::getInstance();
  895. $aAccounts = [];
  896. $aAccountsSuspend = [];
  897. $bSuspend = false;
  898. $aPruning = explode(',', getParam('sys_account_accounts_pruning'));
  899. foreach ($aPruning as $sPruning) {
  900. switch($sPruning) {
  901. case 'no_login_suspend':
  902. $aAccountsSuspend = $oAccountQuery->getAccountsForPruning('no_login', $iTime);
  903. break;
  904. case 'no_login_delete':
  905. $aAccounts = array_merge($aAccounts, $oAccountQuery->getAccountsForPruning('no_login', $iTime));
  906. break;
  907. case 'no_confirm_delete':
  908. $aAccounts1 = $oAccountQuery->getAccountsForPruning('no_confirm', $iTime);
  909. foreach ($aAccounts1 as $k => $aAccount) {
  910. $oAccount = BxDolAccount::getInstance($aAccount['id']);
  911. if($oAccount->isConfirmed()){
  912. unset($aAccounts1[$k]);
  913. }
  914. }
  915. $aAccounts = array_merge($aAccounts, $aAccounts1);
  916. break;
  917. case 'no_profile_delete':
  918. $aAccounts = array_merge($aAccounts, $aAccounts = $oAccountQuery->getAccountsForPruning('no_profile', $iTime));
  919. break;
  920. }
  921. }
  922. foreach ($aAccountsSuspend as $k => $aAccount) {
  923. $oAccount = BxDolAccount::getInstance($aAccount['id']);
  924. $oProfile = BxDolProfile::getInstanceAccountProfile($aAccount['id']);
  925. $oProfile->suspend(BX_PROFILE_ACTION_AUTO, 0 ,false);
  926. $aProfiles = $oAccount->getProfiles();
  927. foreach($aProfiles as $aProfile){
  928. BxDolProfile::getInstance($aProfile['id'])->suspend(BX_PROFILE_ACTION_AUTO, 0 ,false);
  929. }
  930. $iCount++;
  931. }
  932. foreach ($aAccounts as $k => $aAccount) {
  933. $oAccount = BxDolAccount::getInstance($aAccount['id']);
  934. $oAccount->delete(false);
  935. $iCount++;
  936. }
  937. return $iCount;
  938. }
  939. protected function _getImageUrl($sSize, $aData)
  940. {
  941. $sImageUrl = '';
  942. if(!empty($this->_aImageTranscoders[$sSize]) && ($oImagesTranscoder = BxDolTranscoderImage::getObjectInstance($this->_aImageTranscoders[$sSize])) !== false)
  943. $sImageUrl = $oImagesTranscoder->getFileUrl($aData[$this->_sImageField]);
  944. return $sImageUrl;
  945. }
  946. }
  947. /** @} */