/**
  * Test fixture for dropMulti() method, matrix mode, hot cache
  *
  * @version 1.0
  * @since 1.0
  * 
  * =======================================================================================
  */
 public function test_dropMulti_matrix_HOT()
 {
     self::loadData();
     // Load the cache
     // ####################################################################
     $request = array('A' => array(), 'B' => array(), 'C' => array());
     $valid = false;
     try {
         $result = $this->cls->getMulti($request, $ctrl, $valid);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(1));
     }
     // HOT CACHE - All items in cache have authority from previous GET operation
     // ===================================================================
     // Drop objects
     // ####################################################################
     $data = array(array("L3" => 'A', "L2" => "X", "L1" => 1), array("L3" => 'A', "L2" => "Y"), array("L3" => 'B'), array("L3" => 'C'));
     $ctrl = array('validate' => true, 'mode' => 'matrix', 'trap_*' => true);
     try {
         $rows_changed = $this->cls->dropMulti($data, $ctrl);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(1));
     }
     // Should return (int)1 to indicate 18 rows were dropped
     $this->assertEquals(16, $rows_changed);
     // Verify db state
     // ####################################################################
     $db = new FOX_db();
     $columns = null;
     $args = null;
     $ctrl = array('format' => 'array_key_array', 'key_col' => array('L3', 'L2', 'L1'));
     try {
         $result = $db->runSelectQuery($this->cls->_struct(), $args, $columns, $ctrl);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(1));
     }
     // NOTE: the datastore will automatically clip empty branches
     $check = array('A' => array('X' => array(2 => false, 3 => (int) 0, 5 => true)));
     $this->assertEquals($check, $result);
     // Check cache state
     // ####################################################################
     // Since we're working with a hot cache, the all_cached flag will be set for all
     // nodes that already exist in the database. The L2 and L3 LUT's for these
     // nodes will be missing, because the all_cached flag takes priority.
     // PASS 1: Check the L4 nodes individually to simplify debugging
     // ====================================================================
     $check_cache_A = array('all_cached' => true, 'L2' => null, 'keys' => array('X' => array(2 => false, 3 => (int) 0, 5 => true)));
     $this->assertEquals($check_cache_A, $this->cls->cache['A']);
     // PASS 2: Combine the L4 nodes into a single array and check it
     // again. This finds L4 keys that aren't supposed to be there.
     // ====================================================================
     $check_cache = array('A' => $check_cache_A);
     $this->assertEquals($check_cache, $this->cls->cache);
     // Verify persistent cache state by reading-back all items
     // ####################################################################
     $request = array('A' => array(), 'B' => array(), 'C' => array());
     $valid = false;
     try {
         $result = $this->cls->getMulti($request, $ctrl, $valid);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(1));
     }
     $this->assertEquals(false, $valid);
     // Should report invalid because
     // the '2' and '3' L4's don't exist
     $this->assertEquals($check, $result);
 }
 /**
  * Loads the class instance with the test data set, and verifies it was correctly written
  * to the database and cache
  *
  * @version 1.0
  * @since 1.0
  * 
  * =======================================================================================
  */
 public function loadData()
 {
     $test_obj = new stdClass();
     $test_obj->foo = "11";
     $test_obj->bar = "test_Bar";
     $test_data = array(array("L4" => 'A', "L3" => "X", "L2" => "K", "L1" => 1, "L0" => null), array("L4" => 'A', "L3" => "X", "L2" => "K", "L1" => 2, "L0" => false), array("L4" => 'A', "L3" => "X", "L2" => "K", "L1" => 5, "L0" => true), array("L4" => 'A', "L3" => "X", "L2" => "Z", "L1" => 3, "L0" => (int) 0), array("L4" => 'A', "L3" => "Y", "L2" => "K", "L1" => 1, "L0" => (int) 1), array("L4" => 'A', "L3" => "Y", "L2" => "K", "L1" => 2, "L0" => (int) -1), array("L4" => 'A', "L3" => "Y", "L2" => "K", "L1" => 3, "L0" => (double) 1.7), array("L4" => 'A', "L3" => "Y", "L2" => "Z", "L1" => 4, "L0" => (double) -1.6), array("L4" => 'B', "L3" => "X", "L2" => "K", "L1" => 1, "L0" => (string) "foo"), array("L4" => 'B', "L3" => "X", "L2" => "K", "L1" => 2, "L0" => array(null, true, false, 1, 1.0, "foo")), array("L4" => 'B', "L3" => "X", "L2" => "Z", "L1" => 3, "L0" => $test_obj));
     // Load class with data
     // ===============================================================
     try {
         $rows_changed = $this->cls->setL1_multi($test_data, $ctrl = null);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(array('depth' => 1, 'data' => true)));
     }
     // Should return (int)11 to indicate  11 keys were added
     $this->assertEquals(11, $rows_changed);
     // Check cache state
     // ===============================================================
     // NOTE: the LUT's won't be set at this point, because we haven't done any
     // database reads that give objects authority
     $check = array('A' => array('keys' => array('X' => array('K' => array(1 => null, 2 => false, 5 => true), 'Z' => array(3 => (int) 0)), 'Y' => array('K' => array(1 => (int) 1, 2 => (int) -1, 3 => (double) 1.7), 'Z' => array(4 => (double) -1.6)))), 'B' => array('keys' => array('X' => array('K' => array(1 => (string) "foo", 2 => array(null, true, false, 1, 1.0, "foo")), 'Z' => array(3 => $test_obj)))));
     $this->assertEquals($check, $this->cls->cache);
     // Check db state
     // ===============================================================
     $check = array('A' => array('X' => array('K' => array(1 => null, 2 => false, 5 => true), 'Z' => array(3 => (int) 0)), 'Y' => array('K' => array(1 => (int) 1, 2 => (int) -1, 3 => (double) 1.7), 'Z' => array(4 => (double) -1.6))), 'B' => array('X' => array('K' => array(1 => (string) "foo", 2 => array(null, true, false, 1, 1.0, "foo")), 'Z' => array(3 => $test_obj))));
     $db = new FOX_db();
     $columns = null;
     $args = null;
     $ctrl = array('format' => 'array_key_array', 'key_col' => array('L4', 'L3', 'L2', 'L1'));
     try {
         $result = $db->runSelectQuery($this->cls->_struct(), $args, $columns, $ctrl);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(1));
     }
     $this->assertEquals($check, $result);
 }
 /**
  * Test fixture for dropGlobal() method, L4, multi item
  *
  * @version 1.0
  * @since 1.0
  * 
  * =======================================================================================
  */
 public function test_dropGlobal_L4_multi()
 {
     self::loadData();
     // Drop objects
     // ####################################################################
     $drop_ctrl = array("validate" => true);
     try {
         $rows_changed = $this->cls->dropGlobal(4, array('X', 'Y'), $drop_ctrl);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(1));
     }
     // Should report 19 rows were dropped
     $this->assertEquals(19, $rows_changed);
     // Verify db state
     // ####################################################################
     $db = new FOX_db();
     $columns = null;
     $args = null;
     $ctrl = array('format' => 'array_key_array', 'key_col' => array('L5', 'L4', 'L3', 'L2', 'L1'));
     try {
         $result = $db->runSelectQuery($this->cls->_struct(), $args, $columns, $ctrl);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(1));
     }
     $this->assertEquals(null, $result);
     // Check class cache state
     // ####################################################################
     $check_cache = array();
     $this->assertEquals($check_cache, $this->cls->cache);
     // Verify persistent cache state by reading-back all items
     // ####################################################################
     $request = array(1 => array(), 2 => array(), 3 => array());
     $valid = false;
     try {
         $result = $this->cls->getMulti($request, $ctrl, $valid);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(1));
     }
     $this->assertEquals(false, $valid);
     // Should report invalid because
     // a requested L5 doesn't exist
     $this->assertEquals(array(), $result);
 }
 /**
  * Test fixture for processHTMLForm() method, data integrity
  *
  * @version 1.0
  * @since 1.0
  * 
  * =======================================================================================
  */
 public function test_processHTMLForm_dataIntegrity()
 {
     self::loadData();
     // Missing key names string
     // ===============================================================
     $check = array('plugin_1^X^K^N' => true, 'plugin_1^X^K^N2' => false, 'plugin_1^X^Z^N3' => 2, 'plugin_1^Y^Z^N4' => -1.6);
     try {
         $result = $this->cls->processHTMLForm($check);
         // Execution will halt on the previous line if processHTMLForm() throws an exception
         $this->fail("Method processHTMLForm() failed to throw an exception on missing key names parameter");
     } catch (FOX_exception $child) {
     }
     // Invalid plugin name
     // ===============================================================
     $check = array('key_names' => '1^X^K^N1,plugin_1^X^K^N2,plugin_1^X^Z^N3,plugin_1^Y^Z^N4', 'plugin_1^X^K^N' => true, 'plugin_1^X^K^N2' => false, 'plugin_1^X^Z^N3' => 2, 'plugin_1^Y^Z^N4' => -1.6);
     try {
         $result = $this->cls->processHTMLForm($check);
         // Execution will halt on the previous line if processHTMLForm() throws an exception
         $this->fail("Method processHTMLForm() failed to throw an exception on invalid plugin name");
     } catch (FOX_exception $child) {
     }
     // Invalid tree name
     // ===============================================================
     $check = array('key_names' => 'plugin_1^1^K^N1,plugin_1^X^K^N2,plugin_1^X^Z^N3,plugin_1^Y^Z^N4', 'plugin_1^X^K^N' => true, 'plugin_1^X^K^N2' => false, 'plugin_1^X^Z^N3' => 2, 'plugin_1^Y^Z^N4' => -1.6);
     try {
         $result = $this->cls->processHTMLForm($check);
         // Execution will halt on the previous line if processHTMLForm() throws an exception
         $this->fail("Method processHTMLForm() failed to throw an exception on invalid tree name");
     } catch (FOX_exception $child) {
     }
     // Invalid branch name
     // ===============================================================
     $check = array('key_names' => 'plugin_1^X^1^N1,plugin_1^X^K^N2,plugin_1^X^Z^N3,plugin_1^Y^Z^N4', 'plugin_1^X^K^N' => true, 'plugin_1^X^K^N2' => false, 'plugin_1^X^Z^N3' => 2, 'plugin_1^Y^Z^N4' => -1.6);
     try {
         $result = $this->cls->processHTMLForm($check);
         // Execution will halt on the previous line if processHTMLForm() throws an exception
         $this->fail("Method processHTMLForm() failed to throw an exception on invalid branch name");
     } catch (FOX_exception $child) {
     }
     // Invalid node name
     // ===============================================================
     $check = array('key_names' => 'plugin_1^X^K^1,plugin_1^X^K^N2,plugin_1^X^Z^N3,plugin_1^Y^Z^N4', 'plugin_1^X^K^N' => true, 'plugin_1^X^K^N2' => false, 'plugin_1^X^Z^N3' => 2, 'plugin_1^Y^Z^N4' => -1.6);
     try {
         $result = $this->cls->processHTMLForm($check);
         // Execution will halt on the previous line if processHTMLForm() throws an exception
         $this->fail("Method processHTMLForm() failed to throw an exception on invalid node name");
     } catch (FOX_exception $child) {
     }
     // Nonexistent node
     // ===============================================================
     $check = array('key_names' => 'plugin_1^X^K^N6,plugin_1^X^K^N2,plugin_1^X^Z^N3,plugin_1^Y^Z^N4', 'plugin_1^X^K^N' => true, 'plugin_1^X^K^N2' => false, 'plugin_1^X^Z^N3' => 2, 'plugin_1^Y^Z^N4' => -1.6);
     try {
         $result = $this->cls->processHTMLForm($check);
         // Execution will halt on the previous line if processHTMLForm() throws an exception
         $this->fail("Method processHTMLForm() failed to throw an exception on nonexistent node");
     } catch (FOX_exception $child) {
     }
     // Check db state
     // ===============================================================
     $test_obj = new stdClass();
     $test_obj->foo = "11";
     $test_obj->bar = "test_Bar";
     $check = array("plugin_1" => array('X' => array('K' => array('N1' => array('filter' => 'debug', 'filter_ctrl' => false, 'val' => null), 'N2' => array('filter' => 'debug', 'filter_ctrl' => false, 'val' => false), 'N5' => array('filter' => 'debug', 'filter_ctrl' => false, 'val' => true)), 'Z' => array('N3' => array('filter' => 'debug', 'filter_ctrl' => false, 'val' => (int) 0))), 'Y' => array('K' => array('N1' => array('filter' => 'debug', 'filter_ctrl' => false, 'val' => (int) 1), 'N2' => array('filter' => 'debug', 'filter_ctrl' => false, 'val' => (int) -1), 'N3' => array('filter' => 'debug', 'filter_ctrl' => false, 'val' => (double) 1.7)), 'Z' => array('N4' => array('filter' => 'debug', 'filter_ctrl' => false, 'val' => (double) -1.6)))), "plugin_2" => array('X' => array('K' => array('N1' => array('filter' => 'debug', 'filter_ctrl' => false, 'val' => (string) "foo"), 'N2' => array('filter' => 'debug', 'filter_ctrl' => false, 'val' => array(null, true, false, 1, 1.0, "foo"))), 'Z' => array('N3' => array('filter' => 'debug', 'filter_ctrl' => false, 'val' => $test_obj)))));
     $db = new FOX_db();
     $columns = null;
     $ctrl = array('format' => 'array_key_array', 'key_col' => array('plugin', 'tree', 'branch', 'node'));
     try {
         $struct = $this->cls->_struct();
         $result = $db->runSelectQuery($struct, $args = null, $columns, $ctrl);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(1));
     }
     $this->assertEquals($check, $result);
 }
 /**
  * Fetches the type_id of a given type_slug, for one or more slugs.
  *
  * @version 1.0
  * @since 1.0
  * @param int $module_id | Module id that owns this object type.
  * @param string $type_slug | Single slug as string. Multiple slugs as array of strings.
  * @return bool/array | Exception on failure. False on nonexistent. Array "type_slug"=>"type_id" on success.
  */
 public function slugToTypeId($module_id, $type_slug)
 {
     $db = new FOX_db();
     if (!is_array($type_slug)) {
         $type_slug = array($type_slug);
     }
     // Load as many id's as possible from the cache
     // ============================================
     $result = array();
     $missing_slugs = array();
     $persistent_cache_loaded = false;
     foreach ($type_slug as $slug) {
         // If a requested slug is in the class cache, add its level_id to the the results array
         if (FOX_sUtil::keyExists($slug, $this->cache["slug_to_type_id"][$module_id])) {
             $result[$slug] = $this->cache["slug_to_type_id"][$module_id][$slug];
         } elseif (!$persistent_cache_loaded) {
             try {
                 self::loadCache();
                 $persistent_cache_loaded = true;
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 1, 'text' => "Cache read error", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
             }
             if (FOX_sUtil::keyExists($slug, $this->cache["slug_to_type_id"][$module_id])) {
                 $result[$slug] = $this->cache["slug_to_type_id"][$module_id][$slug];
             } else {
                 $missing_slugs[] = $slug;
             }
         } else {
             $missing_slugs[] = $slug;
         }
     }
     unset($slug);
     // Fetch any missing "type_slug"-"type_id" pairs from the db
     // ===========================================================
     if (count($missing_slugs) > 0) {
         $args = array(array("col" => "module_id", "op" => "=", "val" => $module_id), array("col" => "type_slug", "op" => "=", "val" => $missing_slugs));
         $columns = array("mode" => "include", "col" => array("type_slug", "type_id"));
         $ctrl = array("format" => "array_key_single", "key_col" => "type_slug", "val_col" => "type_id");
         try {
             $db_result = $db->runSelectQuery($this->_struct(), $args, $columns, $ctrl);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 2, 'text' => "Error reading from database", 'data' => array('args' => $args, 'columns' => $columns, 'ctrl' => $ctrl), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
         }
         if ($db_result) {
             // The if($db_result) prevents array_merge()
             // foreach() from crashing on an empty db result
             $result = array_merge($result, $db_result);
             // Update the cache
             // ==============================
             try {
                 $cache_image = self::readCache();
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 3, 'text' => "Cache read error", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
             }
             // Rebuild the cache image
             foreach ($db_result as $slug => $type_id) {
                 $cache_image["slug_to_type_id"][$slug] = $type_id;
             }
             unset($slug, $type_id);
             try {
                 self::writeCache($cache_image);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 4, 'text' => "Cache write error", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
             }
         }
     }
     // Overwrite the class cache
     $this->cache = $cache_image;
     if (count($result) > 0) {
         return $result;
     } else {
         return false;
     }
 }
 function test_dropId_Multi()
 {
     // load db
     // ======================================================
     $add_tokens = array("one", "two", "three", "four", "five");
     try {
         $add_result = $this->cls->addToken($add_tokens);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(array('depth' => 50, 'data' => true)));
     }
     // Test  single dropId
     // ======================================================
     try {
         $drop_ids = array(1, 2, 3, 4, 5);
         $this->assertEquals(5, $this->cls->dropId($drop_ids));
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(array('depth' => 50, 'data' => true)));
     }
     // Check Db has no rows
     // ======================================================
     try {
         $db = new FOX_db();
         $this->assertEquals(false, $db->runSelectQuery(FOX_test_dictionary::$struct));
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(array('depth' => 50, 'data' => true)));
     }
     // Check Class cache is empty
     $this->assertEquals(array('ids' => array(), 'tokens' => array()), $this->cls->cache);
 }
 /**
  * Test fixture for addMulti() method (matrix mode)
  *
  * @version 1.0
  * @since 1.0
  * 
  * =======================================================================================
  */
 function test_addMulti_matrix()
 {
     $test_obj = new stdClass();
     $test_obj->foo = "11";
     $test_obj->bar = "test_Bar";
     $test_data = array(array('L4' => 'A', 'L3' => 'X', 'L2' => 'K', 'L1' => 1, 'L0' => null), array('L4' => 'A', 'L3' => 'X', 'L2' => 'K', 'L1' => 2, 'L0' => false), array('L4' => 'A', 'L3' => 'X', 'L2' => 'K', 'L1' => 5, 'L0' => true), array('L4' => 'A', 'L3' => 'X', 'L2' => 'Z', 'L1' => 3, 'L0' => (int) 0), array('L4' => 'A', 'L3' => 'Y', 'L2' => 'K', 'L1' => 1, 'L0' => (int) 1), array('L4' => 'A', 'L3' => 'Y', 'L2' => 'K', 'L1' => 2, 'L0' => (int) -1), array('L4' => 'A', 'L3' => 'Y', 'L2' => 'K', 'L1' => 3, 'L0' => (double) 1.7), array('L4' => 'A', 'L3' => 'Y', 'L2' => 'Z', 'L1' => 4, 'L0' => (double) -1.6), array('L4' => 'B', 'L3' => 'X', 'L2' => 'K', 'L1' => 1, 'L0' => (string) "foo"), array('L4' => 'B', 'L3' => 'X', 'L2' => 'K', 'L1' => 2, 'L0' => array(null, true, false, 1, 1.0, "foo")), array('L4' => 'B', 'L3' => 'X', 'L2' => 'Z', 'L1' => 3, 'L0' => $test_obj));
     // Load class with data
     // ===============================================================
     try {
         $ctrl = array('mode' => 'matrix');
         $set_ok = $this->cls->addMulti($test_data, $ctrl);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(array('depth' => 50, 'data' => true)));
     }
     // Should return number of L1's added
     $this->assertEquals(11, $set_ok);
     // Test adding some  duplicate itemes
     // ===============================================================
     try {
         $dupe_data = array(array('L4' => 'A', 'L3' => 'X', 'L2' => 'K', 'L1' => 1, 'L0' => null), array('L4' => 'A', 'L3' => 'X', 'L2' => 'K', 'L1' => 2, 'L0' => false), array('L4' => 'A', 'L3' => 'X', 'L2' => 'K', 'L1' => 1, 'L0' => true), array('L4' => 'A', 'L3' => 'X', 'L2' => 'Z', 'L1' => 3, 'L0' => (int) 0), array('L4' => 'A', 'L3' => 'Y', 'L2' => 'K', 'L1' => 1, 'L0' => (int) 1), array('L4' => 'A', 'L3' => 'Y', 'L2' => 'K', 'L1' => 2, 'L0' => (int) -1), array('L4' => 'A', 'L3' => 'Y', 'L2' => 'K', 'L1' => 3, 'L0' => (double) 1.7), array('L4' => 'A', 'L3' => 'Y', 'L2' => 'Z', 'L1' => 4, 'L0' => (double) -1.6));
         $ctrl = array('mode' => 'matrix');
         $this->cls->addMulti($dupe_data, $ctrl);
         // Execution will halt on the previous line if addMulti() throws an exception
         $this->fail("Method addMulti() failed to throw an exception on duplicate entry");
     } catch (FOX_exception $child) {
     }
     // Check cache state
     // ===============================================================
     // NOTE: the LUT's won't be set at this point, because we haven't done any
     // database reads that give objects authority
     $check = array('A' => array('keys' => array('X' => array('K' => array(1 => null, 2 => false, 5 => true), 'Z' => array(3 => (int) 0)), 'Y' => array('K' => array(1 => (int) 1, 2 => (int) -1, 3 => (double) 1.7), 'Z' => array(4 => (double) -1.6)))), 'B' => array('keys' => array('X' => array('K' => array(1 => (string) "foo", 2 => array(null, true, false, 1, 1.0, "foo")), 'Z' => array(3 => $test_obj)))));
     $this->assertEquals($check, $this->cls->cache);
     // Check db state
     // ===============================================================
     $check = array('A' => array('X' => array('K' => array(1 => null, 2 => false, 5 => true), 'Z' => array(3 => (int) 0)), 'Y' => array('K' => array(1 => (int) 1, 2 => (int) -1, 3 => (double) 1.7), 'Z' => array(4 => (double) -1.6))), 'B' => array('X' => array('K' => array(1 => (string) "foo", 2 => array(null, true, false, 1, 1.0, "foo")), 'Z' => array(3 => $test_obj))));
     $db = new FOX_db();
     $columns = null;
     $ctrl = array('format' => 'array_key_array', 'key_col' => array('L4', 'L3', 'L2', 'L1'));
     try {
         $struct = $this->cls->_struct();
         $result = $db->runSelectQuery($struct, $args = null, $columns, $ctrl);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(1));
     }
     $this->assertEquals($check, $result);
 }
 function test_deleteItem_multi_withMedias()
 {
     // Clear the albums table, medias table, and caches
     // ===================================================
     $this->alb->truncate();
     $this->alb->flushCache();
     $this->med->truncate();
     $this->med->flushCache();
     // Load albums class with test data
     // ===================================================
     $test_data = array(array('date_created' => "2011-01-01 15:14:13", 'title' => "Test Title", 'caption' => "Test Caption", 'privacy' => 3, 'module_id' => 1), array('date_created' => "2011-01-01 15:14:14", 'title' => "Test Title", 'caption' => "Test Caption", 'privacy' => 2, 'module_id' => 1), array('date_created' => "2011-01-01 15:14:15", 'title' => "Test Title", 'caption' => "Test Caption", 'privacy' => 1, 'module_id' => 2));
     $result = $this->alb->addItemMulti($user_id = 1, $test_data, $error);
     $this->assertEquals(true, $result, FOX_debug::formatError_print($error));
     // Add media items to test albums
     // ===================================================
     $test_data = array('owner_id' => 1, 'album_id' => 1, 'title' => "Test Media Title 01", 'caption' => "Test Media Caption 01", 'date_created' => "2011-02-02 17:18:19", 'module_slug' => "slug_01");
     $result = $this->med->addItem($test_data, $media_object, $error);
     $this->assertEquals(1, $result, FOX_debug::formatError_print($error));
     $test_data = array('owner_id' => 1, 'album_id' => 1, 'title' => "Test Media Title 02", 'caption' => "Test Media Caption 02", 'date_created' => "2011-02-02 17:18:19", 'module_slug' => "slug_01");
     $result = $this->med->addItem($test_data, $media_object, $error);
     $this->assertEquals(2, $result, FOX_debug::formatError_print($error));
     $test_data = array('owner_id' => 1, 'album_id' => 1, 'title' => "Test Media Title 03", 'caption' => "Test Media Caption 03", 'date_created' => "2011-02-02 17:18:19", 'module_slug' => "slug_01");
     $result = $this->med->addItem($test_data, $media_object, $error);
     $this->assertEquals(3, $result, FOX_debug::formatError_print($error));
     $test_data = array('owner_id' => 1, 'album_id' => 2, 'title' => "Test Media Title 04", 'caption' => "Test Media Caption 04", 'date_created' => "2011-02-02 17:18:19", 'module_slug' => "slug_01");
     $result = $this->med->addItem($test_data, $media_object, $error);
     $this->assertEquals(4, $result, FOX_debug::formatError_print($error));
     $test_data = array('owner_id' => 1, 'album_id' => 2, 'title' => "Test Media Title 05", 'caption' => "Test Media Caption 05", 'date_created' => "2011-02-02 17:18:19", 'module_slug' => "slug_01");
     $result = $this->med->addItem($test_data, $media_object, $error);
     $this->assertEquals(5, $result, FOX_debug::formatError_print($error));
     $test_data = array('owner_id' => 1, 'album_id' => 2, 'title' => "Test Media Title 06", 'caption' => "Test Media Caption 06", 'date_created' => "2011-02-02 17:18:19", 'module_slug' => "slug_01");
     $result = $this->med->addItem($test_data, $media_object, $error);
     $this->assertEquals(6, $result, FOX_debug::formatError_print($error));
     $test_data = array('owner_id' => 1, 'album_id' => 3, 'title' => "Test Media Title 07", 'caption' => "Test Media Caption 07", 'date_created' => "2011-02-02 17:18:19", 'module_slug' => "slug_01");
     $result = $this->med->addItem($test_data, $media_object, $error);
     $this->assertEquals(7, $result, FOX_debug::formatError_print($error));
     $test_data = array('owner_id' => 1, 'album_id' => 3, 'title' => "Test Media Title 08", 'caption' => "Test Media Caption 08", 'date_created' => "2011-02-02 17:18:19", 'module_slug' => "slug_01");
     $result = $this->med->addItem($test_data, $media_object, $error);
     $this->assertEquals(8, $result, FOX_debug::formatError_print($error));
     $test_data = array('owner_id' => 1, 'album_id' => 3, 'title' => "Test Media Title 09", 'caption' => "Test Media Caption 09", 'date_created' => "2011-02-02 17:18:19", 'module_slug' => "slug_01");
     $result = $this->med->addItem($test_data, $media_object, $error);
     $this->assertEquals(9, $result, FOX_debug::formatError_print($error));
     // Delete albums
     // ===================================================
     $result = $this->alb->deleteItem($user_id = 1, $album_id = array(1, 3), $error);
     $this->assertEquals(2, $result, FOX_debug::formatError_print($error));
     // Verify albums were deleted
     // ===================================================
     $result = $this->alb->get(array(1, 2, 3));
     $check_array = array(new FOX_album(array("id" => 2, 'owner_id' => 1, 'cover_image' => 4, 'display_order' => array(4, 5, 6), 'date_created' => "2011-01-01 15:14:14", 'title' => "Test Title", 'caption' => "Test Caption", 'privacy' => 2, 'module_id' => 1)));
     $this->assertEquals($check_array, $result);
     // Verify medias were deleted
     // ===================================================
     $db = new FOX_db();
     $args = array(array("col" => "owner_id", "op" => "=", "val" => 1));
     $ctrl = array("format" => "array_array");
     $db_result = $db->runSelectQuery(FOX_media::_struct(), $args, $columns = null, $ctrl);
     $check_data = array(array("id" => 4, 'owner_id' => 1, 'album_id' => 2, 'date_created' => "2011-02-02 17:18:19", 'title' => "Test Media Title 04", 'caption' => "Test Media Caption 04", 'privacy' => 2, 'module_id' => 1, 'pixels_x' => 1000, 'pixels_y' => 2000, 'bytes_master' => 1111, 'module_id' => 1, 'master_id' => "ABC1111"), array("id" => 5, 'owner_id' => 1, 'album_id' => 2, 'date_created' => "2011-02-02 17:18:19", 'title' => "Test Media Title 05", 'caption' => "Test Media Caption 05", 'privacy' => 2, 'module_id' => 1, 'pixels_x' => 1000, 'pixels_y' => 2000, 'bytes_master' => 1111, 'module_id' => 1, 'master_id' => "ABC1111"), array("id" => 6, 'owner_id' => 1, 'album_id' => 2, 'date_created' => "2011-02-02 17:18:19", 'title' => "Test Media Title 06", 'caption' => "Test Media Caption 06", 'privacy' => 2, 'module_id' => 1, 'pixels_x' => 1000, 'pixels_y' => 2000, 'bytes_master' => 1111, 'module_id' => 1, 'master_id' => "ABC1111"));
     $this->assertEquals($check_data, $db_result);
 }
 /**
  * Loads target data into the cache
  *
  * @version 1.0
  * @since 1.0
  *
  * @param array $data | Array of targets to cache, or null to cache all rows in the database
  *	=> VAL @param string/array $location | Single location as string. Multiple locations as array of strings.
  *	=> VAL @param string/array $target | Single target as string. Multiple targets as array of strings.
  *	=> VAL @param int/array $module_id | Single module_id as int. Multiple module_ids as array of int.
  *
  * @return bool | Exception on failure. False on nonexistent. True on success.
  */
 public function load($data, $skip_load = false)
 {
     $db = new FOX_db();
     $struct = self::_struct();
     // Build and run query
     // ===========================================================
     $args = array();
     if ($data["location"]) {
         $args[] = array("col" => "location", "op" => "=", "val" => $data["location"]);
     } elseif ($data["target"]) {
         $args[] = array("col" => "target", "op" => "=", "val" => $data["target"]);
     } elseif ($data["module_id"]) {
         $args[] = array("col" => "module_id", "op" => "=", "val" => $data["module_id"]);
     }
     $ctrl = array("format" => "array_key_array", "key_col" => array("location", "target"));
     $columns = null;
     try {
         $db_result = $db->runSelectQuery($struct, $args, $columns, $ctrl);
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 1, 'text' => "Error reading from database", 'data' => array('args' => $args, 'columns' => $columns, 'ctrl' => $ctrl), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
     }
     if (!$db_result) {
         // No results were found
         return false;
     }
     // Fetch the persistent cache image
     // ===================================================
     if ($skip_load) {
         $cache_image = $this->cache;
     } else {
         try {
             $cache_image = self::readCache();
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 2, 'text' => "Cache read error", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
     }
     // Rebuild the cache image
     // ===================================================
     if (empty($data)) {
         $cache_image["all_cached"] = true;
     } elseif (!$data["target"] && !$data["module_id"]) {
         if (!is_array($data["location"])) {
             $cache_locations = array($data["location"]);
         } else {
             $cache_locations = $data["location"];
         }
         foreach ($cache_locations as $location_name) {
             $cache_image["locations"][$location_name] = true;
         }
         unset($cache_locations, $location_name);
     }
     foreach ($db_result as $location => $targets) {
         foreach ($targets as $target => $target_data) {
             if ($location == "page") {
                 // If the location is a page, only copy the fields used by pages
                 $cache_image["data"][$location][$target]["module_id"] = $target_data["module_id"];
                 $cache_image["data"][$location][$target]["php_class"] = $target_data["php_class"];
             } else {
                 $cache_image["data"][$location][$target] = $target_data;
             }
         }
         unset($location, $targets);
     }
     unset($location, $targets);
     // Update the persistent cache
     // ===================================================
     try {
         self::writeCache($cache_image);
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 3, 'text' => "Cache write error", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
     }
     // Update the class cache
     $this->cache = $cache_image;
     return true;
 }
 /**
  * Loads node, branch, tree, or the entire datastore into the cache
  *
  * @version 1.0
  * @since 1.0
  *
  * @param int/array $tree| Single tree id as int.
  * @param int/array $branch | Single branch id as int.
  * @param string/array $node | Single node name as string. Multiple node names as array of strings.
  * @param bool $skip_load | If set true, the function will not update the class cache array from
  *			    the persistent cache before adding data from a db call and saving it
  *			    back to the persistent cache.
  *
  * @return bool | Exception on failure. True on success.
  */
 public function load($tree = null, $branch = null, $node = null, $skip_load = false)
 {
     $db = new FOX_db();
     $struct = $this->_struct();
     // Build and run query
     // ===========================================================
     $args = array(array("col" => "tree", "op" => "=", "val" => $tree));
     if ($branch) {
         $args[] = array("col" => "branch", "op" => "=", "val" => $branch);
         if ($node) {
             $args[] = array("col" => "node", "op" => "=", "val" => $node);
         }
     }
     $ctrl = array("format" => "array_key_array", "key_col" => array("tree", "branch", "node"));
     $columns = null;
     try {
         $db_result = $db->runSelectQuery($struct, $args, $columns, $ctrl);
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 1, 'text' => "Error reading from database", 'data' => array('args' => $args, 'ctrl' => $ctrl), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
     }
     if ($db_result) {
         // Fetch the persistent cache image
         // ===================================================
         if ($skip_load) {
             $cache_image = $this->cache;
         } else {
             try {
                 $cache_image = self::readCache();
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 2, 'text' => "Cache read error", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
             }
         }
         // Rebuild the cache image
         // ===================================================
         $ancestor_cached = false;
         if (!$tree) {
             // Indicate all data for the module_id is cached
             $cache_image["all_cached"] = true;
             // Clear descendent dictionaries, because they're redundant
             unset($cache_image["trees"]);
             unset($cache_image["branches"]);
             // Prevent descendents from loading their dictionaries
             $ancestor_cached = true;
         }
         foreach ($db_result as $_tree => $branches) {
             if (!$branch && !$ancestor_cached) {
                 $cache_image["trees"][$_tree] = true;
                 unset($cache_image["branches"][$_tree]);
                 $ancestor_cached = true;
             }
             foreach ($branches as $_branch => $nodes) {
                 if (!$node && !$ancestor_cached) {
                     $cache_image["branches"][$_tree][$_branch] = true;
                 }
                 foreach ($nodes as $_node => $val) {
                     $cache_image["keys"][$_tree][$_branch][$_node] = $val;
                 }
                 unset($_node, $val);
             }
             unset($_branch, $nodes);
         }
         unset($_tree, $branches);
         // Clear empty walks from dictionary arrays
         $cache_image["branches"] = FOX_sUtil::arrayPrune($cache_image["branches"], 1);
         // Update the persistent cache
         // ===================================================
         try {
             self::writeCache($cache_image);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 3, 'text' => "Cache write error", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
         }
         // Update the class cache
         $this->cache = $cache_image;
     }
     return true;
 }
 /**
  * Removes one or more users from one or more groups
  *
  * @version 1.0
  * @since 1.0
  *
  * @param int/array $user_id | Single user_id as int. Multiple user_id's as array of ints.
  * @param int/array $group_id | Single group_id as int. Multiple group_id's as array of ints.
  * @return int | False on failure. Int number of rows deleted on success. Int 0 if no rows deleted.
  */
 public function removeFromGroup($user_id, $group_id)
 {
     global $fox;
     $db = new FOX_db();
     try {
         $gk = new FOX_uGroupKeyRing();
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 1, 'text' => "FOX_uGroupKeyRing constructor exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
     }
     // CASE 1: Single user_id, single group_id
     // =================================================================
     if (!is_array($user_id) && !is_array($group_id)) {
         // Get the combined keyring of the user's current groups,
         // minus the one we're removing the user from
         $args = array(array("col" => "user_id", "op" => "=", "val" => $user_id), array("col" => "group_id", "op" => "!=", "val" => $group_id));
         $columns = array("mode" => "include", "col" => "group_id");
         $ctrl = array("format" => "col");
         try {
             $in_groups = $db->runSelectQuery(self::$struct, $args, $columns, $ctrl);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 2, 'text' => "DB select exception", 'data' => array("args" => $args, "columns" => $columns, "ctrl" => $ctrl), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         try {
             $keep_keys = $gk->getKeys($in_groups);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 3, 'text' => "FOX_uGroupKeyRing getKeys exception", 'data' => array("in_groups" => $in_groups), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         // Get the keyring for the group we're removing the user from
         try {
             $drop_keys = $gk->getKeys($group_id);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 4, 'text' => "FOX_uGroupKeyRing getKeys exception", 'data' => array("group_id" => $group_id), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         // Intersect the remove group's keyring with the user's other groups' keyrings
         // to determine the keys that need to be revoked from the user's keyring
         if ($keep_keys && $drop_keys) {
             $revoke_keys = array_diff($drop_keys, $keep_keys);
         } else {
             $revoke_keys = $drop_keys;
         }
         // @@@@@@ BEGIN TRANSACTION @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         try {
             $transaction_started = $db->beginTransaction();
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 5, 'text' => "beginTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         if ($transaction_started) {
             // Drop the user-group pair from the groupstore db table
             $args = array(array("col" => "user_id", "op" => "=", "val" => $user_id), array("col" => "group_id", "op" => "=", "val" => $group_id));
             try {
                 $rows_changed = $db->runDeleteQuery(self::$struct, $args);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 6, 'text' => "DB delete exception", 'data' => $args, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             // If the user is not a member of the group we are attempting to delete
             // them from, return "0" to indicate that no rows were changed
             if (!$rows_changed) {
                 try {
                     $db->rollbackTransaction();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 7, 'text' => "rollbackTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 return 0;
             }
             // Revoke the keys on the group's keyring that the user is not
             // being granted by other groups
             if ($revoke_keys) {
                 $ks = new FOX_uKeyRing();
                 try {
                     $keys_ok = $ks->revokeKey($user_id, $revoke_keys);
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 8, 'text' => "FOX_uKeyRing revokeKey exception", 'data' => array("user_id" => $user_id, "revoke_keys" => $revoke_keys), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
             } else {
                 // Handle no keys to revoke
                 $keys_ok = true;
             }
             // If all operations were successful, commit the transaction
             if ($rows_changed && $keys_ok) {
                 try {
                     $commit_ok = $db->commitTransaction();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 9, 'text' => "commitTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 // Update the cache
                 if ($commit_ok) {
                     // Load, update, writeback
                     $this->cache[$user_id] = $fox->cache->get("FOX_uGroupMember", $user_id);
                     $this->cache[$user_id]["keys"][$group_id] = false;
                     $cache_ok = $fox->cache->set("FOX_uGroupMember", $user_id, $this->cache[$user_id]);
                     if ($cache_ok) {
                         return $rows_changed;
                     } else {
                         return false;
                     }
                 } else {
                     return false;
                 }
             } else {
                 try {
                     $db->rollbackTransaction();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 10, 'text' => "rollbackTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 return false;
             }
         } else {
             // If we couldn't start a transaction, return false
             return false;
         }
         // @@@@@@ END TRANSACTION @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     }
     // CASE 2: Single user_id, multiple groups
     // =================================================================
     if (!is_array($user_id) && is_array($group_id)) {
         // Get the combined keyring of all the user's current groups,
         // minus the groups we're removing them from
         $args = array(array("col" => "user_id", "op" => "=", "val" => $user_id), array("col" => "group_id", "op" => "!=", "val" => $group_id));
         $columns = array("mode" => "include", "col" => "group_id");
         $ctrl = array("format" => "col");
         try {
             $in_groups = $db->runSelectQuery(self::$struct, $args, $columns, $ctrl);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 11, 'text' => "DB select exception", 'data' => array("args" => $args, "columns" => $columns, "ctrl" => $ctrl), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         try {
             $keep_keys = $gk->getKeys($in_groups);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 12, 'text' => "FOX_uGroupKeyRing getKeys exception", 'data' => array("in_groups" => $in_groups), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         // Get the combined keyring of all the groups that we're
         // removing the user from
         try {
             $drop_keys = $gk->getKeys($group_id);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 13, 'text' => "FOX_uGroupKeyRing getKeys exception", 'data' => array("groiup_id" => $group_id), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         // Intersect the $keep_keys and $drop_keys arrays to get
         // a list of keys we need to revoke from the user
         if ($keep_keys && $drop_keys) {
             $revoke_keys = array_diff($drop_keys, $keep_keys);
         } else {
             $revoke_keys = $drop_keys;
         }
         // @@@@@@ BEGIN TRANSACTION @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         try {
             $transaction_started = $db->beginTransaction();
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 14, 'text' => "beginTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         if ($transaction_started) {
             // Drop the user-group pairs from the groupstore db table
             $args = array(array("col" => "user_id", "op" => "=", "val" => $user_id), array("col" => "group_id", "op" => "=", "val" => $group_id));
             try {
                 $rows_changed = $db->runDeleteQuery(self::$struct, $args);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 15, 'text' => "DB delete exception", 'data' => $args, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             // If the user is not a member of any of the groups we're trying to delete
             // them from, return "0" to indicate that no db rows were changed
             if (!$rows_changed) {
                 try {
                     $db->rollbackTransaction();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 16, 'text' => "rollbackTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 return 0;
             }
             // Revoke all the keys we previously calculated
             if ($revoke_keys) {
                 $ks = new FOX_uKeyRing();
                 try {
                     $keys_ok = $ks->revokeKey($user_id, $revoke_keys);
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 17, 'text' => "FOX_uKeyRing revokeKey exception", 'data' => array("user_id" => $user_id, "revoke_keys" => $revoke_keys), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
             } else {
                 // Handle no keys to revoke
                 $keys_ok = true;
             }
             // If all operations were successful, commit the transaction
             if ($rows_changed && $keys_ok) {
                 try {
                     $commit_ok = $db->commitTransaction();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 18, 'text' => "commitTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 // Update the cache
                 if ($commit_ok) {
                     // Load, update, writeback
                     $this->cache[$user_id] = $fox->cache->get("FOX_uGroupMember", $user_id);
                     foreach ($group_id as $group) {
                         $this->cache[$user_id]["keys"][$group] = false;
                     }
                     unset($group);
                     $cache_ok = $fox->cache->set("FOX_uGroupMember", $user_id, $this->cache[$user_id]);
                     if ($cache_ok) {
                         return $rows_changed;
                     } else {
                         return false;
                     }
                 } else {
                     return false;
                 }
             } else {
                 try {
                     $db->rollbackTransaction();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 19, 'text' => "rollbackTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 return false;
             }
         } else {
             // If we couldn't start a transaction, return false
             return false;
         }
         // @@@@@@ END TRANSACTION @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     }
     // CASE 3: Multiple user_id's, single group
     // =================================================================
     if (is_array($user_id) && !is_array($group_id)) {
         // Load all of the groups that each user is currently in, except
         // the group we're removing them from
         $args = array(array("col" => "user_id", "op" => "=", "val" => $user_id), array("col" => "group_id", "op" => "!=", "val" => $group_id));
         $ctrl = array("format" => "array_key_array_grouped", "key_col" => array("user_id", "group_id"));
         try {
             $in_groups = $db->runSelectQuery(self::$struct, $args, $columns = null, $ctrl);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 20, 'text' => "DB select exception", 'data' => array("args" => $args, "ctrl" => $ctrl), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         // @@@@@@ BEGIN TRANSACTION @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         try {
             $started_transaction = $db->beginTransaction();
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 21, 'text' => "beginTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         if ($started_transaction) {
             $all_ok = true;
             try {
                 $gk = new FOX_uGroupKeyRing();
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 22, 'text' => "FOX_uGroupKeyRing constructor exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             foreach ($user_id as $user) {
                 // Get the combined keyring of all the user's other groups
                 try {
                     $keep_keys = $gk->getKeys($in_groups[$user]);
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 23, 'text' => "FOX_uGroupKeyRing getKeys exception", 'data' => array("user" => $in_groups[$user]), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 // Get the keyring of the group we're removing the user from
                 try {
                     $drop_keys = $gk->getKeys($group_id);
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 24, 'text' => "FOX_uGroupKeyRing getKeys exception", 'data' => array("group_id" => $group_id), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 // Intersect the $keep_keys and $drop_keys arrays to get
                 // a list of keys we need to revoke from the user
                 if ($keep_keys && $drop_keys) {
                     $revoke_keys = array_diff($drop_keys, $keep_keys);
                 } else {
                     $revoke_keys = $drop_keys;
                 }
                 // Revoke all the keys we previously calculated
                 if ($revoke_keys) {
                     $ks = new FOX_uKeyRing();
                     try {
                         $keys_ok = $ks->revokeKey($user, $revoke_keys);
                     } catch (FOX_exception $child) {
                         throw new FOX_exception(array('numeric' => 25, 'text' => "FOX_uKeyRing revokeKey exception", 'data' => array("user" => $user, "revoke_keys" => $revoke_keys), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                     }
                     if ($keys_ok === false) {
                         // Handles (int)0 return value from revokeKey()
                         $all_ok = false;
                         // if we try to remove a key from a user that doesn't
                     }
                     // have the key, as part of removing a key from
                 }
                 // a large batch of users
             }
             unset($user);
             // Drop the user-group pairs from the groupstore db table
             $args = array(array("col" => "user_id", "op" => "=", "val" => $user_id), array("col" => "group_id", "op" => "=", "val" => $group_id));
             try {
                 $rows_changed = $db->runDeleteQuery(self::$struct, $args);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 26, 'text' => "DB delete exception", 'data' => $args, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             // If none of the users are a member of the group we're trying to delete
             // them from, return "0" to indicate that no db rows were changed
             if (!$rows_changed) {
                 try {
                     $db->rollbackTransaction();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 27, 'text' => "rollbackTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 return 0;
             }
             // If all operations completed successfully, commit the transaction and
             // return the result of the commit.
             // =======================================================
             if ($rows_changed && $all_ok) {
                 try {
                     $commit_ok = $db->commitTransaction();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 28, 'text' => "commitTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 // Update the cache
                 if ($commit_ok) {
                     $all_ok = true;
                     foreach ($user_id as $user) {
                         // Load, update, writeback
                         $this->cache[$user] = $fox->cache->get("FOX_uGroupMember", $user);
                         $this->cache[$user]["keys"][$group_id] = false;
                         $cache_ok = $fox->cache->set("FOX_uGroupMember", $user, $this->cache[$user]);
                         if (!$cache_ok) {
                             $all_ok = false;
                         }
                     }
                     unset($user);
                     if ($all_ok) {
                         return $rows_changed;
                     } else {
                         return false;
                     }
                 } else {
                     return false;
                 }
             } else {
                 try {
                     $db->rollbackTransaction();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 29, 'text' => "rollbackTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 return false;
             }
         } else {
             // If we couldn't start a transaction, return false
             return false;
         }
         // @@@@@@ END TRANSACTION @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     }
     // CASE 4: Multiple user_id's, multiple groups
     // =================================================================
     if (is_array($user_id) && is_array($group_id)) {
         // Load all of the groups that each user is currently in, except
         // for the groups we're deleting users from
         $args = array(array("col" => "user_id", "op" => "=", "val" => $user_id), array("col" => "group_id", "op" => "!=", "val" => $group_id));
         $ctrl = array("format" => "array_key_array_grouped", "key_col" => array("user_id", "group_id"));
         try {
             $in_groups = $db->runSelectQuery(self::$struct, $args, $columns = null, $ctrl);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 30, 'text' => "DB select exception", 'data' => array("args" => $args, "ctrl" => $ctrl), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         // @@@@@@ BEGIN TRANSACTION @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         if ($db->beginTransaction()) {
             $all_ok = true;
             try {
                 $gk = new FOX_uGroupKeyRing();
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 31, 'text' => "FOX_uGroupKeyRing constructor exception", 'data' => array("in_groups" => $in_groups), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             foreach ($user_id as $user) {
                 // Get the combined keyring of all the user's other groups
                 try {
                     $keep_keys = $gk->getKeys($in_groups[$user]);
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 32, 'text' => "FOX_uGroupKeyRing getKeys exception", 'data' => array("user" => $in_groups[$user]), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 // Get the keyring of the groups we're removing the user from
                 try {
                     $drop_keys = $gk->getKeys($group_id);
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 33, 'text' => "FOX_uGroupKeyRing getKeys exception", 'data' => array("group_id" => $group_id), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 // Intersect the $keep_keys and $drop_keys arrays to get
                 // a list of keys we need to revoke from the user
                 if ($keep_keys && $drop_keys) {
                     $revoke_keys = array_diff($drop_keys, $keep_keys);
                 } else {
                     $revoke_keys = $drop_keys;
                 }
                 // Revoke all the keys we previously calculated
                 if ($revoke_keys) {
                     $ks = new FOX_uKeyRing();
                     try {
                         $keys_ok = $ks->revokeKey($user, $revoke_keys);
                     } catch (FOX_exception $child) {
                         throw new FOX_exception(array('numeric' => 34, 'text' => "FOX_uKeyRing revokeKey exception", 'data' => array("user" => $user, "revoke_keys" => $revoke_keys), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                     }
                     if ($keys_ok === false) {
                         // Handles (int)0 return value from revokeKey()
                         $all_ok = false;
                         // if we try to remove a key from a user that doesn't
                     }
                     // have the key, as part of removing a key from
                 }
                 // a large batch of users
             }
             unset($user);
             // Drop the user-group pairs from the groupstore db table
             $args = array(array("col" => "user_id", "op" => "=", "val" => $user_id), array("col" => "group_id", "op" => "=", "val" => $group_id));
             try {
                 $rows_changed = $db->runDeleteQuery(self::$struct, $args);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 35, 'text' => "DB delete exception", 'data' => $args, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             // If none of the users are a members of any of the groups we're trying to delete
             // them from, return "0" to indicate that no db rows were changed
             if (!$rows_changed) {
                 try {
                     $db->rollbackTransaction();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 36, 'text' => "rollbackTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 return 0;
             }
             // If all operations completed successfully, commit the transaction and
             // return the result of the commit.
             // =======================================================
             if ($rows_changed && $all_ok) {
                 try {
                     $commit_ok = $db->commitTransaction();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 37, 'text' => "commitTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 // Update the cache
                 if ($commit_ok) {
                     $all_ok = true;
                     foreach ($user_id as $user) {
                         // Load, update, writeback
                         $this->cache[$user] = $fox->cache->get("FOX_uGroupMember", $user);
                         foreach ($group_id as $group) {
                             $this->cache[$user]["keys"][$group] = false;
                         }
                         $cache_ok = $fox->cache->set("FOX_uGroupMember", $user, $this->cache[$user]);
                         if (!$cache_ok) {
                             $all_ok = false;
                         }
                     }
                     unset($user, $group);
                     if ($all_ok) {
                         return $rows_changed;
                     } else {
                         return false;
                     }
                 } else {
                     return false;
                 }
             } else {
                 try {
                     $db->rollbackTransaction();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 38, 'text' => "rollbackTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 return false;
             }
         } else {
             // If we couldn't start a transaction, return false
             return false;
         }
         // @@@@@@ END TRANSACTION @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     }
 }
 /**
  * Loads key, type_id, REMOVE, or all data for one or more modules from the db
  * into the key cache
  *
  * @version 1.0
  * @since 1.0
  *
  * @param int/array $module_id | Single module id as int. Multiple module id as array of int.
  * @param string/array $type_id | Single type_id name as string. Multiple type_id id as array of string.
  * @param string/array $branch_id | Single type as string. Multiple types as array of strings.
  * @param int/array $key_id | Single key_id as int. Multiple key_id's as array of ints.
  * @param bool $skip_load | If set true, the function will not update the class cache array from
  *			    the persistent cache before adding data from a db call and saving it
  *			    back to the persistent cache.
  *
  * @return bool | Exception on failure. True on success.
  */
 public function load($module_id, $type_id = null, $branch_id = null, $key_id = null, $skip_load = false)
 {
     $db = new FOX_db();
     $struct = $this->_struct();
     // Build and run query
     // ===========================================================
     $args = array(array("col" => "module_id", "op" => "=", "val" => $module_id));
     if ($type_id) {
         $args[] = array("col" => "type_id", "op" => "=", "val" => $type_id);
         if ($branch_id) {
             $args[] = array("col" => "branch_id", "op" => "=", "val" => $branch_id);
             if ($key_id) {
                 $args[] = array("col" => "key_id", "op" => "=", "val" => $key_id);
             }
         }
     }
     $ctrl = array("format" => "array_key_array", "key_col" => array("module_id", "type_id", "branch_id", "key_id"));
     $columns = null;
     try {
         $db_result = $db->runSelectQuery($struct, $args, $columns, $ctrl);
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 1, 'text' => "Error loading requested keys from database", 'data' => array("module_id" => $module_id, "type_id" => $type_id, "branch_id" => $branch_id, "key_id" => $key_id, "skip_load" => $skip_load), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
     }
     if (!$db_result) {
         // If the query returned zero results, no further work needs to be done
         return true;
     }
     // Load the persistent cache records for all returned module_id's into
     // the temp class cache
     // =====================================================================
     $module_ids = array_keys($db_result);
     if ($skip_load) {
         foreach ($module_ids as $_module_id) {
             $update_cache[$_module_id] = $this->cache[$_module_id];
         }
         unset($_module_id);
     } else {
         try {
             $update_cache = self::readCachePage($module_ids);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 2, 'text' => "Cache get error", 'data' => array("module_ids" => array_keys($db_result)), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
         }
     }
     unset($module_ids);
     // Overwrite the temp cache records with the data fetched in the db query,
     // while setting the correct heirarchical cache flags
     // =====================================================================
     $ancestor_cached = false;
     foreach ($db_result as $_module_id => $type_ids) {
         if (!$type_id) {
             // Indicate all data for the module_id is cached
             $update_cache[$_module_id]["all_cached"] = true;
             // Clear descendent dictionaries, because they're redundant
             unset($update_cache[$_module_id]["type_id"]);
             unset($update_cache[$_module_id]["branch_id"]);
             // Prevent descendents from loading their dictionaries
             $ancestor_cached = true;
         }
         foreach ($type_ids as $_type_id => $branch_ids) {
             if (!$branch_id && !$ancestor_cached) {
                 $update_cache[$_module_id]["type_id"][$_type_id] = true;
                 unset($update_cache[$_module_id]["branch_id"]);
                 $ancestor_cached = true;
             }
             foreach ($branch_ids as $_branch_id => $key_ids) {
                 if (!$key_id && !$ancestor_cached) {
                     $update_cache[$_module_id]["branch_id"][$_type_id][$_branch_id] = true;
                 }
                 foreach ($key_ids as $_key_id => $val) {
                     $update_cache[$_module_id]["keys"][$_type_id][$_branch_id][$_key_id] = $val["key_id"];
                 }
                 unset($_key_id, $val);
             }
             unset($_branch_id, $key_ids);
         }
         unset($_type_id, $branch_ids);
     }
     unset($_module_id, $type_ids);
     // Clear empty walks from dictionary arrays
     $update_cache["branch_id"] = FOX_sUtil::arrayPrune($update_cache["branch_id"], 1);
     // Overwrite the persistent cache records with the temp class cache array
     // =====================================================================
     try {
         self::writeCachePage($update_cache);
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 3, 'text' => "Cache set error", 'data' => $update_cache, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
     }
     // Write the temp class cache array items to the class cache array
     foreach ($update_cache as $update_module_id => $module_data) {
         $this->cache[$update_module_id] = $module_data;
     }
     unset($update_module_id, $module_data);
     return true;
 }
 /**
  * Loads user keys into the cache.
  *
  * @version 1.0
  * @since 1.0
  *
  * @param int/array $user_id | Single user id as int. Multiple user id's as array of int.
  * @param int/array $key_id | Single key id as int. Multiple key ids as array of int.
  * @return bool | False on failure. True on success.
  */
 public function load($user_id, $key_id = null, $skip_load = false)
 {
     global $fox;
     $db = new FOX_db();
     $args = array();
     if ($user_id) {
         $args[] = array("col" => "user_id", "op" => "=", "val" => $user_id);
     }
     if ($key_id) {
         $args[] = array("col" => "key_id", "op" => "=", "val" => $key_id);
     }
     $columns = array("mode" => "include", "col" => array("user_id", "key_id"));
     $ctrl = array("format" => "array_key_array_grouped", "key_col" => array("user_id", "key_id"));
     try {
         $db_result = $db->runSelectQuery(self::$struct, $args, $columns, $ctrl);
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 1, 'text' => "DB select exception", 'data' => array("args" => $args, "columns" => $columns, "ctrl" => $ctrl), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
     }
     // When the function is called with specific keys to look for, if a user doesn't
     // have that key, we can add that information to the cache. This saves a query
     // the next time the key is requested.
     if ($db_result) {
         if ($key_id) {
             if (!is_array($user_id)) {
                 $user_id = array($user_id);
             }
             if (!is_array($key_id)) {
                 $key_id = array($key_id);
             }
             $all_ok = true;
             foreach ($user_id as $user) {
                 // Load the persistent cache record for the user_id into the class's cache array
                 if (!$skip_load) {
                     $this->cache[$user] = $fox->cache->get("FOX_uKeyRing", $user);
                 }
                 foreach ($key_id as $key) {
                     if (FOX_sUtil::valExists($key, $db_result[$user])) {
                         $this->cache[$user]["keys"][$key] = true;
                     } else {
                         $this->cache[$user]["keys"][$key] = false;
                     }
                 }
                 // Save the updated persistent cache record for the user_id
                 $cache_ok = $fox->cache->set("FOX_uData", $user, $this->cache[$user]);
                 if (!$cache_ok) {
                     $all_ok = false;
                 }
             }
             return $all_ok;
         } else {
             // If the function is called without $key_id set, all of the keys for each user will be added
             // to the cache. At this point we can unset all of the user's "false" keys because we know the
             // list of keys is authoratative (if it's not in the array, they don't have the key).
             $all_ok = true;
             foreach ($db_result as $user => $keys) {
                 unset($this->cache[$user]);
                 $this->cache[$user]["all_cached"] = true;
                 foreach ($keys as $key) {
                     $this->cache[$user]["keys"][$key] = true;
                 }
                 // Save the updated persistent cache record for the user_id
                 $cache_ok = $fox->cache->set("FOX_uData", $user, $this->cache[$user]);
                 if (!$cache_ok) {
                     $all_ok = false;
                 }
             }
             return $all_ok;
         }
     } else {
         return false;
     }
 }
 /**
  * Test fixture for dropGlobal() method, L1, multiple items
  *
  * @version 1.0
  * @since 1.0
  * 
  * =======================================================================================
  */
 public function test_dropGlobal_L1_multi()
 {
     self::loadData();
     // Drop objects
     // ####################################################################
     $drop_ctrl = array("validate" => true);
     try {
         $rows_changed = $this->cls->dropGlobal(1, array(1, 2), $drop_ctrl);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(1));
     }
     // Should report 12 rows were dropped
     $this->assertEquals(10, $rows_changed);
     // Verify db state
     // ####################################################################
     $db = new FOX_db();
     $columns = null;
     $args = null;
     $ctrl = array('format' => 'array_key_array', 'key_col' => array('L2', 'L1'));
     try {
         $result = $db->runSelectQuery($this->cls->_struct(), $args, $columns, $ctrl);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(1));
     }
     // NOTE: the datastore will automatically clip empty branches
     $test_obj = new stdClass();
     $test_obj->foo = "11";
     $test_obj->bar = "test_Bar";
     $check = array('X' => array(5 => true, 3 => (int) 0), 'Y' => array(3 => (double) 1.7, 4 => (double) -1.6), 'Z' => array(3 => $test_obj), 'A' => array(5 => true, 3 => (int) 0), 'B' => array(3 => (double) 1.7, 4 => (double) -1.6));
     $this->assertEquals($check, $result);
     // Check class cache state
     // ####################################################################
     $check_cache = array();
     $this->assertEquals($check_cache, $this->cls->cache);
     // Verify persistent cache state by reading-back all items
     // ####################################################################
     $request = array('X' => array(), 'Y' => array(), 'Z' => array(), 'A' => array(), 'B' => array());
     $valid = false;
     try {
         $result = $this->cls->getMulti($request, $ctrl, $valid);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(1));
     }
     $this->assertEquals(true, $valid);
     // Should report valid because all
     // requested L2's exist
     $this->assertEquals($check, $result);
 }
 /**
  * Test fixture for setMulti() method (matrix mode)
  *
  * @version 1.0
  * @since 1.0
  * 
  * =======================================================================================
  */
 function test_setMulti_matrix()
 {
     $test_obj = new stdClass();
     $test_obj->foo = "11";
     $test_obj->bar = "test_Bar";
     $test_data = array(array('L5' => 1, 'L4' => 'X', 'L3' => 'K', 'L2' => 'K', 'L1' => 1, 'L0' => null), array('L5' => 1, 'L4' => 'X', 'L3' => 'K', 'L2' => 'K', 'L1' => 2, 'L0' => false), array('L5' => 1, 'L4' => 'X', 'L3' => 'K', 'L2' => 'T', 'L1' => 1, 'L0' => true), array('L5' => 1, 'L4' => 'X', 'L3' => 'Z', 'L2' => 'Z', 'L1' => 3, 'L0' => (int) 0), array('L5' => 1, 'L4' => 'Y', 'L3' => 'K', 'L2' => 'K', 'L1' => 1, 'L0' => (int) 1), array('L5' => 1, 'L4' => 'Y', 'L3' => 'K', 'L2' => 'K', 'L1' => 2, 'L0' => (int) -1), array('L5' => 1, 'L4' => 'Y', 'L3' => 'K', 'L2' => 'T', 'L1' => 3, 'L0' => (double) 1.7), array('L5' => 1, 'L4' => 'Y', 'L3' => 'Z', 'L2' => 'Z', 'L1' => 4, 'L0' => (double) -1.6), array('L5' => 2, 'L4' => 'X', 'L3' => 'K', 'L2' => 'K', 'L1' => 1, 'L0' => (string) "foo"), array('L5' => 2, 'L4' => 'X', 'L3' => 'K', 'L2' => 'K', 'L1' => 2, 'L0' => array(null, true, false, 1, 1.0, "foo")), array('L5' => 2, 'L4' => 'X', 'L3' => 'Z', 'L2' => 'Z', 'L1' => 3, 'L0' => $test_obj));
     // Load class with data
     // ===============================================================
     try {
         $ctrl = array('mode' => 'matrix');
         $set_ok = $this->cls->setMulti($test_data, $ctrl);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(array('depth' => 50, 'data' => true)));
     }
     // Should return number of L1's added
     $this->assertEquals(11, $set_ok);
     // Test adding some  duplicate itemes
     // ===============================================================
     try {
         $dupe_data = array(array('L5' => 1, 'L4' => 'X', 'L3' => 'K', 'L2' => 'K', 'L1' => 1, 'L0' => null), array('L5' => 1, 'L4' => 'X', 'L3' => 'K', 'L2' => 'K', 'L1' => 2, 'L0' => false), array('L5' => 1, 'L4' => 'X', 'L3' => 'K', 'L2' => 'T', 'L1' => 1, 'L0' => true), array('L5' => 1, 'L4' => 'X', 'L3' => 'Z', 'L2' => 'Z', 'L1' => 3, 'L0' => (int) 0), array('L5' => 1, 'L4' => 'Y', 'L3' => 'K', 'L2' => 'K', 'L1' => 1, 'L0' => (int) 1), array('L5' => 1, 'L4' => 'Y', 'L3' => 'K', 'L2' => 'K', 'L1' => 2, 'L0' => (int) -1), array('L5' => 1, 'L4' => 'Y', 'L3' => 'K', 'L2' => 'T', 'L1' => 3, 'L0' => (double) 1.7), array('L5' => 1, 'L4' => 'Y', 'L3' => 'Z', 'L2' => 'Z', 'L1' => 4, 'L0' => (double) -1.6));
         $ctrl = array('mode' => 'matrix');
         $rows_changed = $this->cls->setMulti($dupe_data, $ctrl);
         // Should return (int)0 to indicate no rows were changed
         $this->assertEquals(0, $rows_changed);
     } catch (FOX_exception $child) {
         // Shouldn't throw an exception because the set() method allows
         // existing rows to be overwritten
         $this->fail($child->dumpString(array('depth' => 50, 'data' => true)));
     }
     // Check cache state
     // ===============================================================
     // NOTE: the LUT's won't be set at this point, because we haven't done any
     // database reads that give objects authority
     $check = array(1 => array('keys' => array('X' => array('K' => array('K' => array(1 => null, 2 => false), 'T' => array(1 => true)), 'Z' => array('Z' => array(3 => (int) 0))), 'Y' => array('K' => array('K' => array(1 => (int) 1, 2 => (int) -1), 'T' => array(3 => (double) 1.7)), 'Z' => array('Z' => array(4 => (double) -1.6))))), 2 => array('keys' => array('X' => array('K' => array('K' => array(1 => (string) "foo", 2 => array(null, true, false, 1, 1.0, "foo"))), 'Z' => array('Z' => array(3 => $test_obj))))));
     $this->assertEquals($check, $this->cls->cache);
     // Check db state
     // ===============================================================
     $check = array(1 => array('X' => array('K' => array('K' => array(1 => null, 2 => false), 'T' => array(1 => true)), 'Z' => array('Z' => array(3 => (int) 0))), 'Y' => array('K' => array('K' => array(1 => (int) 1, 2 => (int) -1), 'T' => array(3 => (double) 1.7)), 'Z' => array('Z' => array(4 => (double) -1.6)))), 2 => array('X' => array('K' => array('K' => array(1 => (string) "foo", 2 => array(null, true, false, 1, 1.0, "foo"))), 'Z' => array('Z' => array(3 => $test_obj)))));
     $db = new FOX_db();
     $columns = null;
     $ctrl = array('format' => 'array_key_array', 'key_col' => array('L5', 'L4', 'L3', 'L2', 'L1'));
     try {
         $struct = $this->cls->_struct();
         $result = $db->runSelectQuery($struct, $args = null, $columns, $ctrl);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(1));
     }
     $this->assertEquals($check, $result);
 }
Example #16
0
 /**
  * Deletes a single group, given its group_id
  *
  * @version 1.0
  * @since 1.0
  *
  * @param int $group_id | id of the group
  * @return bool | False on failure. True on success.
  */
 public function deleteGroup($group_id)
 {
     global $fox;
     $db = new FOX_db();
     $columns = array("mode" => "include", "col" => array("is_default", "name"));
     $ctrl = array("format" => "row_array");
     try {
         $group = $db->runSelectQueryCol(self::$struct, "group_id", "=", $group_id, $columns, $ctrl);
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 1, 'text' => "DB select exception", 'data' => array("data" => $data, "col" => "group_id", "op" => "=", "val" => $group_id, "columns" => $columns, "ctrl" => $ctrl), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
     }
     // If the group we're trying to delete is the default group, reject the action. There must *always* be a
     // default group on the system. If the admin wants to delete the default group, they have to make
     // another group the default group first.
     if ($group["is_default"] == true) {
         //echo "\nclass.user.group.types::deleteGroup() - attempted delete on default group\n";
         return false;
     }
     // Trap trying to delete a nonexistent group
     if (!$group) {
         //echo "\nclass.user.group.types::deleteGroup() - attempted delete on nonexistent group: $group_id \n";
         return false;
     }
     // Get the user_id of every user in the group we're deleting
     $columns = array("mode" => "include", "col" => "user_id");
     $ctrl = array("format" => "col");
     try {
         $user_ids = $db->runSelectQueryCol(FOX_uGroupMember::_struct(), "group_id", "=", $group_id, $columns, $ctrl);
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 2, 'text' => "DB select exception", 'data' => array("data" => $data, "col" => "group_id", "op" => "=", "val" => $group_id, "columns" => $columns, "ctrl" => $ctrl), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
     }
     // CASE 1: There are users that are members of the group
     // ===============================================================================
     if ($user_ids) {
         // Load all of the groups that each user is currently in, except
         // the group we're removing them from
         $args = array(array("col" => "user_id", "op" => "=", "val" => $user_ids), array("col" => "group_id", "op" => "!=", "val" => $group_id));
         $ctrl = array("format" => "array_key_array_grouped", "key_col" => array("user_id", "group_id"));
         try {
             $in_groups = $db->runSelectQuery(FOX_uGroupMember::_struct(), $args, $columns = null, $ctrl);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 3, 'text' => "DB select exception", 'data' => array("args" => $args, "ctrl" => $ctrl), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         // @@@@@@ BEGIN TRANSACTION @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         try {
             $started_transaction = $db->beginTransaction();
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 4, 'text' => "beginTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         if ($started_transaction) {
             $keys_ok = true;
             try {
                 $gk = new FOX_uGroupKeyRing();
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 2, 'text' => "FOX_uGroupKeyRing constructor exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             foreach ($user_ids as $user) {
                 // Get the combined keyring of all the user's other groups
                 try {
                     $keep_keys = $gk->getKeys($in_groups[$user]);
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 5, 'text' => "FOX_uGroupKeyRing getKeys exception", 'data' => array("user" => $in_groups[$user]), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 // Get the keyring of the group we're removing the user from
                 try {
                     $drop_keys = $gk->getKeys($group_id);
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 6, 'text' => "FOX_uGroupKeyRing getKeys exception", 'data' => array("group_id" => $group_id), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 // Intersect the $keep_keys and $drop_keys arrays to get
                 // a list of keys we need to revoke from the user
                 if ($keep_keys && $drop_keys) {
                     $revoke_keys = array_diff($drop_keys, $keep_keys);
                 } else {
                     $revoke_keys = $drop_keys;
                 }
                 // Revoke all the keys we previously calculated
                 if ($revoke_keys) {
                     $ks = new FOX_uKeyRing();
                     try {
                         $revoke_ok = $ks->revokeKey($user, $revoke_keys);
                     } catch (FOX_exception $child) {
                         throw new FOX_exception(array('numeric' => 7, 'text' => "FOX_uKeyRing revokeKeys exception", 'data' => array("user" => $user, "revoke_keys" => $revoke_keys), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                     }
                     if (!$revoke_ok) {
                         $keys_ok = false;
                     }
                 } else {
                     // Handle no keys to revoke
                     $keys_ok = true;
                 }
             }
             unset($user);
             // Because we are inside a transaction, we have to directly delete items from
             // the other class's db tables. If we deleted items using the other class's
             // functions, the other classes would remove them from their caches before we
             // could confirm all steps in the transaction were successful.
             // ============================================================================
             // Drop the group-user pairs from the group members table
             $args = array(array("col" => "group_id", "op" => "=", "val" => $group_id));
             try {
                 $gm_ok = $db->runDeleteQuery(FOX_uGroupMember::_struct(), $args);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 8, 'text' => "DB delete exception", 'data' => $args, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             // Drop the group-key pairs from the group keyring table
             $args = array(array("col" => "group_id", "op" => "=", "val" => $group_id));
             try {
                 $gk_ok = $db->runDeleteQuery(FOX_uGroupKeyRing::_struct(), $args);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 9, 'text' => "DB delete exception", 'data' => $args, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             // Drop the group from the group types table
             $args = array(array("col" => "group_id", "op" => "=", "val" => $group_id));
             try {
                 $gt_ok = $db->runDeleteQuery(self::$struct, $args);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 10, 'text' => "DB delete exception", 'data' => $args, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             // Update the cache
             if ($keys_ok && $gm_ok !== false && $gk_ok !== false && $gt_ok) {
                 // Handle groups with no members and
                 // groups with no keys returning (int)0
                 try {
                     $commit_ok = $db->commitTransaction();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 11, 'text' => "commitTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 if ($commit_ok) {
                     // Because we directly modified other class's db tables, we have to
                     // flush the cache for the affected classes
                     try {
                         $fox->cache->flushNamespace("FOX_uGroupMember");
                     } catch (FOX_exception $child) {
                         throw new FOX_exception(array('numeric' => 12, 'text' => "FOX_uGroupMember flushNamespace exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                     }
                     try {
                         $fox->cache->flushNamespace("FOX_uGroupKeyRing");
                     } catch (FOX_exception $child) {
                         throw new FOX_exception(array('numeric' => 13, 'text' => "FOX_uGroupKeyRing flushNamespace exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                     }
                     // Load, update, writeback
                     try {
                         self::loadCache();
                     } catch (FOX_exception $child) {
                         throw new FOX_exception(array('numeric' => 14, 'text' => "FOX_uGroupKeyRing getKeys exception", 'data' => array("user" => $group_id), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                     }
                     unset($this->cache["ids"][$group["name"]]);
                     $cache_ok = self::saveCache();
                     return $cache_ok;
                 } else {
                     return false;
                 }
             }
         } else {
             // If we couldn't start a transaction, return false
             return false;
         }
         // @@@@@@ END TRANSACTION @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     } else {
         // @@@@@@ BEGIN TRANSACTION @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         try {
             $started_transaction = $db->beginTransaction();
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 15, 'text' => "beginTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         if ($started_transaction) {
             // Because we are inside a transaction, we have to directly delete items from
             // the other class's db tables. If we deleted items using the other class's
             // functions, the other classes would remove them from their caches before we
             // could confirm all steps in the transaction were successful.
             // ============================================================================
             // Drop the group-key pairs from the group keyring table
             $args = array(array("col" => "group_id", "op" => "=", "val" => $group_id));
             try {
                 $gk_ok = $db->runDeleteQuery(FOX_uGroupKeyRing::_struct(), $args);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 16, 'text' => "DB delete exception", 'data' => $args, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             // Drop the group from the group types table
             $args = array(array("col" => "group_id", "op" => "=", "val" => $group_id));
             try {
                 $gt_ok = $db->runDeleteQuery(self::$struct, $args);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 17, 'text' => "DB delete exception", 'data' => $args, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             // Update the cache
             if ($gk_ok !== false && $gt_ok) {
                 // Handle groups with no keys
                 // returning (int)0
                 try {
                     $commit_ok = $db->commitTransaction();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 18, 'text' => "commitTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 if ($commit_ok) {
                     // Because we directly modified another class's db table, we
                     // have to flush the cache for the affected class
                     try {
                         $fox->cache->flushNamespace("FOX_uGroupKeyRing");
                     } catch (FOX_exception $child) {
                         throw new FOX_exception(array('numeric' => 2, 'text' => "FOX_uGroupKeyRing flushNamespace exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                     }
                     // Load, update, writeback
                     try {
                         self::loadCache();
                     } catch (FOX_exception $child) {
                         throw new FOX_exception(array('numeric' => 19, 'text' => "loadCache exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                     }
                     unset($this->cache["ids"][$group["name"]]);
                     try {
                         $cache_ok = self::saveCache();
                     } catch (FOX_exception $child) {
                         throw new FOX_exception(array('numeric' => 20, 'text' => "saveCache exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                     }
                     return $cache_ok;
                 } else {
                     return false;
                 }
             }
         } else {
             // If we couldn't start a transaction, return false
             return false;
         }
         // @@@@@@ END TRANSACTION @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     }
     // It might be possible to do this using a sophisticated query
     // Remove all keys granted by the group, from every user on the site, unless another group grants
     // the key, and the user is a member of that other group
     // ========================================================
     // DELETE kst
     // FROM user_keystore_table AS kst
     // INNER JOIN group_members_table AS gmt ON kst.user_id = gmt.user_id	    // user has to be a member of the deleted group
     // INNER JOIN group_keyring_table AS gkt ON gmt.group_id = gkt.group_id	    // key has to be granted by the deleted group
     // WHERE kst.key_id NOT IN (SELECT key_id
     //			    FROM group_keyring_table AS gkt2
     //			    INNER JOIN group_members_table AS gmt2 ON gkt2.group_id = gmt2.group_id
     //			    WHERE gmt2.group_id != gmt.group_id	    // where the key does not belong to another group
     //			    AND gmt2.user_id = gmt.user_id )	    // and the user is a member of that group
     // AND gkt.group_id = [this group]
     // AND gmt.group_id = [this group]
     // ...It also might be possible to do this using MySQL "foreign keys"
 }
 function test_dropId_Multi()
 {
     // load db
     // ======================================================
     $add_tokens = array("one", "two", "three", "four", "five");
     try {
         $add_result = $this->cls->addToken($add_tokens);
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 3, 'text' => "addToken exception", 'data' => array("tokens" => $add_tokens), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
         return false;
     }
     // Test  single dropId
     // ======================================================
     try {
         $drop_ids = array(1, 2, 3, 4, 5);
         $this->assertEquals(5, $this->cls->dropId($drop_ids));
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 4, 'text' => "dropId exception", 'data' => array("drop ids" => $drop_ids), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
         return false;
     }
     // Check Db has no rows
     // ======================================================
     try {
         $db = new FOX_db();
         $this->assertEquals(false, $db->runSelectQuery(FOX_test_dictionary::$struct));
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 5, 'text' => "DB select exception", 'data' => array("ctrl" => array("count" => true, "format" => "var")), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
         return false;
     }
     // Check Class cache is empty
     $this->assertEquals(array('ids' => array(), 'tokens' => array()), $this->cls->cache);
 }
 /**
  * Loads group keys into the cache.
  *
  * @version 1.0
  * @since 1.0
  *
  * @param int/array $group_id | Single group id as int. Multiple group id's as array of int.
  * @param int/array $key_id | Single key id as int. Multiple key ids as array of int.
  * @param bool $skip_load | If set true, the function will not update the class cache array from
  *			    the persistent cache before adding data from a db call and saving it
  *			    back to the persistent cache.
  * @return bool | False on failure. True on success.
  */
 public function load($group_id, $key_id = null, $skip_load = false)
 {
     global $fox;
     $db = new FOX_db();
     $args = array();
     $args = array(array("col" => "group_id", "op" => "=", "val" => $group_id));
     if ($key_id) {
         $args[] = array("col" => "key_id", "op" => "=", "val" => $key_id);
     }
     $columns = array("mode" => "include", "col" => array("group_id", "key_id"));
     $ctrl = array("format" => "array_key_array_true", "key_col" => array("group_id", "key_id"));
     try {
         $db_result = $db->runSelectQuery(self::$struct, $args, $columns, $ctrl);
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 1, 'text' => "DB select exception", 'data' => array("args" => $args, "columns" => $columns, "ctrl" => $ctrl), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
     }
     // Load the class cache array from the persistent cache
     if (!$skip_load) {
         try {
             self::loadCache();
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 2, 'text' => "loadCache exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
     }
     if ($db_result) {
         // When the function is called with specific keys to look for, if a group doesn't
         // have that key, we can add that information to the cache. This saves a query
         // the next time the key is requested.
         if ($key_id) {
             if (!is_array($group_id)) {
                 $group_id = array($group_id);
             }
             if (!is_array($key_id)) {
                 $key_id = array($key_id);
             }
             foreach ($group_id as $group) {
                 foreach ($key_id as $key) {
                     if (FOX_sUtil::keyExists($key, $db_result[$group])) {
                         $this->cache["keys"][$group][$key] = true;
                     } else {
                         $this->cache["keys"][$group][$key] = false;
                     }
                 }
                 unset($key);
             }
             unset($group);
         } else {
             // If the function is called with only a group_id, all of the keys that
             // group has will be added to the cache. But we cannot add information
             // about what keys a group *doesn't* have to the list, because we don't
             // have a list of keys to check against.
             foreach ($db_result as $group => $keys) {
                 // Flag the entire group as cached
                 $this->cache["groups"][$group] = true;
                 foreach ($keys as $key => $fake_var) {
                     // $fake_var is needed because we're operating on
                     // key names, not key values
                     $this->cache["keys"][$group][$key] = true;
                 }
                 unset($key);
             }
             unset($group, $keys);
         }
         // Write the updated local cache array to the persistent cache
         try {
             $cache_ok = self::saveCache();
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 3, 'text' => "saveCache exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         return $cache_ok;
     } else {
         return false;
     }
 }
Example #19
0
 /**
  * Loads key, branch, tree, or all data for one or more users from the db
  * into the key cache
  *
  * @version 1.0
  * @since 1.0
  *
  * @param int/array $user_id | Single user id as int. Multiple user id as array of int.
  * @param int/array $tree | Single tree id as int. Multiple tree id as array of int.
  * @param int/array $branch | Single branch id as int. Multiple branch id as array of int.
  * @param string/array $key | Single key as string. Multiple keys as array of strings.
  * @param bool $skip_load | If set true, the function will not update the class cache array from
  *			    the persistent cache before adding data from a db call and saving it
  *			    back to the persistent cache.
  * @return bool | False on failure. True on success.
  */
 public function load($user_id, $tree = null, $branch = null, $key = null, $skip_load = false)
 {
     global $rad;
     $db = new FOX_db();
     // Run query
     // ===========================================================
     $args = array(array("col" => "user_id", "op" => "=", "val" => $user_id));
     if ($tree) {
         $args[] = array("col" => "tree", "op" => "=", "val" => $tree);
         if ($branch) {
             $args[] = array("col" => "branch", "op" => "=", "val" => $branch);
             if ($key) {
                 $args[] = array("col" => "node", "op" => "=", "val" => $key);
             }
         }
     }
     $ctrl = array("format" => "array_key_array", "key_col" => array("user_id", "tree", "branch", "node"));
     try {
         $db_result = $db->runSelectQuery(self::$struct, $args, $columns = null, $ctrl);
     } catch (FOX_exception $child) {
         $debug_args = array("user_id" => $user_id, "tree" => $tree, "branch" => $branch, "key" => $key, "skip_load" => $skip_load);
         throw new FOX_exception(array('numeric' => 1, 'text' => "Db select exception ", 'data' => $debug_args, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
     }
     // Update cache
     // ===========================================================
     if (!$db_result) {
         // If the query returned zero results, no further work needs to be done
         return true;
     } else {
         // Key or array of keys from the *same branch* and *same tree*
         // --------------------------------------------------------------
         if ($user_id && $tree && $branch && $key) {
             // Load the persistent cache record for the user_id into the class's cache array
             if (!$skip_load) {
                 try {
                     $this->cache[$user_id] = $rad->cache->get("FOX_uData", $user_id, $valid);
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 2, 'text' => "Cache get exception", 'data' => array("user_id" => $user_id, "tree" => $tree, "branch" => $branch, "key" => $key), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
             }
             // Overwrite the class cache array with the data fetched in the db query
             foreach ($db_result[$user_id][$tree][$branch] as $key_name => $val) {
                 $this->cache[$user_id]["keys"][$tree][$branch][$key_name] = $val["val"];
             }
             // Overwrite the persistent cache record with the updated class cache array
             try {
                 $cache_ok = $rad->cache->set("FOX_uData", $user_id, $this->cache[$user_id]);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 3, 'text' => "Cache set exception", 'data' => array("user_id" => $user_id, "tree" => $tree, "branch" => $branch, "key" => $key), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
         } elseif ($user_id && $tree && $branch && !$key) {
             // Load the persistent cache record for the user_id into the class's cache array
             if (!$skip_load) {
                 try {
                     $this->cache[$user_id] = $rad->cache->get("FOX_uData", $user_id, $valid);
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 4, 'text' => "Cache get exception", 'data' => array("user_id" => $user_id, "tree" => $tree, "branch" => $branch), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
             }
             // Overwrite the class cache array with the data fetched in the db query
             foreach ($db_result[$user_id][$tree] as $branch_name => $keys) {
                 foreach ($keys as $key_name => $val) {
                     $this->cache[$user_id]["keys"][$tree][$branch_name][$key_name] = $val["val"];
                 }
                 // Set the branch cache
                 $this->cache[$user_id]["branch"][$tree][$branch_name] = true;
             }
             // Overwrite the persistent cache record with the updated class cache array
             try {
                 $cache_ok = $rad->cache->set("FOX_uData", $user_id, $this->cache[$user_id]);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 5, 'text' => "Cache set exception", 'data' => array("user_id" => $user_id, "tree" => $tree, "branch" => $branch), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
         } elseif ($user_id && $tree && !$branch && !$key) {
             // Load the persistent cache record for the user_id into the class's cache array
             if (!$skip_load) {
                 try {
                     $this->cache[$user_id] = $rad->cache->get("FOX_uData", $user_id, $valid);
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 6, 'text' => "Cache get exception", 'data' => array("user_id" => $user_id, "tree" => $tree), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
             }
             // Overwrite the class cache array with the data fetched in the db query
             foreach ($db_result[$user_id] as $tree_name => $branches) {
                 foreach ($branches as $branch_name => $keys) {
                     foreach ($keys as $key_name => $val) {
                         $this->cache[$user_id]["keys"][$tree_name][$branch_name][$key_name] = $val["val"];
                     }
                 }
                 // Set the tree cache
                 $this->cache[$user_id]["tree"][$tree_name] = true;
             }
             // Overwrite the persistent cache record with the updated class cache array
             try {
                 $cache_ok = $rad->cache->set("FOX_uData", $user_id, $this->cache[$user_id]);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 7, 'text' => "Cache set exception", 'data' => array("user_id" => $user_id, "tree" => $tree), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
         } elseif ($user_id && !$tree && !$branch && !$key) {
             $cache_ok = true;
             foreach ($db_result as $single_user => $trees) {
                 // Load the persistent cache record for the user_id into the class's cache array
                 if (!$skip_load) {
                     try {
                         $this->cache[$single_user] = $rad->cache->get("FOX_uData", $single_user, $valid);
                     } catch (FOX_exception $child) {
                         throw new FOX_exception(array('numeric' => 8, 'text' => "Cache get exception", 'data' => array("user_id" => $user_id), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                     }
                 }
                 // Overwrite the class cache array with the data fetched in the db query
                 foreach ($trees as $tree_name => $branches) {
                     foreach ($branches as $branch_name => $keys) {
                         foreach ($keys as $key_name => $val) {
                             $this->cache[$single_user]["keys"][$tree_name][$branch_name][$key_name] = $val["val"];
                         }
                     }
                 }
                 // Set the all_cached flag
                 $this->cache[$single_user]["all_cached"] = true;
                 // Overwrite the persistent cache record with the updated class cache array
                 try {
                     $cache_ok = $rad->cache->set("FOX_uData", $single_user, $this->cache[$single_user]);
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 9, 'text' => "Cache set exception", 'data' => array("user_id" => $user_id), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
             }
         }
         return true;
     }
 }
 /**
  * Returns an array of logged events based on supplied parameters. This function integrates with a jQuery
  * datagrid control in the admin screens, allowing arbitrary sorting and filtering of events.
  *
  * @param array $args | Args in the form: array("col"=>column_name, "op" => "<, >, =, !=", "val" => "int | string | array()")
  *	    => ARR @param array $args | Args in the form: array("col"=>column_name, "op"=>"<, >, =, !=", "val"=>"int | string | array()")
  *		=> ARR @param int '' | Array index
  *		    => VAL @param string $col | Name of the column this key describes	 *
  *		    => VAL @param string $op | SQL comparison operator to use: ">=" | "<=" | ">" | "<" | "=" | "!=" | "<>"
  *		    => VAL @param int/string/array $val | Value or values to test against. Single value as int/string. Multiple values as array.	 *
  *
  * @param object/array $columns | Either array with specific columns to include/exclue or anything else to return all columns.
  *	=> ARR @param int '' | Array index
  *	    => VAL @param string $table_class | Class of table that the column is from.
  *	    => VAL @param string $table_alias | Alias table that the column is from. Used instead of table_class not required
  *	    => VAL @param string $col_name | Name of the column
  *	    => VAL @param string $col_alias | Alias of the column
  *	    => VAL @param bool $count | True to count this column
  *	    => VAL @param bool/array $sum | True sum this column, array sums multiple columns
  *	        => ARR @param int '' | Array index
  *			=> VAL @param string $table_alias | Table alias of table that the column is from. if table alias is not set the default is t(number)
  *			=> VAL @param string $col_name | Name of the column
  *			=> VAL @param string $col_alias | Column alias used instead of table alias and col_name
  *			=> VAL @param bool $count | True to count this column
  *			=> VAL @param string $op | Operation to perform on column value +,- or *(muliple) default +
  *
  * @param array $ctrl | Control parameters for the query
  *	=> VAL @param int $page | Set current page (used when traversing multi-page data sets)
  *	=> VAL @param int $per_page | Max number of rows to return in a query / number of rows to return per page when transversing a multi-page data set
  *	=> VAL @param int $offset | Shift results page forward or backward "n" items within the returned data set
  *	=> ARR @param array $sort | Sort results by supplied parameters. Multi-dimensional sorts possible by passing multiple arrays.
  *	    => ARR @param int '' | Array index
  *		=> VAL @param string $class | Class name that owns the table
  *		=> VAL @param string $col | Name of column to sort by
  *		=> VAL @param string $col_alias | Column alias used instead of class and col. not required
  *		=> VAL @param string/array $sort | Direction to sort in. "ASC", "DESC", array(val, val, val) where position in array
  *						   is the desired position in the results set for the row or rows with columm matching val
  *	=> ARR @param array $group | Apply SQL GROUP to columns. Multi-dimensional group possible by passing multiple arrays.
  *	    => ARR @param int '' | Array index
  *		=> VAL @param string $class | Class name that owns the table
  *		=> VAL @param string $col | Name of column to apply GROUP to
  *		=> VAL @param string $col_alias | Column alias used instead of class and col. not required
  *		=> VAL @param string $sort | Direction to sort in. "ASC" | "DESC"
  *	=> VAL @param string $format | @see FOX_db::runQuery() for detailed info on format string
  *	=> VAL @param string $key_col | Column name to get key names from when using $format="key" or $format="asc"
  *	=> VAL @param string $asc_col | Column name to use as value when using $format="asc"
  *
  * @param bool $return_tokens | True to use dictionary and return words for tree, branch and node
  *
  * @return bool/int/array | False on failure. Int on count. Array of rows on success.
  */
 public function query($args = null, $columns = null, $ctrl = null, $return_tokens = true)
 {
     if (!isset($ctrl)) {
         $ctrl = array();
     }
     if (!isset($ctrl['format'])) {
         $ctrl['format'] = "array_array";
     }
     $join_args = array("tree" => array(), "branch" => array(), "node" => array());
     if (is_array($args)) {
         // Process args in case dic entries are used instead of numbers
         foreach ($args as $arg) {
             // check dic columns
             if (in_array($arg["col"], array('tree', 'branch', 'node'))) {
                 // if val is string or array of strings
                 if (!is_numeric($arg["val"]) || is_array($arg["val"]) && !is_numeric($arg["val"][0])) {
                     // if val is string or array of strings then add to join_args and don't add to processed_args
                     $join_args[$arg['col']][] = $args;
                     continue;
                 }
             }
             $processed_args[] = $arg;
         }
     }
     $db = new FOX_db();
     // Check if need to use runSelectLeftJoin
     $num_join_args = count($join_args['tree']) + count($join_args['branch']) + count($join_args['node']);
     if ($return_tokens || $num_join_args > 0) {
         if (!isset($ctrl['group'])) {
             $ctrl["group"] = array(array("class" => self::$struct, "col" => "id", "sort" => "ASC"));
         }
         $primary = array("class" => self::$struct, "args" => $processed_args);
         $join = array(array("class" => "FOX_log_dictionary_tree", "on" => array("pri" => "tree", "op" => "=", "sec" => "id"), "args" => $join_args['tree']), array("class" => "FOX_log_dictionary_branch", "on" => array("pri" => "branch", "op" => "=", "sec" => "id"), "args" => $join_args['branch']), array("class" => "FOX_log_dictionary_node", "on" => array("pri" => "node", "op" => "=", "sec" => "id"), "args" => $join_args['node']));
         if (is_null($columns)) {
             if ($return_tokens) {
                 $columns = array(array("table_alias" => "t1", "col_name" => "id", "col_alias" => "id"), array("table_alias" => "t2", "col_name" => "token", "col_alias" => "tree"), array("table_alias" => "t3", "col_name" => "token", "col_alias" => "branch"), array("table_alias" => "t4", "col_name" => "token", "col_alias" => "node"), array("table_alias" => "t1", "col_name" => "user_id", "col_alias" => "user_id"), array("table_alias" => "t1", "col_name" => "level", "col_alias" => "level"), array("table_alias" => "t1", "col_name" => "date", "col_alias" => "date"), array("table_alias" => "t1", "col_name" => "summary", "col_alias" => "summary"), array("table_alias" => "t1", "col_name" => "data", "col_alias" => "data"));
             } else {
                 $columns = array(array("table_alias" => "t1", "col_name" => "id", "col_alias" => "id"), array("table_alias" => "t1", "col_name" => "tree", "col_alias" => "tree"), array("table_alias" => "t1", "col_name" => "branch", "col_alias" => "branch"), array("table_alias" => "t1", "col_name" => "node", "col_alias" => "node"), array("table_alias" => "t1", "col_name" => "user_id", "col_alias" => "user_id"), array("table_alias" => "t1", "col_name" => "level", "col_alias" => "level"), array("table_alias" => "t1", "col_name" => "date", "col_alias" => "date"), array("table_alias" => "t1", "col_name" => "summary", "col_alias" => "summary"), array("table_alias" => "t1", "col_name" => "data", "col_alias" => "data"));
             }
         }
         try {
             $result = $db->runSelectQueryLeftJoin($primary, $join, $columns, $ctrl);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 3, 'text' => "Error reading from DB \n", 'data' => array('primary' => $primary, 'join' => $join, 'columns' => $columns, 'ctrl' => $ctrl), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             return false;
         }
     } else {
         if (!is_null($columns) && count($columns) > 0) {
             $select_columns["mode"] = "include";
             if (count($columns) == 1) {
                 $select_columns["col"] = $columns[0]["col_name"];
             } else {
                 foreach ($columns as $col) {
                     $select_columns["col"][] = $col["col_name"];
                 }
             }
         }
         try {
             $result = $db->runSelectQuery(self::$struct, $processed_args, $select_columns, $ctrl);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 4, 'text' => "Error reading from DB \n", 'data' => array('primary' => $primary, 'join' => $join, 'columns' => $columns, 'ctrl' => $ctrl), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             return false;
         }
     }
     if ($result == 0) {
         return false;
     } else {
         return $result;
     }
 }
 /**
  * Deletes an entire tree of keys
  *
  * @version 1.0
  * @since 1.0
  *
  * @param string $tree | The tree name
  * @return bool | False on failure. True on success.
  */
 public function dropTree($tree)
 {
     global $fox;
     $db = new FOX_db();
     // Fetch the key_id's for all matching keys from the db
     $args = array(array("col" => "tree", "op" => "=", "val" => $tree));
     $columns = array("mode" => "include", "col" => array("key_id"));
     $ctrl = array("format" => "col");
     try {
         $drop_ids = $db->runSelectQuery(self::$struct, $args, $columns, $ctrl);
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 1, 'text' => "DB select exception", 'data' => array("args" => $args, "columns" => $columns, "ctrl" => $ctrl), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
     }
     // @@@@@@ BEGIN TRANSACTION @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     try {
         $started_transaction = $db->beginTransaction();
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 2, 'text' => "beginTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
     }
     if ($started_transaction) {
         $args = array(array("col" => "key_id", "op" => "=", "val" => $drop_ids));
         try {
             $db->runDeleteQuery(self::$struct, $args);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 3, 'text' => "DB delete exception", 'data' => $args, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         // TODO:
         // 1) drop the keys from the user keystore
         // 2) purge the dropped keys from the user keystore cache
         // 3) drop the keys from all groups that grant them
         try {
             $query_ok = $db->commitTransaction();
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 4, 'text' => "commitTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
     } else {
         $query_ok = false;
     }
     // @@@@@@ END TRANSACTION @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     // Update the cache
     if ($query_ok) {
         // Update the class cache from the persistent cache
         try {
             self::loadCache();
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 5, 'text' => "loadCache exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         // Flush the deleted branch from the class cache
         unset($this->cache["keys"][$tree_name]);
         unset($this->cache["branches"][$tree_name]);
         unset($this->cache["trees"][$tree_name]);
         // Write the updated class cache array to the persistent cache
         try {
             $cache_ok = self::saveCache();
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 6, 'text' => "saveCache exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         return $cache_ok;
     } else {
         return false;
     }
 }
Example #22
0
 /**
  * Loads module_slug data into the cache
  *
  * @version 1.0
  * @since 1.0
  *
  * @param array $data | Array of identifiers to cache, or null to cache all rows in the database
  *	=> VAL @param string/array $module_type | Single module_type as string. Multiple module_types as array of strings.
  *	=> VAL @param string/array $module_slug | Single module_slug as string. Multiple module_slugs as array of strings.
  *	=> VAL @param int/array $module_id | Single module_id as int. Multiple module_ids as array of int.
  *
  * @return array | Exception on failure. False on nonexistent. Results array on success.
  */
 public function load($data = null, $skip_load = false)
 {
     $db = new FOX_db();
     $struct = self::_struct();
     $args = array();
     // Build query args
     // ====================================================================
     if ($data["module_type"]) {
         $args[] = array("col" => "module_type", "op" => "=", "val" => $data["module_type"]);
     } elseif ($data["module_slug"]) {
         $args[] = array("col" => "module_slug", "op" => "=", "val" => $data["module_slug"]);
     } elseif ($data["module_id"]) {
         $args[] = array("col" => "module_id", "op" => "=", "val" => $data["module_id"]);
     }
     $ctrl = array("format" => "array_key_array", "key_col" => array("module_type", "module_slug"));
     $columns = null;
     try {
         $db_result = $db->runSelectQuery($struct, $args, $columns, $ctrl);
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 1, 'text' => "Error reading from database", 'data' => array('args' => $args, 'ctrl' => $ctrl), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
     }
     if (!$db_result) {
         // No results were found
         return false;
     } else {
         // Fetch the persistent cache image
         // ===================================================
         if ($skip_load) {
             $cache_image = $this->cache;
         } else {
             try {
                 $cache_image = self::readCache();
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 2, 'text' => "Cache read error", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
         }
         // Rebuild the cache image
         // ===================================================
         if (!$data["module_type"] && !$data["module_slug"] && !$data["module_id"]) {
             $cache_image["all_cached"] = true;
         } elseif (!$data["module_slug"] && !$data["module_id"]) {
             if (!is_array($data["module_type"])) {
                 $cache_module_types = array($data["module_type"]);
             } else {
                 $cache_module_types = $data["module_type"];
             }
             foreach ($cache_module_types as $module_type_name) {
                 $cache_image["module_types"][$module_type_name] = true;
             }
             unset($cache_module_types, $module_type_name);
         }
         foreach ($db_result as $module_type => $module_slugs) {
             foreach ($module_slugs as $module_slug => $module_slug_data) {
                 $cache_image["data"][$module_type][$module_slug] = $module_slug_data;
             }
             unset($module_type, $module_slugs);
         }
         unset($module_type, $module_slugs);
         // Update the persistent cache
         // ===================================================
         try {
             self::writeCache($cache_image);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 3, 'text' => "Cache write error", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         // Update the class cache
         $this->cache = $cache_image;
         return true;
     }
 }