BxDolPrivacy.php 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010
  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. define('BX_DOL_PG_HIDDEN', '1');
  10. define('BX_DOL_PG_MEONLY', '2');
  11. define('BX_DOL_PG_ALL', '3');
  12. define('BX_DOL_PG_MEMBERS', '4');
  13. define('BX_DOL_PG_FRIENDS', '5');
  14. define('BX_DOL_PG_FRIENDS_SELECTED', '6');
  15. define('BX_DOL_PG_RELATIONS', '7');
  16. define('BX_DOL_PG_RELATIONS_SELECTED', '8');
  17. define('BX_DOL_PG_MEMBERSHIPS_SELECTED', '9');
  18. define('BX_DOL_PG_CUSTOM', '99');
  19. define('BX_DOL_PG_DEFAULT', BX_DOL_PG_ALL);
  20. /**
  21. * Privacy settings for any content.
  22. *
  23. * Integration of the content with privacy engine allows site member
  24. * to organize the access to his content.
  25. *
  26. * In addition to regular privacy groups (Public, Friends), spaces are supported.
  27. * When some space (usually some another profile) is specified as privacy,
  28. * then another profile visibility is used to check the privacy.
  29. *
  30. * Related classes:
  31. * BxDolPrivacyQuery - database queries.
  32. *
  33. * Example of usage:
  34. * 1. Register your privacy actions in `sys_privacy_actions` database table.
  35. * 2. Add one privacy field(with INT type) in the table with your items for each action.
  36. * For example, for action 'comment', the field name should be 'allow_comment_to'.
  37. * 3. Add group choosers for necessary actions in the form, which is used to add new items.
  38. * @code
  39. * $oPrivacy = new BxDolPrivacy();
  40. * $oPrivacy->getGroupChooser($iItemOwnerId, $sModuleUri, $sModuleAction);
  41. * @endcode
  42. *
  43. * 4. Check privacy when any user tries to view an item.
  44. * @code
  45. * $oPrivacy = new BxDolPrivacy($sTable, $sFieldId, $sFieldOwnerId);
  46. * if($oPrivacy->check($sAction, $iObjectId, $iViewerId)) {
  47. * //show necessary content
  48. * }
  49. * @endcode
  50. *
  51. * @see an example of integration in BoonEx modules, for example: Posts
  52. *
  53. *
  54. * Memberships/ACL:
  55. * Doesn't depend on user's membership.
  56. *
  57. *
  58. * Alerts:
  59. * no alerts available
  60. *
  61. */
  62. class BxDolPrivacy extends BxDolFactory implements iBxDolFactoryObject
  63. {
  64. protected $_oDb;
  65. protected $_sObject;
  66. protected $_aObject;
  67. protected $_aGroupsSettings;
  68. protected $_aGroupsExclude;
  69. protected $_sFormGroupCustom;
  70. protected $_sFormDisplayGcMembers;
  71. protected $_sFormDisplayGcMemberships;
  72. /**
  73. * Constructor
  74. * @param $aObject array of grid options
  75. */
  76. protected function __construct($aObject)
  77. {
  78. parent::__construct();
  79. $this->_aObject = $aObject;
  80. $this->_sObject = $aObject['object'];
  81. $this->_oDb = new BxDolPrivacyQuery();
  82. $this->_oDb->init($this->_aObject);
  83. $this->_aGroupsSettings = [
  84. BX_DOL_PG_FRIENDS_SELECTED => [
  85. 'name' => 'friends_selected',
  86. 'is_allowed' => '',
  87. 'connection' => 'sys_profiles_friends',
  88. 'js_method_create_group' => 'selectMembers',
  89. 'db_table_items' => 'sys_privacy_groups_custom_members',
  90. 'db_field_item' => 'member_id',
  91. 'uri_get_items' => 'users_list',
  92. ],
  93. BX_DOL_PG_RELATIONS => [
  94. 'name' => 'relations',
  95. 'is_allowed' => 'isAllowedRelations',
  96. 'connection' => 'sys_profiles_relations',
  97. ],
  98. BX_DOL_PG_RELATIONS_SELECTED => [
  99. 'name' => 'relations_selected',
  100. 'is_allowed' => 'isAllowedRelations',
  101. 'connection' => 'sys_profiles_relations',
  102. 'js_method_create_group' => 'selectMembers',
  103. 'db_table_items' => 'sys_privacy_groups_custom_members',
  104. 'db_field_item' => 'member_id',
  105. 'uri_get_items' => 'users_list',
  106. ],
  107. BX_DOL_PG_MEMBERSHIPS_SELECTED => [
  108. 'name' => 'memberships_selected',
  109. 'is_allowed' => 'isAllowedMemberships',
  110. 'connection' => '',
  111. 'js_method_create_group' => 'selectMemberships',
  112. 'db_table_items' => 'sys_privacy_groups_custom_memberships',
  113. 'db_field_item' => 'membership_id',
  114. 'uri_get_items' => 'memberships_list',
  115. ]
  116. ];
  117. $this->_aGroupsExclude = array();
  118. $this->_sFormGroupCustom = 'sys_privacy_group_custom';
  119. $this->_sFormDisplayGcMembers = 'sys_privacy_group_custom_members';
  120. $this->_sFormDisplayGcMemberships = 'sys_privacy_group_custom_memberships';
  121. }
  122. /**
  123. * Get privacy object instance by object name
  124. * @param $sObject object name
  125. * @return object instance or false on error
  126. */
  127. public static function getObjectInstance($sObject)
  128. {
  129. if(isset($GLOBALS['bxDolClasses']['BxDolPrivacy!' . $sObject]))
  130. return $GLOBALS['bxDolClasses']['BxDolPrivacy!' . $sObject];
  131. $aObject = BxDolPrivacyQuery::getPrivacyObject($sObject);
  132. if(!$aObject || !is_array($aObject))
  133. return false;
  134. $sClass = 'BxTemplPrivacy';
  135. if(!empty($aObject['override_class_name'])) {
  136. $sClass = $aObject['override_class_name'];
  137. if(!empty($aObject['override_class_file']))
  138. require_once(BX_DIRECTORY_PATH_ROOT . $aObject['override_class_file']);
  139. }
  140. $o = new $sClass($aObject);
  141. return ($GLOBALS['bxDolClasses']['BxDolPrivacy!' . $sObject] = $o);
  142. }
  143. /**
  144. * Get Select element with available groups.
  145. *
  146. * @param string $sObject privacy object name.
  147. * @param integer $iOwnerId object's owner ID.
  148. * @param array $aParams an array of custom selector's params (dynamic_groups - an array of arrays('key' => group_id, 'value' => group_title), title - the title to be used for generated field).
  149. * @return an array with Select element description.
  150. */
  151. public static function getGroupChooser($sObject, $iOwnerId = 0, $aParams = array())
  152. {
  153. $oPrivacy = BxDolPrivacy::getObjectInstance($sObject);
  154. if(empty($oPrivacy))
  155. return array();
  156. $sModule = $oPrivacy->_aObject['module'];
  157. $sAction = $oPrivacy->_aObject['action'];
  158. if($iOwnerId == 0)
  159. $iOwnerId = bx_get_logged_profile_id();
  160. $sValue = $oPrivacy->_oDb->getDefaultGroupByUser($sModule, $sAction, $iOwnerId);
  161. if(empty($sValue))
  162. $sValue = $oPrivacy->_oDb->getDefaultGroup($sModule, $sAction);
  163. $aValues = $oPrivacy->getGroups($iOwnerId, $aParams);
  164. $aValues = $oPrivacy->addDynamicGroups($aValues, $iOwnerId, $aParams);
  165. if(getParam('sys_enable_post_to_context_for_privacy') == 'on')
  166. $aValues = $oPrivacy->addSpaces($aValues, $iOwnerId, $aParams);
  167. $sName = $oPrivacy->convertActionToField($sAction);
  168. $sTitle = isset($aParams['title']) && !empty($aParams['title']) ? $aParams['title'] : '';
  169. if(empty($sTitle)) {
  170. $sTitle = $oPrivacy->_oDb->getTitle($sModule, $sAction);
  171. $sTitle = _t(!empty($sTitle) ? $sTitle : '_' . $sName);
  172. }
  173. $bDynamicMode = (isset($aParams['dynamic_mode']) && (bool)$aParams['dynamic_mode'] === true) || bx_is_dynamic_request();
  174. return array(
  175. 'type' => 'select',
  176. 'name' => $sName,
  177. 'caption' => $sTitle,
  178. 'value' => $sValue,
  179. 'values' => $aValues,
  180. 'attrs' => array(
  181. 'class' => 'sys-privacy-group',
  182. 'onchange' => 'javascript: ' . $oPrivacy->getJsObjectName() . '.selectGroup(this);'
  183. ),
  184. 'checker' => array(
  185. 'func' => 'avail',
  186. 'error' => _t('_sys_ps_ferr_incorrect_select')
  187. ),
  188. 'db' => array(
  189. 'pass' => 'Int'
  190. ),
  191. 'content' => $oPrivacy->addCssJs($bDynamicMode)
  192. );
  193. }
  194. public static function initGroupChooser($sObject, $iOwnerId = 0, $aParams = array())
  195. {
  196. $sResult = '';
  197. $oPrivacy = BxDolPrivacy::getObjectInstance($sObject);
  198. if(empty($oPrivacy))
  199. return $sResult;
  200. $iOwnerId = !empty($iOwnerId) ? (int)$iOwnerId : bx_get_logged_profile_id();
  201. $iContentId = !empty($aParams['content_id']) ? (int)$aParams['content_id'] : 0;
  202. $iGroupId = !empty($aParams['group_id']) ? (int)$aParams['group_id'] : 0;
  203. $bDynamicMode = (isset($aParams['dynamic_mode']) && (bool)$aParams['dynamic_mode'] === true) || bx_is_dynamic_request();
  204. $sJsCodeAdd = '';
  205. if($oPrivacy->isGroupsCustom())
  206. $sJsCodeAdd = $oPrivacy->getLoadGroupCustom($iOwnerId, $iContentId, $iGroupId, isset($aParams['html_ids']) ? $aParams['html_ids'] : array());
  207. return $oPrivacy->getJsScript($sJsCodeAdd, $bDynamicMode);
  208. }
  209. public static function initGroupChooserAPI($sObject, $iOwnerId = 0, $aParams = [])
  210. {
  211. $sResult = '';
  212. $oPrivacy = BxDolPrivacy::getObjectInstance($sObject);
  213. if(empty($oPrivacy))
  214. return $sResult;
  215. $iOwnerId = !empty($iOwnerId) ? (int)$iOwnerId : bx_get_logged_profile_id();
  216. $iContentId = !empty($aParams['content_id']) ? (int)$aParams['content_id'] : 0;
  217. $iGroupId = !empty($aParams['group_id']) ? (int)$aParams['group_id'] : 0;
  218. if($oPrivacy->isGroupsCustom() && ($aGroupsSettings = $oPrivacy->getGroupSettings($iGroupId)) !== false) {
  219. $aGroupCustom = $oPrivacy->getGroupCustom([
  220. 'type' => 'pcog_ext',
  221. 'profile_id' => $iOwnerId,
  222. 'content_id' => $iContentId,
  223. 'object' => $sObject,
  224. 'group_id' => $iGroupId,
  225. 'group_items_table' => $aGroupsSettings['db_table_items'],
  226. 'group_items_field' => $aGroupsSettings['db_field_item']
  227. ]);
  228. if(is_array($aGroupCustom) && !empty($aGroupCustom['items']) && is_array($aGroupCustom['items']))
  229. $sResult = implode(',', $aGroupCustom['items']);
  230. }
  231. return $sResult;
  232. }
  233. public static function getIcon($iVisibility)
  234. {
  235. $aIcons =array(
  236. BX_DOL_PG_MEONLY => 'lock',
  237. BX_DOL_PG_ALL => 'globe',
  238. BX_DOL_PG_FRIENDS => 'user-friends',
  239. BX_DOL_PG_FRIENDS => 'user-friends'
  240. );
  241. return isset($aIcons[$iVisibility]) ? $aIcons[$iVisibility] : 'eye';
  242. }
  243. public function actionLoadGroupCustom()
  244. {
  245. $iProfileId = (int)bx_get('profile_id');
  246. $iContentId = (int)bx_get('content_id');
  247. $iGroupId = (int)bx_get('group_id');
  248. $oForm = BxDolForm::getObjectInstance($this->_sFormGroupCustom, $this->_sFormDisplayGcMembers);
  249. return echoJson(array('eval' => $this->getJsObjectName() . '.onSelectGroup(oData);', 'content' => $oForm->getElementGroupCustom(array(
  250. 'profile_id' => $iProfileId,
  251. 'content_id' => $iContentId,
  252. 'object' => $this->_sObject,
  253. 'group_id' => $iGroupId
  254. ))));
  255. }
  256. public function actionSelectMembers()
  257. {
  258. $aValues = array(
  259. 'profile_id' => (int)bx_get('profile_id'),
  260. 'content_id' => (int)bx_get('content_id'),
  261. 'object' => $this->_sObject,
  262. 'group_id' => (int)bx_get('group_id')
  263. );
  264. $aParams = array(
  265. 'popup_only' => (bool)bx_get('popup_only')
  266. );
  267. if(!isset($this->_aGroupsSettings[$aValues['group_id']]))
  268. return echoJson(array());
  269. if($aValues['profile_id'] != bx_get_logged_profile_id())
  270. return echoJson(array('msg' => _t('_sys_ps_ferr_incorrect_gc_owner')));
  271. if(($mixedResult = $this->isSelectGroupCustomUsers($aValues)) !== true)
  272. return echoJson(array('msg' => $mixedResult));
  273. return echoJson($this->getSelectGroup($aValues, $aParams));
  274. }
  275. public function actionSelectMemberships()
  276. {
  277. $aValues = array(
  278. 'profile_id' => (int)bx_get('profile_id'),
  279. 'content_id' => (int)bx_get('content_id'),
  280. 'object' => $this->_sObject,
  281. 'group_id' => (int)bx_get('group_id')
  282. );
  283. $aParams = array(
  284. 'popup_only' => (bool)bx_get('popup_only')
  285. );
  286. if(!isset($this->_aGroupsSettings[$aValues['group_id']]))
  287. return echoJson(array());
  288. if($aValues['profile_id'] != bx_get_logged_profile_id())
  289. return echoJson(array('msg' => _t('_sys_ps_ferr_incorrect_gc_owner')));
  290. if(($mixedResult = $this->isSelectGroupCustomMemberships($aValues)) !== true)
  291. return echoJson(array('msg' => $mixedResult));
  292. return echoJson($this->getSelectMemberships($aValues, $aParams));
  293. }
  294. public function actionUsersList()
  295. {
  296. $iGroup = (int)bx_get('group');
  297. if(!isset($this->_aGroupsSettings[$iGroup]))
  298. return echoJson(array());
  299. $oConnection = BxDolConnection::getObjectInstance($this->_aGroupsSettings[$iGroup]['connection']);
  300. if(!$oConnection)
  301. return echoJson(array());
  302. $iProfileId = bx_get_logged_profile_id();
  303. $aConnectedIds = $oConnection->getConnectedContent($iProfileId, true);
  304. if(empty($aConnectedIds) || !is_array($aConnectedIds))
  305. return echoJson(array());
  306. $sTerm = bx_get('term');
  307. $aProfiles = BxDolService::call('system', 'profiles_search', array($sTerm), 'TemplServiceProfiles');
  308. if(empty($aProfiles))
  309. return echoJson(array());
  310. $aResult = array();
  311. foreach($aProfiles as $aProfile)
  312. if(in_array($aProfile['value'], $aConnectedIds))
  313. $aResult[] = $aProfile;
  314. echoJson($aResult);
  315. }
  316. public function getGroupSettings($iGroup)
  317. {
  318. if(empty($this->_aGroupsSettings[$iGroup]) || !is_array($this->_aGroupsSettings[$iGroup]))
  319. return false;
  320. return $this->_aGroupsSettings[$iGroup];
  321. }
  322. public function isGroupsCustom()
  323. {
  324. $aGroups = $this->_oDb->getGroupsBy(array('type' => 'active_list'));
  325. return in_array(BX_DOL_PG_FRIENDS_SELECTED, $aGroups) || in_array(BX_DOL_PG_RELATIONS_SELECTED, $aGroups) || in_array(BX_DOL_PG_MEMBERSHIPS_SELECTED, $aGroups);
  326. }
  327. public function getGroupsBy($aParams)
  328. {
  329. return $this->_oDb->getGroupsBy($aParams);
  330. }
  331. public function getGroupCustom($aParams)
  332. {
  333. return $this->_oDb->getGroupCustom($aParams);
  334. }
  335. /**
  336. * Checks if this privacy object allows to add content in specified context.
  337. *
  338. * @param type $sContext - context module name
  339. * @return boolean
  340. */
  341. public function isAllowedAddInContext($sContext)
  342. {
  343. $bSpaces = !empty($this->_aObject['spaces']);
  344. if($bSpaces && $this->_aObject['spaces'] == 'all')
  345. return true;
  346. if($bSpaces && in_array($sContext, explode(',', $this->_aObject['spaces'])))
  347. return true;
  348. return false;
  349. }
  350. /**
  351. * Note. Is used when privacy group is processing after the main form submit.
  352. */
  353. public function addGroupCustom($aGroup, $aItems = [])
  354. {
  355. $this->deleteGroupCustom([
  356. 'profile_id' => $aGroup['profile_id'],
  357. 'content_id' => $aGroup['content_id'],
  358. 'object' => $aGroup['object']
  359. ]);
  360. if(empty($aItems) || !is_array($aItems))
  361. return 0;
  362. $iGroupId = (int)$aGroup['group_id'];
  363. $iGroupCustomId = $this->_oDb->insertGroupCustom($aGroup);
  364. if($iGroupCustomId !== false && !empty($aItems) && is_array($aItems)) {
  365. $bMembers = $iGroupId == BX_DOL_PG_FRIENDS_SELECTED || $iGroupId == BX_DOL_PG_RELATIONS_SELECTED;
  366. $bMemberships = $iGroupId == BX_DOL_PG_MEMBERSHIPS_SELECTED;
  367. if(($bMembers || $bMemberships) && ($sType = $bMembers ? 'member' : ($bMemberships ? 'membership' : '')) !== '')
  368. foreach($aItems as $iItem)
  369. $this->_oDb->{'insertGroupCustom' . ucfirst($sType)}(['group_id' => $iGroupCustomId, $sType . '_id' => $iItem]);
  370. }
  371. return $iGroupCustomId;
  372. }
  373. /**
  374. * Note. Is used when privacy group is processing after the main form submit.
  375. */
  376. public function editGroupCustom($aGroup, $aItems = [])
  377. {
  378. $aGroupCustom = $this->getGroupCustom([
  379. 'type' => 'pco',
  380. 'profile_id' => $aGroup['profile_id'],
  381. 'content_id' => $aGroup['content_id'],
  382. 'object' => $this->_sObject
  383. ]);
  384. if(empty($aGroupCustom) || !is_array($aGroupCustom))
  385. return $this->addGroupCustom($aGroup, $aItems);
  386. $iGroupId = (int)$aGroupCustom['group_id'];
  387. $iGroupCustomId = (int)$aGroupCustom['id'];
  388. if($iGroupId != (int)$aGroup['group_id'])
  389. return $this->addGroupCustom($aGroup, $aItems);
  390. $bMembers = $iGroupId == BX_DOL_PG_FRIENDS_SELECTED || $iGroupId == BX_DOL_PG_RELATIONS_SELECTED;
  391. $bMemberships = $iGroupId == BX_DOL_PG_MEMBERSHIPS_SELECTED;
  392. if(($bMembers || $bMemberships) && ($sType = $bMembers ? 'member' : ($bMemberships ? 'membership' : '')) !== '') {
  393. $sTypeUc = ucfirst($sType);
  394. $this->_oDb->{'deleteGroupCustom' . $sTypeUc}(['group_id' => $iGroupCustomId]);
  395. if(!empty($aItems) && is_array($aItems))
  396. foreach($aItems as $iItemId)
  397. $this->_oDb->{'insertGroupCustom' . $sTypeUc}(['group_id' => $iGroupCustomId, $sType . '_id' => $iItemId]);
  398. }
  399. return $iGroupCustomId;
  400. }
  401. public function updateGroupCustom($aParamsSet, $aParamsWhere)
  402. {
  403. return $this->_oDb->updateGroupCustom($aParamsSet, $aParamsWhere);
  404. }
  405. public function deleteGroupCustom($aParamsWhere)
  406. {
  407. return $this->_oDb->deleteGroupCustom($aParamsWhere);
  408. }
  409. public function associateGroupCustomWithContent($iProfileId, $iContentId, $iGroupId)
  410. {
  411. return $this->updateGroupCustom(array('content_id' => $iContentId), array(
  412. 'profile_id' => $iProfileId,
  413. 'content_id' => 0,
  414. 'object' => $this->_sObject,
  415. 'group_id' => $iGroupId
  416. ));
  417. }
  418. public function reassociateGroupCustomWithContent($iProfileId, $iContentId, $iGroupId)
  419. {
  420. $aGroupCustom = $this->getGroupCustom(array(
  421. 'type' => 'pco',
  422. 'profile_id' => $iProfileId,
  423. 'content_id' => $iContentId,
  424. 'object' => $this->_sObject
  425. ));
  426. if(!empty($aGroupCustom) && is_array($aGroupCustom) && $aGroupCustom['group_id'] != $iGroupId) {
  427. $this->deleteGroupCustom(array('id' => $aGroupCustom['id']));
  428. $this->associateGroupCustomWithContent($iProfileId, $iContentId, $iGroupId);
  429. }
  430. }
  431. public function deleteGroupCustomByContentId($iContentId)
  432. {
  433. return $this->_oDb->deleteGroupCustom(array('content_id' => $iContentId, 'object' => $this->_sObject));
  434. }
  435. public function deleteGroupCustomByProfileId($iProfileId, $mixedContentId = false)
  436. {
  437. $aParams = ['type' => 'profile_id', 'profile_id' => $iProfileId];
  438. if($mixedContentId !== false)
  439. $aParams['content_id'] = (int)$mixedContentId;
  440. $aGroups = $this->_oDb->getGroupCustom($aParams);
  441. if(empty($aGroups) || !is_array($aGroups))
  442. return true;
  443. foreach($aGroups as $aGroup)
  444. $this->_oDb->deleteGroupCustom(array('id' => $aGroup['id']));
  445. return true;
  446. }
  447. public function deleteGroupCustomByProfileIdUnused($iProfileId)
  448. {
  449. return $this->deleteGroupCustomByProfileId($iProfileId, 0);
  450. }
  451. public function addDynamicGroups($aValues, $iOwnerId, $aParams)
  452. {
  453. if (isset($aParams['dynamic_groups']) && is_array($aParams['dynamic_groups']))
  454. $aValues = array_merge($aValues, $aParams['dynamic_groups']);
  455. return $aValues;
  456. }
  457. public function addSpaces($aValues, $iOwnerId, $aParams)
  458. {
  459. if (!$this->_aObject['spaces'])
  460. return $aValues;
  461. if (!($oProfile = BxDolProfile::getInstance($iOwnerId)))
  462. return $aValues;
  463. if (!($aModules = BxDolModuleQuery::getInstance()->getModules()))
  464. return $aValues;
  465. $aExcludeModules = explode(',', getParam('sys_hide_post_to_context_for_privacy'));
  466. foreach ($aModules as $aModule) {
  467. if (!$aModule['enabled'])
  468. continue;
  469. if ('all' != $this->_aObject['spaces'] && false === stripos($this->_aObject['spaces'], $aModule['name']))
  470. continue;
  471. if (!BxDolRequest::serviceExists($aModule['name'], 'act_as_profile'))
  472. continue;
  473. if (in_array($aModule['name'], $aExcludeModules))
  474. continue;
  475. $oModule = BxDolModule::getInstance($aModule['name']);
  476. if (bx_srv($aModule['name'], 'act_as_profile') && (!isset($oModule->_oConfig->CNF['ALLOW_AS_CONTEXT']) || $oModule->_oConfig->CNF['ALLOW_AS_CONTEXT'] == false))
  477. continue;
  478. $a = BxDolService::call($aModule['name'], 'get_participating_profiles', array($oProfile->id()));
  479. // for an organization we should treat the organization profile itself as a participant of itself
  480. // to be able to post into an organization's context while being logged as that organization
  481. if ($aModule['name'] == 'bx_organizations' && $oProfile->getModule() == 'bx_organizations') $a = array_merge($a, [$oProfile->id()]);
  482. $aSpaces = array();
  483. foreach ($a as $iProfileId) {
  484. if (!($o = BxDolProfile::getInstance($iProfileId)))
  485. continue;
  486. // check whether a profile is allowed to post this type of content to a context
  487. $oConnectedProfile = BxDolProfile::getInstance($iProfileId);
  488. if (bx_srv($aModule['name'], 'check_allowed_post_in_profile', array($oConnectedProfile->getContentId(), $this->_aObject['module'])) !== CHECK_ACTION_RESULT_ALLOWED)
  489. continue;
  490. $aSpaces[-$iProfileId] = array('key' => -$iProfileId, 'value' => $o->getDisplayName());
  491. }
  492. if ($aSpaces) {
  493. $aItemStart = array(array('type' => 'group_header', 'value' => mb_strtoupper(BxDolService::call($aModule['name'], 'get_space_title'))));
  494. $aItemEnd = array(array('type' => 'group_end'));
  495. $aValues = array_merge($aValues, $aItemStart, array_values($aSpaces), $aItemEnd);
  496. }
  497. }
  498. return $aValues;
  499. }
  500. /**
  501. * Get database field name for action.
  502. *
  503. * @param string $sObject privacy object name.
  504. * @param string $sAction action name.
  505. * @return string with field name.
  506. */
  507. public static function getFieldName($sObject, $sAction = '')
  508. {
  509. $oPrivacy = BxDolPrivacy::getObjectInstance($sObject);
  510. if(empty($oPrivacy))
  511. return '';
  512. if(empty($sAction))
  513. $sAction = $oPrivacy->_aObject['action'];
  514. return $oPrivacy->convertActionToField($sAction);
  515. }
  516. /**
  517. * Get necessary condition array to use privacy in search classes
  518. * @param $mixedGroupId group ID or array of group IDs
  519. * @return array of conditions, for now with 'restriction' part only is returned
  520. */
  521. public function getContentByGroupAsCondition($mixedGroupId)
  522. {
  523. $aResult = array(
  524. 'restriction' => array (
  525. 'privacy_' . $this->_sObject => array(
  526. 'value' => $mixedGroupId,
  527. 'field' => $this->convertActionToField($this->_aObject['action']),
  528. 'operator' => is_array($mixedGroupId) ? 'in' : '=',
  529. 'table' => $this->_aObject['table'],
  530. ),
  531. ),
  532. );
  533. /**
  534. * @hooks
  535. * @hookdef hook-system-privacy_condition 'system', 'privacy_condition' - hook on get necessary condition array to use privacy in search classes
  536. * - $unit_name - equals `profile`
  537. * - $action - equals `privacy_condition`
  538. * - $object_id - not used
  539. * - $sender_id - not used
  540. * - $extra_params - array of additional params with the following array keys:
  541. * - `group_id` - [int] group ID or array of group IDs
  542. * - `field` - [string] field name
  543. * - `object` - [array] array with object privacy
  544. * - `privacy_object` - [array] object privacy
  545. * - `result` - [bool] by ref, on success return true otherwise false, can be overridden in hook processing
  546. * @hook @ref hook-system-privacy_condition
  547. */
  548. bx_alert('system', 'privacy_condition', 0, false, array(
  549. 'group_id' => $mixedGroupId,
  550. 'field' => $this->convertActionToField($this->_aObject['action']),
  551. 'object' => $this->_aObject,
  552. 'privacy_object' => $this,
  553. 'result' => &$aResult
  554. )
  555. );
  556. return $aResult;
  557. }
  558. /**
  559. * Get necessary condition array to use privacy in search classes
  560. * @param $iProfileIdOwner owner profile ID
  561. * @return array of conditions, for now with 'restriction' part only is returned
  562. */
  563. public function getContentPublicAsCondition($iProfileIdOwner = 0, $aCustomGroups = array())
  564. {
  565. $mixedPrivacyGroups = $this->getPrivacyGroupsForContentPublic($iProfileIdOwner, $aCustomGroups);
  566. if($mixedPrivacyGroups === true)
  567. return array();
  568. return $this->getContentByGroupAsCondition($mixedPrivacyGroups);
  569. }
  570. /**
  571. * Get necessary parts of SQL query to use privacy in other queries
  572. * @param $mixedGroupId group ID or array of group IDs
  573. * @return array of SQL string parts, for now 'where' part only is returned
  574. */
  575. public function getContentByGroupAsSQLPart($mixedGroupId)
  576. {
  577. $sField = $this->convertActionToField($this->_aObject['action']);
  578. return $this->_oDb->getContentByGroupAsSQLPart($sField, $mixedGroupId);
  579. }
  580. /**
  581. * Get necessary parts of SQL query to use privacy in other queries
  582. * @param $iProfileIdOwner owner profile ID
  583. * @return array of SQL string parts, for now 'where' part only is returned
  584. */
  585. public function getContentPublicAsSQLPart($iProfileIdOwner = 0, $aCustomGroups = array())
  586. {
  587. $mixedPrivacyGroups = $this->getPrivacyGroupsForContentPublic($iProfileIdOwner, $aCustomGroups);
  588. if($mixedPrivacyGroups === true)
  589. return array();
  590. return $this->getContentByGroupAsSQLPart($mixedPrivacyGroups);
  591. }
  592. /**
  593. * Get necessary parts of SQL query to use privacy in other queries
  594. * @param $iProfileIdOwner owner profile ID
  595. * @return array of SQL string parts, for now 'where' part only is returned
  596. */
  597. public function getContentPublicAndInContextAsSQLPart($iProfileIdOwner = 0, $aCustomGroups = [], $aCustomContexts = [])
  598. {
  599. $mixedPrivacyGroups = $this->getPrivacyGroupsForContentPublic($iProfileIdOwner, $aCustomGroups);
  600. if($mixedPrivacyGroups === true)
  601. return [];
  602. $sField = $this->convertActionToField($this->_aObject['action']);
  603. return $this->_oDb->getContentByGroupAndContextAsSQLPart($sField, $mixedPrivacyGroups, $aCustomContexts);
  604. }
  605. /**
  606. * Check whether the viewer can make requested action.
  607. *
  608. * @param integer $iObjectId object ID the action to be performed with.
  609. * @param integer $iViewerId viewer ID.
  610. * @return boolean result of operation.
  611. */
  612. public function check($iObjectId, $iViewerId = 0)
  613. {
  614. $aObject = $this->getObjectInfo($this->convertActionToField($this->_aObject['action']), $iObjectId);
  615. $bRv = $this->_check($iObjectId, $iViewerId, $aObject);
  616. /**
  617. * @hooks
  618. * @hookdef hook-system-check_privacy 'system', 'check_privacy' - check privacy for object
  619. * - $unit_name - equals `profile`
  620. * - $action - equals `check_privacy`
  621. * - $object_id - not used
  622. * - $sender_id - not used
  623. * - $extra_params - array of additional params with the following array keys:
  624. * - `object_id` - [int] object id
  625. * - `viewer_id` - [int] profile_id for viewer
  626. * - `object` - [array] object privacy
  627. * - `object_privacy` - [array] arry with object privacy
  628. * - `result` - [bool] by ref, on success return true otherwise false, can be overridden in hook processing
  629. * @hook @ref hook-system-check_privacy
  630. */
  631. bx_alert('system', 'check_privacy', 0, 0, array(
  632. 'object_id' => $iObjectId,
  633. 'viewer_id' => $iViewerId,
  634. 'object' => $aObject,
  635. 'object_privacy' => $this->_aObject,
  636. 'result' => &$bRv
  637. ));
  638. return $bRv;
  639. }
  640. public function checkSpace($aObject, $iViewerId)
  641. {
  642. $oProfile = BxDolProfile::getInstance(-(int)$aObject['group_id']);
  643. if (!$oProfile)
  644. return false;
  645. return CHECK_ACTION_RESULT_ALLOWED === bx_srv($oProfile->getModule(), 'check_space_privacy_for_profile', [$oProfile->getContentId(), $iViewerId]);
  646. }
  647. public function checkMeOnly($iOwnerId, $iViewerId)
  648. {
  649. return false;
  650. }
  651. public function checkPublic($iOwnerId, $iViewerId)
  652. {
  653. return true;
  654. }
  655. public function checkMembers($iOwnerId, $iViewerId)
  656. {
  657. return isMember();
  658. }
  659. public function checkFriends($iOwnerId, $iViewerId)
  660. {
  661. return BxDolConnection::getObjectInstance('sys_profiles_friends')->isConnected($iOwnerId, $iViewerId, true);
  662. }
  663. public function checkFriendsSelectedByObject($aObject, $iViewerId)
  664. {
  665. if(!$this->checkFriends($aObject['owner_id'], $iViewerId))
  666. return false;
  667. $aGroupCustom = $this->getGroupCustom(array(
  668. 'type' => 'pcog_ext',
  669. 'profile_id' => $aObject['owner_id'],
  670. 'content_id' => $aObject['id'],
  671. 'object' => $this->_sObject,
  672. 'group_id' => $aObject['group_id'],
  673. 'group_items_table' => $this->_aGroupsSettings[$aObject['group_id']]['db_table_items'],
  674. 'group_items_field' => $this->_aGroupsSettings[$aObject['group_id']]['db_field_item']
  675. ));
  676. return !empty($aGroupCustom['items']) && is_array($aGroupCustom['items']) && in_array($iViewerId, $aGroupCustom['items']);
  677. }
  678. public function checkRelations($iOwnerId, $iViewerId)
  679. {
  680. return BxDolConnection::getObjectInstance('sys_profiles_relations')->isConnected($iOwnerId, $iViewerId, true);
  681. }
  682. public function checkRelationsSelectedByObject($aObject, $iViewerId)
  683. {
  684. if(!$this->checkRelations($aObject['owner_id'], $iViewerId))
  685. return false;
  686. $aGroupCustom = $this->getGroupCustom(array(
  687. 'type' => 'pcog_ext',
  688. 'profile_id' => $aObject['owner_id'],
  689. 'content_id' => $aObject['id'],
  690. 'object' => $this->_sObject,
  691. 'group_id' => $aObject['group_id'],
  692. 'group_items_table' => $this->_aGroupsSettings[$aObject['group_id']]['db_table_items'],
  693. 'group_items_field' => $this->_aGroupsSettings[$aObject['group_id']]['db_field_item']
  694. ));
  695. return !empty($aGroupCustom['items']) && is_array($aGroupCustom['items']) && in_array($iViewerId, $aGroupCustom['items']);
  696. }
  697. public function checkMembershipsSelectedByObject($aObject, $iViewerId)
  698. {
  699. $aGroupCustom = $this->getGroupCustom(array(
  700. 'type' => 'pcog_ext',
  701. 'profile_id' => $aObject['owner_id'],
  702. 'content_id' => $aObject['id'],
  703. 'object' => $this->_sObject,
  704. 'group_id' => $aObject['group_id'],
  705. 'group_items_table' => $this->_aGroupsSettings[$aObject['group_id']]['db_table_items'],
  706. 'group_items_field' => $this->_aGroupsSettings[$aObject['group_id']]['db_field_item']
  707. ));
  708. return !empty($aGroupCustom['items']) && is_array($aGroupCustom['items']) && BxDolAcl::getInstance()->isMemberLevelInSet($aGroupCustom['items'], $iViewerId);
  709. }
  710. public function checkCustomByObject($aObject, $iViewerId)
  711. {
  712. return '';
  713. }
  714. public function setTableFieldAuthor($sValue)
  715. {
  716. $this->_aObject['table_field_author'] = $sValue;
  717. $this->_oDb->init($this->_aObject);
  718. }
  719. protected function getObjectInfo($sAction, $iObjectId)
  720. {
  721. return $this->_oDb->getObjectInfo($sAction, $iObjectId);
  722. }
  723. protected function getPrivacyGroupsForContentPublic($iProfileIdOwner = 0, $aCustomGroups = array())
  724. {
  725. $aGroups = array(BX_DOL_PG_ALL);
  726. if(isLogged()) {
  727. $iProfileIdLogged = bx_get_logged_profile_id();
  728. if($iProfileIdLogged == $iProfileIdOwner)
  729. return true;
  730. $aGroups[] = BX_DOL_PG_MEMBERS;
  731. if($iProfileIdOwner && $this->checkFriends($iProfileIdOwner, $iProfileIdLogged))
  732. $aGroups[] = BX_DOL_PG_FRIENDS;
  733. }
  734. return array_merge($aGroups, $aCustomGroups);
  735. }
  736. protected function getCheckMethod($s)
  737. {
  738. if(substr($s, 0, 1) != '@')
  739. return false;
  740. return bx_gen_method_name(str_replace('@', 'check_', $s));
  741. }
  742. protected function convertActionToField($sAction)
  743. {
  744. return 'allow_' . strtolower(str_replace(' ', '-', $sAction)) . '_to';
  745. }
  746. /**
  747. * Check whethere viewer is a member of dynamic group.
  748. *
  749. * @param mixed $mixedGroupId dynamic group ID.
  750. * @param integer $iObjectOwnerId object owner ID.
  751. * @param integer $iViewerId viewer ID.
  752. * @return boolean result of operation.
  753. */
  754. protected function isDynamicGroupMember($mixedGroupId, $iObjectOwnerId, $iViewerId, $iObjectId)
  755. {
  756. return false;
  757. }
  758. /**
  759. * get privacy groups for getGroupChooser
  760. */
  761. protected function getGroups($iOwnerId = 0, $aParams = [])
  762. {
  763. $aValues = array();
  764. $aGroups = $this->_oDb->getGroupsBy(array('type' => 'active'));
  765. foreach($aGroups as $aGroup) {
  766. $iGroupId = (int)$aGroup['id'];
  767. if((int)$aGroup['active'] == 0 || in_array($iGroupId, $this->_aGroupsExclude))
  768. continue;
  769. if(isset($this->_aGroupsSettings[$iGroupId]) && !empty($this->_aGroupsSettings[$iGroupId]['is_allowed'])) {
  770. $sMethodIsAllowed = $this->_aGroupsSettings[$iGroupId]['is_allowed'];
  771. if(method_exists($this, $sMethodIsAllowed) && $this->$sMethodIsAllowed($iOwnerId) !== true)
  772. continue;
  773. }
  774. $aValues[] = array('key' => $aGroup['id'], 'value' => _t($aGroup['title']));
  775. }
  776. return $aValues;
  777. }
  778. /**
  779. * Check whethere a custom group (based on users list) is allowed in current circumstances.
  780. * NOTE. Can be overwritten if it's needed.
  781. *
  782. * @param type $aParams an array of parameters.
  783. * @return boolean result of operation.
  784. */
  785. protected function isSelectGroupCustomUsers($aParams)
  786. {
  787. return true;
  788. }
  789. /**
  790. * Check whethere a custom group (based on memberships list) is allowed in current circumstances.
  791. * NOTE. Can be overwritten if it's needed.
  792. *
  793. * @param type $aParams an array of parameters.
  794. * @return boolean result of operation.
  795. */
  796. protected function isSelectGroupCustomMemberships($aParams)
  797. {
  798. return true;
  799. }
  800. public function isAllowedRelations($iUserId)
  801. {
  802. return BxDolConnectionRelation::isEnabled();
  803. }
  804. public function isAllowedMemberships($iUserId)
  805. {
  806. $aCheck = checkActionModule($iUserId, 'show membership levels in privacy groups', 'system', false);
  807. return $aCheck[CHECK_ACTION_RESULT] === CHECK_ACTION_RESULT_ALLOWED;
  808. }
  809. /**
  810. * Check whether the viewer can make requested action.
  811. *
  812. * @param integer $iObjectId object ID the action to be performed with.
  813. * @param integer $iViewerId viewer ID.
  814. * @return boolean result of operation.
  815. */
  816. protected function _check($iObjectId, $iViewerId, $aObject)
  817. {
  818. if(empty($iViewerId))
  819. $iViewerId = (int)bx_get_logged_profile_id();
  820. if(empty($aObject) || !is_array($aObject))
  821. return false;
  822. if($aObject['group_id'] == BX_DOL_PG_HIDDEN)
  823. return false;
  824. $aViewer = BxDolProfileQuery::getInstance()->getInfoById($iViewerId);
  825. if(($aViewer && isAdmin($aViewer['account_id'])) || $iViewerId == $aObject['owner_id'])
  826. return true;
  827. if(strncmp($aObject['group_id'], 'ml', 2) === 0) {
  828. $iLevel = (int)substr($aObject['group_id'], 2);
  829. return (bool)BxDolAcl::getInstance()->isMemberLevelInSet(array($iLevel), $iViewerId);
  830. }
  831. // A space (context) is stored as its negative profile id. Compare numerically: under
  832. // PHP 8 an empty or other non-numeric value compares as a string, so '' < 0 is true.
  833. if(is_numeric($aObject['group_id']) && (int)$aObject['group_id'] < 0)
  834. return $this->checkSpace($aObject, $iViewerId);
  835. $aGroup = $this->_oDb->getGroupsBy(array('type' => 'id', 'id' => $aObject['group_id']));
  836. if(!empty($aGroup) && is_array($aGroup) && (int)$aGroup['active'] == 1 && !empty($aGroup['check'])) {
  837. $sCheckMethod = $this->getCheckMethod($aGroup['check']);
  838. if(method_exists($this, $sCheckMethod) && $this->$sCheckMethod((substr($sCheckMethod, -8) == 'ByObject' ? $aObject : $aObject['owner_id']), $iViewerId))
  839. return true;
  840. }
  841. return $this->isDynamicGroupMember($aObject['group_id'], $aObject['owner_id'], $iViewerId, $iObjectId);
  842. }
  843. }
  844. /** @} */