PageRenderTime 52ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/core/users/class.user.group.keyring.php

http://buddypress-media.googlecode.com/
PHP | 771 lines | 371 code | 223 blank | 177 comment | 73 complexity | 923d3cde735ba961a7e8ec22b98c2e12 MD5 | raw file
Possible License(s): AGPL-1.0, Apache-2.0, GPL-2.0, LGPL-2.1
  1. <?php
  2. /**
  3. * BP-MEDIA USER GROUP KEYRING
  4. * This class determines which keys are granted to a user when they become a member of a group.
  5. *
  6. * @version 0.1.9
  7. * @since 0.1.9
  8. * @package BP-Media
  9. * @subpackage Group Keys
  10. * @license GPL v2.0
  11. * @link http://code.google.com/p/buddypress-media/
  12. *
  13. * ========================================================================================================
  14. */
  15. class BPM_uGroupKeyRing extends BPM_db_base {
  16. var $cache; // Main cache array for this class
  17. // ============================================================================================================ //
  18. // DB table names and structures are hard-coded into the class. This allows class methods to be
  19. // fired from an AJAX call, without loading the entire BP stack.
  20. public static $struct = array(
  21. "table" => "bpm_sys_user_groupkeyring",
  22. "engine" => "InnoDB", // Required for transactions
  23. "columns" => array(
  24. "group_id" => array( "php"=>"int", "sql"=>"smallint", "format"=>"%d", "width"=>null, "flags"=>"UNSIGNED NOT NULL", "auto_inc"=>false, "default"=>null,
  25. // This forces every group_id + key_id combination to be unique
  26. "index"=>array("name"=>"groupid_keyid", "col"=>array("group_id", "key_id"), "index"=>"PRIMARY"), "this_row"=>true),
  27. "key_id" => array( "php"=>"int", "sql"=>"smallint", "format"=>"%d", "width"=>null, "flags"=>"UNSIGNED NOT NULL", "auto_inc"=>false, "default"=>null, "index"=>true)
  28. )
  29. );
  30. // PHP allows this: $foo = new $class_name; $result = $foo::$struct; but does not allow this: $result = $class_name::$struct;
  31. // or this: $result = $class_name::get_struct(); ...so we have to do this: $result = call_user_func( array($class_name,'_struct') );
  32. public static function _struct() {
  33. return self::$struct;
  34. }
  35. // ================================================================================================================
  36. public function BPM_uGroupKeyRing(){
  37. self::loadCache();
  38. }
  39. /**
  40. * Clears the persistent cache for the class.
  41. *
  42. * @version 0.1.9
  43. * @since 0.1.9
  44. *
  45. * @return bool | False on failure. True on success.
  46. */
  47. public function flushCache() {
  48. global $bpm;
  49. $this->cache = array();
  50. $result = $bpm->cache->flushNamespace("BPM_uGroupKeyRing");
  51. return $result;
  52. }
  53. /**
  54. * Loads the class cache array from the persistent cache.
  55. *
  56. * @version 0.1.9
  57. * @since 0.1.9
  58. *
  59. * @return bool | False on failure. True on success.
  60. */
  61. public function loadCache(){
  62. global $bpm;
  63. if( $bpm->cache->isActive() ){
  64. $cache_image = $bpm->cache->get("BPM_uGroupKeyRing", "cache");
  65. // Handle loading from a completely empty cache
  66. if($cache_image){
  67. $this->cache = $cache_image;
  68. return true;
  69. }
  70. else {
  71. $this->cache = array();
  72. return false;
  73. }
  74. }
  75. else {
  76. if(!$this->cache){
  77. $this->cache = array();
  78. }
  79. return true;
  80. }
  81. }
  82. /**
  83. * Saves the class cache array to the persistent cache.
  84. *
  85. * @version 0.1.9
  86. * @since 0.1.9
  87. *
  88. * @return bool | False on failure. True on success.
  89. */
  90. public function saveCache(){
  91. global $bpm;
  92. $result = $bpm->cache->set("BPM_uGroupKeyRing", "cache", $this->cache);
  93. return $result;
  94. }
  95. /**
  96. * Loads group keys into the cache.
  97. *
  98. * @version 0.1.9
  99. * @since 0.1.9
  100. *
  101. * @param int/array $group_id | Single group id as int. Multiple group id's as array of int.
  102. * @param int/array $key_id | Single key id as int. Multiple key ids as array of int.
  103. * @param bool $skip_load | If set true, the function will not update the class cache array from
  104. * the persistent cache before adding data from a db call and saving it
  105. * back to the persistent cache.
  106. * @return bool | False on failure. True on success.
  107. */
  108. public function load($group_id, $key_id=null, $skip_load=false){
  109. global $bpm;
  110. $db = new BPM_db();
  111. $args = array();
  112. $args = array(
  113. array("col"=>"group_id", "op"=>"=", "val"=>$group_id)
  114. );
  115. if($key_id){
  116. $args[] = array("col"=>"key_id", "op"=>"=", "val"=>$key_id);
  117. }
  118. $columns = array("mode"=>"include", "col"=>array("group_id", "key_id") );
  119. $ctrl = array("format"=>"array_key_array_true", "key_col"=>array("group_id", "key_id") );
  120. $db_result = $db->runSelectQuery(self::$struct, $args, $columns, $ctrl);
  121. // Load the class cache array from the persistent cache
  122. if(!$skip_load){
  123. self::loadCache();
  124. }
  125. if($db_result){
  126. // When the function is called with specific keys to look for, if a group doesn't
  127. // have that key, we can add that information to the cache. This saves a query
  128. // the next time the key is requested.
  129. if($key_id){
  130. if( !is_array($group_id) ){
  131. $group_id = array($group_id);
  132. }
  133. if( !is_array($key_id) ){
  134. $key_id = array($key_id);
  135. }
  136. foreach( $group_id as $group ){
  137. foreach( $key_id as $key ){
  138. if( BPM_sUtil::keyExists($key, $db_result[$group]) ){
  139. $this->cache["keys"][$group][$key] = true;
  140. }
  141. else {
  142. $this->cache["keys"][$group][$key] = false;
  143. }
  144. }
  145. unset($key);
  146. }
  147. unset($group);
  148. }
  149. else {
  150. // If the function is called with only a group_id, all of the keys that
  151. // group has will be added to the cache. But we cannot add information
  152. // about what keys a group *doesn't* have to the list, because we don't
  153. // have a list of keys to check against.
  154. foreach( $db_result as $group => $keys){
  155. // Flag the entire group as cached
  156. $this->cache["groups"][$group] = true;
  157. foreach( $keys as $key => $fake_var){ // $fake_var is needed because we're operating on
  158. // key names, not key values
  159. $this->cache["keys"][$group][$key] = true;
  160. }
  161. unset($key);
  162. }
  163. unset($group, $keys);
  164. }
  165. // Write the updated local cache array to the persistent cache
  166. $cache_ok = self::saveCache();
  167. return $cache_ok;
  168. }
  169. else {
  170. return false;
  171. }
  172. }
  173. /**
  174. * Checks if a group grants a specific key
  175. *
  176. * @version 0.1.9
  177. * @since 0.1.9
  178. *
  179. * @param int $group_id | id of the group
  180. * @param int $key_id | id of the key
  181. * @return bool | True if group has key. False if group does not have key.
  182. */
  183. public function hasKey($group_id, $key_id){
  184. if( BPM_sUtil::keyExists($key_id, $this->cache["keys"][$group_id]) ){
  185. return $this->cache["keys"][$group_id][$key_id];
  186. }
  187. else {
  188. $this->load($group_id, $key_id);
  189. return $this->cache["keys"][$group_id][$key_id];
  190. }
  191. }
  192. /**
  193. * Gets one or more groups' keyrings. If multiple groups are specified, the groups
  194. * individual keyrings are merged, eliminating duplicate keys
  195. *
  196. * @version 0.1.9
  197. * @since 0.1.9
  198. *
  199. * @param int $group_id | Group id as int. Multiple groups as array of int.
  200. * @return bool/array | False on failure. Array of key id's on success.
  201. */
  202. public function getKeys($group_id){
  203. global $bpm;
  204. $result = array();
  205. if( !is_array($group_id) ){
  206. if( !(is_array($this->cache["groups"]) && array_key_exists($group_id, $this->cache["groups"]) ) ){
  207. $this->load($group_id);
  208. }
  209. $keyring = $this->cache["keys"][$group_id];
  210. // Remove all of the "negative" (group doesn't have the key)
  211. // cache results, and format as numeric-keyed array
  212. if($keyring){
  213. foreach($keyring as $key_id => $has_key){
  214. if($has_key){
  215. $result[] = $key_id;
  216. }
  217. }
  218. unset($key_id, $has_key);
  219. }
  220. return $result;
  221. }
  222. else {
  223. // If a group doesn't already have all its keys loaded into the cache,
  224. // load them into the cache
  225. $missing_groups = array();
  226. foreach($group_id as $group){
  227. if( !(is_array($this->cache["groups"]) && array_key_exists($group, $this->cache["groups"])) ){
  228. $missing_groups[] = $group;
  229. }
  230. }
  231. unset($group);
  232. if( count($missing_groups) > 0 ){
  233. $this->load($missing_groups);
  234. }
  235. // Merge the groups' keyrings. There is no PHP array function that will take
  236. // two or more numeric-keyed arrays and merge them into a single array containing
  237. // their combined keys with no duplicates. If is possible to merge with duplicates
  238. // then use array_unique() to remove them, but it would be an N^2 process on an
  239. // unsorted aray. The algorithm below is probably faster.
  240. $combined_keyring = array();
  241. foreach($group_id as $group){
  242. $keyring = $this->cache["keys"][$group];
  243. // Remove all of the "negative" (group doesn't have the key)
  244. // cache results, and format as numeric-keyed array
  245. if($keyring){
  246. foreach($keyring as $key_id => $has_key){
  247. if($has_key){
  248. $combined_keyring[$key_id] = true;
  249. }
  250. }
  251. unset($key_id, $has_key);
  252. }
  253. }
  254. unset($group, $keyring);
  255. $result = array_keys($combined_keyring);
  256. return $result;
  257. }
  258. }
  259. /**
  260. * Adds one or more keys to one or more groups' keyrings
  261. *
  262. * @version 0.1.9
  263. * @since 0.1.9
  264. *
  265. * @param int $group_id | id of the group as int. Multiple groups as array of int.
  266. * @param int/array $key_id | id of the key as int. Multiple keys as array of int.
  267. * @return bool | False on failure. True on success.
  268. */
  269. public function addKey($group_id, $key_id){
  270. global $bpm;
  271. $db = new BPM_db();
  272. // Force a load of the entire class cache from the persistent cache
  273. self::loadCache();
  274. // CASE 1: Single group_id, single key
  275. // =================================================================
  276. if( !is_array($group_id) && !is_array($key_id) ){
  277. // If the group already has the key, return true to indicate no db rows were changed
  278. if( self::hasKey($group_id, $key_id) ){
  279. return true;
  280. }
  281. else{
  282. $data = array("group_id"=>$group_id, "key_id"=>$key_id);
  283. $result = $db->runInsertQuery(self::$struct, $data, $columns=null);
  284. if($result){
  285. // Update the cache
  286. $cache_ok = self::saveCache();
  287. if($cache_ok){
  288. return $result;
  289. }
  290. else {
  291. return false;
  292. }
  293. }
  294. else {
  295. return $result;
  296. }
  297. }
  298. }
  299. // CASE 2: Single group_id, multiple keys
  300. // =================================================================
  301. if( !is_array($group_id) && is_array($key_id) ){
  302. $keys_to_add = array();
  303. // Create an array of "to be added" keys the user doesn't have. Note this algorithm
  304. // also eliminates duplicate keys, so we don't have to use array_unique() to avoid
  305. // a failed query if duplicate keys are present in $key_id
  306. foreach( $key_id as $key ){
  307. if( !self::hasKey($group_id, $key) ){
  308. $keys_to_add[] = $key;
  309. }
  310. }
  311. unset($key);
  312. // Check that there are actually keys to add, because running an insert query
  313. // with an empty data array will cause an SQL error
  314. if( count($keys_to_add) > 0){
  315. // Add the new keys to the database
  316. $data = array();
  317. foreach( $keys_to_add as $key ) {
  318. $data[] = array("group_id"=>$group_id, "key_id"=>$key );
  319. }
  320. unset($key);
  321. $result = $db->runInsertQueryMulti(self::$struct, $data, $columns=null);
  322. // Update the cache
  323. if($result){
  324. foreach( $keys_to_add as $key ) {
  325. $this->cache["keys"][$group_id][$key] = true;
  326. }
  327. unset($key);
  328. $cache_ok = self::saveCache();
  329. if($cache_ok){
  330. return $result;
  331. }
  332. else {
  333. return false;
  334. }
  335. }
  336. else {
  337. return $result;
  338. }
  339. }
  340. else {
  341. return true;
  342. }
  343. }
  344. // CASE 3: Multiple group_id's, single key
  345. // =================================================================
  346. if( is_array($group_id) && !is_array($key_id) ){
  347. $groups_to_add = array();
  348. // Create an array of groups to be given the key that don't have the key yet
  349. foreach( $group_id as $group ){
  350. if( !self::hasKey($group, $key_id) ){
  351. $groups_to_add[] = $group;
  352. }
  353. }
  354. unset($group);
  355. // Check that there are actually keys to add, because running an insert query
  356. // with an empty data array will cause an SQL error
  357. if( count($groups_to_add) > 0 ){
  358. // Add the new keys to the database
  359. $data = array();
  360. foreach( $groups_to_add as $group) {
  361. $data[] = array("group_id"=>$group, "key_id"=>$key_id);
  362. }
  363. unset($group);
  364. $result = $db->runInsertQueryMulti(self::$struct, $data, $columns=null);
  365. // Update the cache
  366. if($result){
  367. foreach( $groups_to_add as $group ) {
  368. $this->cache["keys"][$group][$key_id] = true;
  369. }
  370. unset($group);
  371. $cache_ok = self::saveCache();
  372. if($cache_ok){
  373. return $result;
  374. }
  375. else {
  376. return false;
  377. }
  378. }
  379. else {
  380. return $result;
  381. }
  382. }
  383. else {
  384. return true;
  385. }
  386. }
  387. // CASE 4: Multiple group_id's, multiple keys (all groups get same keys)
  388. // =================================================================
  389. if( is_array($group_id) && is_array($key_id) ){
  390. // Create an array of "missing keys" that need to be added. Note this algorithm
  391. // also eliminates duplicate keys, so we don't have to use array_unique() to avoid
  392. // a failed query if duplicate keys are present in $key_id
  393. $keys_to_add = array();
  394. foreach( $group_id as $group ){
  395. foreach( $key_id as $key){
  396. if( !self::hasKey($group, $key) ){
  397. $keys_to_add[$group][] = $key;
  398. }
  399. }
  400. }
  401. unset($group, $key);
  402. // Check that there are actually keys to add, because running an insert query
  403. // with an empty data array will cause an SQL error
  404. if( count($keys_to_add) > 0 ){
  405. // Add the new keys to the database
  406. $data = array();
  407. foreach( $keys_to_add as $group => $keys ) {
  408. foreach( $keys as $key ) {
  409. $data[] = array("group_id"=>$group, "key_id"=>$key );
  410. }
  411. }
  412. unset($group, $key, $keys);
  413. $result = $db->runInsertQueryMulti(self::$struct, $data, $columns=null);
  414. // Update the cache
  415. if($result){
  416. foreach( $keys_to_add as $group => $keys ) {
  417. foreach( $keys as $key ) {
  418. $this->cache["keys"][$group][$key] = true;
  419. }
  420. }
  421. unset($group, $key, $keys);
  422. $cache_ok = self::saveCache();
  423. if($cache_ok){
  424. return $result;
  425. }
  426. else {
  427. return false;
  428. }
  429. }
  430. else {
  431. return $result;
  432. }
  433. }
  434. else {
  435. return true;
  436. } // ENDOF: if( count($keys_to_add) > 0 )
  437. } // ENDOF: if( is_array($group_id) && is_array($key_id) )
  438. } // ENDOF: function addKey()
  439. /**
  440. * Removes one or more keys from one or more group's keyrings
  441. *
  442. * @version 0.1.9
  443. * @since 0.1.9
  444. *
  445. * @param int $group_id | id of the group as int. Multiple groups as array of int.
  446. * @param int/array $key_id | id of the key as int. Multiple keys as array of int.
  447. * @return int | Number of keys affected.
  448. */
  449. public function dropKey($group_id, $key_id) {
  450. global $bpm;
  451. $db = new BPM_db();
  452. // Drop the keys from the db
  453. $args = array(
  454. array("col"=>"group_id", "op"=>"=", "val"=>$group_id),
  455. array("col"=>"key_id", "op"=>"=", "val"=>$key_id)
  456. );
  457. $result = $db->runDeleteQuery(self::$struct, $args);
  458. // Update the cache
  459. if($result){
  460. if(!is_array($group_id)){
  461. $group_id = array($group_id);
  462. }
  463. if(!is_array($key_id)){
  464. $key_id = array($key_id);
  465. }
  466. foreach($group_id as $group) {
  467. foreach($key_id as $key) {
  468. unset($this->cache["keys"][$group][$key]);
  469. }
  470. }
  471. unset($group, $key);
  472. $cache_ok = self::saveCache();
  473. if($cache_ok){
  474. return $result;
  475. }
  476. else {
  477. return false;
  478. }
  479. }
  480. else {
  481. return $result;
  482. }
  483. }
  484. /**
  485. * Deletes the entire keyring for one or more groups
  486. *
  487. * @version 0.1.9
  488. * @since 0.1.9
  489. *
  490. * @param int $group_id | id of the group as int. Multiple groups as array of int.
  491. * @return int | Number of keys affected.
  492. */
  493. public function dropGroup($group_id) {
  494. global $bpm;
  495. $db = new BPM_db();
  496. // Drop the keys from the db
  497. $args = array(
  498. array("col"=>"group_id", "op"=>"=", "val"=>$group_id)
  499. );
  500. $result = $db->runDeleteQuery(self::$struct, $args);
  501. // Update the cache
  502. if($result){
  503. if(!is_array($group_id)){
  504. $group_id = array($group_id);
  505. }
  506. foreach($group_id as $group) {
  507. unset($this->cache["keys"][$group]);
  508. unset($this->cache["groups"][$group]);
  509. }
  510. unset($group);
  511. $cache_ok = self::saveCache();
  512. if($cache_ok){
  513. return $result;
  514. }
  515. else {
  516. return false;
  517. }
  518. }
  519. else {
  520. return $result;
  521. }
  522. }
  523. } // End of class BPM_uGroupKey
  524. /**
  525. * Hooks on the plugin's install function, creates database tables and
  526. * configuration options for the class.
  527. *
  528. * @version 0.1.9
  529. * @since 0.1.9
  530. */
  531. function install_BPM_uGroupKeyRing(){
  532. $cls = new BPM_uGroupKeyRing();
  533. $cls->install();
  534. }
  535. add_action( 'bpm_install', 'install_BPM_uGroupKeyRing', 2 );
  536. /**
  537. * Hooks on the plugin's uninstall function. Removes all database tables and
  538. * configuration options for the class.
  539. *
  540. * @version 0.1.9
  541. * @since 0.1.9
  542. */
  543. function uninstall_BPM_uGroupKeyRing(){
  544. $cls = new BPM_uGroupKeyRing();
  545. $cls->uninstall();
  546. }
  547. add_action( 'bpm_uninstall', 'uninstall_BPM_uGroupKeyRing', 2 );
  548. ?>