/**
  * Creates a new object type
  *
  * @version 1.0
  * @since 1.0
  *
  * @param array $data |
  *	=> VAL @param int $module_id | id for the module that owns this type.
  *	=> VAL @param string $type_slug | Slug name for this type. Must be unique to all other slugs.
  *	=> VAL @param string $name_admin | Admin name for this object type. Max 255 characters.
  *	=> VAL @param string $txt_admin | Admin description for this object type.
  *	=> VAL @param string $action_txt | Text shown to user in create object link. Max 255 characters.
  *	=> VAL @param string $name_user | Name shown to users for this object type. Max 255 characters.
  *	=> VAL @param string $txt_user | Description shown to users for this object type.
  *
  * @return bool/int | Exception on failure. $type_id of the new object type on success.
  */
 public function addType($data)
 {
     $db = new FOX_db();
     $columns = array("mode" => "exclude", "col" => array("type_id"));
     try {
         $db->runInsertQuery($this->_struct(), $data, $columns);
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 1, 'text' => "Error writing to database", 'data' => array('data' => $data, 'columns' => $columns), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
     }
     // NOTE: The new type is *not* added to the "module_id_types" cache table in this function. This is to ensure
     // that if $cache->["module_id_types"]["module_id"] exists in the table, it contains an array of *all*
     // the type_ids for that module_id. This is more efficient than using a separate "module_id was cached"
     // table as we do in other classes, because new object types will rarely be added to the system.
     $type_id = $db->insert_id;
     // Update the cache
     // =====================================================================
     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
     $cache_image["slug_to_type_id"][$data["module_id"]][$data["type_slug"]] = $type_id;
     $cache_image["keys"][$type_id] = $data;
     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));
     }
     // Overwrite the class cache
     $this->cache = $cache_image;
     return $type_id;
 }
 /**
  * Creates a new access level within an object type.
  *
  * @version 1.0
  * @since 1.0
  *
  * @param array $data |
  *	=> VAL @param int $module_id | Module that owns the object type that owns the access level.
  *	=> VAL @param int $type_id | Object type that owns the access level.
  *	=> VAL @param string $level_slug | Slug name for this level. Must be unique to all other slugs.
  * 	=> VAL @param string $level_name | Name of the level. Max 64 characters.
  *	=> VAL @param string $level_desc | Description of the level. Max 255 characters.
  *	=> VAL @param int $rank | Rank of this level within the object type. 1-255 where 1 is the highest.
  *	=> VAL @param int $key_id | Key id required to access to this level
  *
  * @return int | Exception on failure. $level_id of the new level on success.
  */
 public function addLevel($data)
 {
     $db = new FOX_db();
     // Check that the rank doesn't already exist
     // ======================================================
     $args = array(array("col" => "module_id", "op" => "=", "val" => $data["module_id"]), array("col" => "type_id", "op" => "=", "val" => $data["type_id"]), array("col" => "rank", "op" => "=", "val" => $data["rank"]));
     $ctrl = array("format" => "var", "count" => true);
     try {
         $already_exists = $db->runSelectQuery($this->_struct(), $args, $columns = null, $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 ($already_exists) {
         throw new FOX_exception(array('numeric' => 2, 'text' => "Object type already exists", 'data' => $data, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
     } else {
         // Add to the database
         // ======================================================
         try {
             $db->runInsertQuery($this->_struct(), $data, $columns = null);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 3, 'text' => "Error writing to database", 'data' => $data, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
         }
         // NOTE: The new type is *not* added to the "type_levels" cache table in this function. This is to ensure
         // that if $cache->["type_levels"]["type_id"] exists in the table, it contains an array of *all*
         // the level_ids for that type_id. This is more efficient than using a separate "type_id was cached"
         // table as we do in other classes, because new type levels will rarely be added to the system.
         // Update the cache
         // ==============================
         $level_id = $db->insert_id;
         try {
             $cache_image = self::readCache();
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 4, 'text' => "Cache read error", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
         }
         // Rebuild the cache image
         $cache_image["slug_to_level_id"][$data["module_id"]][$data["type_id"]][$data["level_slug"]] = $level_id;
         $cache_image["keys"][$level_id] = $data;
         try {
             self::writeCache($cache_image);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 5, 'text' => "Cache write error", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
         }
         // Overwrite the class cache
         $this->cache = $cache_image;
         return (int) $level_id;
     }
 }
예제 #3
0
 /**
  * Adds an event to the event log
  *
  * @version 1.0
  * @since 1.0
  *
  * @param array $event | Array holding data to be added to DB. An array in this form: key string for the column with value as null | int | string | array
  *	=> KEY @param string "tree"
  *	    => VAL @param int $tree | id of the event's tree
  *	=> KEY @param string "branch"
  *	    => VAL @param int $branch | id of the event's branch
  *	=> KEY @param string "node"
  *	    => VAL @param int $node | id of the event's node
  *	=> KEY @param string "user_id"
  *	    => VAL @param int $user_id | id of the user_id associated with this event. Use int 0 for a system event
  *	=> KEY @param string "level"
  *	    => VAL @param int $level | severity of this event. 1-255, where 1 is the most serious
  *	=> KEY @param string "date"
  *	    => VAL @param int $date | date and time the event occured, as linux datetime
  *	=> KEY @param string "summary"
  *	    => VAL @param string $summary | Human readable summary of the event. Max 128 characters.
  *	=> KEY @param string "data"
  *	    => VAL @param mixed $data | Full data associated with this event. Any PHP data type can be stored.
  *
  * @return bool/int | False on failure. ID of the created log entry on success.
  */
 public function add($event)
 {
     if (empty($event)) {
         throw new FOX_exception(array('numeric' => 1, 'text' => "No event specified for addition", 'data' => $event, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => null));
         return false;
     }
     // Convert tree, branch, and node strings to ids
     // ===================================================================
     if (is_string($event["tree"]) && is_string($event["branch"]) && is_string($event["node"])) {
         try {
             $tree_id = $this->dict_tree->getToken($event["tree"]);
             $branch_id = $this->dict_branch->getToken($event["branch"]);
             $node_id = $this->dict_node->getToken($event["node"]);
         } catch (FOX_exception $child) {
             echo "Error reading dictionary";
             throw new FOX_exception(array('numeric' => 2, 'text' => "Error reading dictionary", 'data' => array("tree_id" => $event["tree"], "branch_id" => $event["branch"], "node_id" => $event["node"]), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             return false;
         }
     } else {
         $tree_id = $event["tree"];
         $branch_id = $event["branch"];
         $node_id = $event["node"];
     }
     // Check date
     if (!isset($event["date"])) {
         $event["date"] = time();
     }
     // Add row to database
     // ===================================================================
     $db = new FOX_db();
     $data = array("tree" => $tree_id, "branch" => $branch_id, "node" => $node_id, "user_id" => $event["user_id"], "level" => $event["level"], "date" => $event["date"], "summary" => $event["summary"], "data" => $event["data"]);
     try {
         $query_result = $db->runInsertQuery(self::$struct, $data, $columns = null, $ctrl = null);
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 3, 'text' => "Error writing to database", 'data' => $data, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         return false;
     }
     return $db->insert_id;
 }
예제 #4
0
 /**
  * Adds one or more keys to one or more groups' keyrings
  *
  * @version 1.0
  * @since 1.0
  *
  * @param int $group_id | id of the group as int. Multiple groups as array of int.
  * @param int/array $key_id | id of the key as int. Multiple keys as array of int.
  * @return bool | False on failure. True on success.
  */
 public function addKey($group_id, $key_id)
 {
     global $fox;
     $db = new FOX_db();
     // Force a load of the entire class cache from the persistent cache
     try {
         self::loadCache();
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 1, 'text' => "loadCache exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
     }
     // CASE 1: Single group_id, single key
     // =================================================================
     if (!is_array($group_id) && !is_array($key_id)) {
         // If the group already has the key, return true to indicate no db rows were changed
         if (self::hasKey($group_id, $key_id)) {
             return true;
         } else {
             $data = array("group_id" => $group_id, "key_id" => $key_id);
             try {
                 $result = $db->runInsertQuery(self::$struct, $data, $columns = null);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 2, 'text' => "Db insert exception", 'data' => $data, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             if ($result) {
                 // Update the 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));
                 }
                 if ($cache_ok) {
                     return $result;
                 } else {
                     return false;
                 }
             } else {
                 return $result;
             }
         }
     }
     // CASE 2: Single group_id, multiple keys
     // =================================================================
     if (!is_array($group_id) && is_array($key_id)) {
         $keys_to_add = array();
         // Create an array of "to be added" keys the user doesn't have. Note this algorithm
         // also eliminates duplicate keys, so we don't have to use array_unique() to avoid
         // a failed query if duplicate keys are present in $key_id
         foreach ($key_id as $key) {
             if (!self::hasKey($group_id, $key)) {
                 $keys_to_add[] = $key;
             }
         }
         unset($key);
         // Check that there are actually keys to add, because running an insert query
         // with an empty data array will cause an SQL error
         if (count($keys_to_add) > 0) {
             // Add the new keys to the database
             $data = array();
             foreach ($keys_to_add as $key) {
                 $data[] = array("group_id" => $group_id, "key_id" => $key);
             }
             unset($key);
             try {
                 $result = $db->runInsertQueryMulti(self::$struct, $data, $columns = null, $ctrl = null);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 4, 'text' => "DB insert exception", 'data' => $data, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             // Update the cache
             if ($result) {
                 foreach ($keys_to_add as $key) {
                     $this->cache["keys"][$group_id][$key] = true;
                 }
                 unset($key);
                 try {
                     $cache_ok = self::saveCache();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 5, 'text' => "saveCache exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 if ($cache_ok) {
                     return $result;
                 } else {
                     return false;
                 }
             } else {
                 return $result;
             }
         } else {
             return true;
         }
     }
     // CASE 3: Multiple group_id's, single key
     // =================================================================
     if (is_array($group_id) && !is_array($key_id)) {
         $groups_to_add = array();
         // Create an array of groups to be given the key that don't have the key yet
         foreach ($group_id as $group) {
             if (!self::hasKey($group, $key_id)) {
                 $groups_to_add[] = $group;
             }
         }
         unset($group);
         // Check that there are actually keys to add, because running an insert query
         // with an empty data array will cause an SQL error
         if (count($groups_to_add) > 0) {
             // Add the new keys to the database
             $data = array();
             foreach ($groups_to_add as $group) {
                 $data[] = array("group_id" => $group, "key_id" => $key_id);
             }
             unset($group);
             try {
                 $result = $db->runInsertQueryMulti(self::$struct, $data, $columns = null, $ctrl = null);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 6, 'text' => "DB insert exception", 'data' => $data, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             // Update the cache
             if ($result) {
                 foreach ($groups_to_add as $group) {
                     $this->cache["keys"][$group][$key_id] = true;
                 }
                 unset($group);
                 try {
                     $cache_ok = self::saveCache();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 7, 'text' => "saveCache exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 if ($cache_ok) {
                     return $result;
                 } else {
                     return false;
                 }
             } else {
                 return $result;
             }
         } else {
             return true;
         }
     }
     // CASE 4: Multiple group_id's, multiple keys (all groups get same keys)
     // =================================================================
     if (is_array($group_id) && is_array($key_id)) {
         // Create an array of "missing keys" that need to be added. Note this algorithm
         // also eliminates duplicate keys, so we don't have to use array_unique() to avoid
         // a failed query if duplicate keys are present in $key_id
         $keys_to_add = array();
         foreach ($group_id as $group) {
             foreach ($key_id as $key) {
                 if (!self::hasKey($group, $key)) {
                     $keys_to_add[$group][] = $key;
                 }
             }
         }
         unset($group, $key);
         // Check that there are actually keys to add, because running an insert query
         // with an empty data array will cause an SQL error
         if (count($keys_to_add) > 0) {
             // Add the new keys to the database
             $data = array();
             foreach ($keys_to_add as $group => $keys) {
                 foreach ($keys as $key) {
                     $data[] = array("group_id" => $group, "key_id" => $key);
                 }
             }
             unset($group, $key, $keys);
             try {
                 $result = $db->runInsertQueryMulti(self::$struct, $data, $columns = null, $ctrl = null);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 8, 'text' => "DB insert exception", 'data' => $data, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             // Update the cache
             if ($result) {
                 foreach ($keys_to_add as $group => $keys) {
                     foreach ($keys as $key) {
                         $this->cache["keys"][$group][$key] = true;
                     }
                 }
                 unset($group, $key, $keys);
                 try {
                     $cache_ok = self::saveCache();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 1, 'text' => "saveCache exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 if ($cache_ok) {
                     return $result;
                 } else {
                     return false;
                 }
             } else {
                 return $result;
             }
         } else {
             return true;
         }
         // ENDOF: if( count($keys_to_add) > 0 )
     }
     // ENDOF: if( is_array($group_id) && is_array($key_id) )
 }
예제 #5
0
 /**
  * Creates a single group
  *
  * @version 1.0
  * @since 1.0
  *
  * @param string $name | name of group (max 32 chars)
  * @param string $title | title of group (max 128 chars)
  * @param string $caption | caption of group
  * @param bool $is_default | if set true, this group is the default group
  * @param int $icon | media_id of image to use as icon for the group
  * @return bool | False on failure. Created group's id on success.
  */
 public function createGroup($data)
 {
     if (!FOX_sUtil::keyExists('name', $data) || empty($data['name'])) {
         throw new FOX_exception(array('numeric' => 1, 'text' => "Missing or invalid 'name' key", 'data' => $data, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => null));
     }
     if (!FOX_sUtil::keyExists('is_default', $data) || $data['is_default'] === null) {
         $data['is_default'] = false;
     }
     // IMPORTANT: Default group flag rules
     // ==========================================
     // 1) Only one group can be the default group
     // 2) There must always be a default group
     $db = new FOX_db();
     $ctrl = array("format" => "var", "count" => true);
     try {
         $groups_count = $db->runSelectQuery(self::$struct, $args = null, $columns = null, $ctrl);
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 1, 'text' => "DB select exception", 'data' => array("ctrl" => $ctrl), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
     }
     // CASE 1: No other groups exist
     // =============================================================================
     if ($groups_count == 0) {
         $data["is_default"] = true;
         // Force $is_default flag to be true
         // Insert new group into the db
         try {
             $insert_ok = $db->runInsertQuery(self::$struct, $data, $columns = null);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 2, 'text' => "DB insert exception", 'data' => array("data" => $data), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         $new_group_id = $db->insert_id;
         if ($insert_ok && $new_group_id) {
             $result = true;
         } else {
             $result = false;
         }
     } elseif ($data['is_default'] == true && $groups_count != 0) {
         // Find $group_id of current default group
         $args = array(array("col" => "is_default", "op" => "=", "val" => "1"));
         $columns = array("mode" => "include", "col" => "group_id");
         $ctrl = array("format" => "var");
         try {
             $old_default = $db->runSelectQuery(self::$struct, $args, $columns, $ctrl);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 3, '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) {
             // Insert new group into the db
             try {
                 $insert_ok = $db->runInsertQuery(self::$struct, $data, $columns = null);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 3, 'text' => "DB insert exception", 'data' => $data, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             $new_group_id = $db->insert_id;
             // Remove remove default flag from old group
             $unset_default = array("is_default" => false);
             try {
                 $unset_ok = $db->runUpdateQueryCol(self::$struct, $unset_default, "group_id", "=", $old_default);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 4, 'text' => "DB update exception", 'data' => array("data" => $unset_default, "col" => "group_id", "op" => "=", "val" => $old_default), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             // If all queries were successful, commit the transaction
             if ($insert_ok && $new_group_id && $unset_ok) {
                 try {
                     $result = $db->commitTransaction();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 5, 'text' => "commitTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
             } else {
                 //echo "\ninsert_ok:$insert_ok new_group_id: $new_group_id old_default:$old_default unset_ok:$unset_ok";
                 try {
                     $db->rollbackTransaction();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 6, 'text' => "rollbackTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 $result = false;
             }
         } else {
             $result = false;
         }
         // @@@@@@ END TRANSACTION @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     } else {
         // Insert new group into the db
         try {
             $insert_ok = $db->runInsertQuery(self::$struct, $data, $columns = null);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 7, 'text' => "DB insert exception", 'data' => array("data" => $data), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         $new_group_id = $db->insert_id;
         if ($insert_ok && $new_group_id) {
             $result = true;
         } else {
             $result = false;
         }
     }
     // Update the cache
     // =========================================
     if ($new_group_id && $result) {
         try {
             self::loadCache();
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 4, 'text' => "loadCache exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         $this->cache["ids"][$data['name']] = $new_group_id;
         if ($data['is_default']) {
             $this->cache["default_id"] = $new_group_id;
         }
         try {
             $cache_ok = self::saveCache();
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 5, 'text' => "saveCache exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         if ($cache_ok) {
             return $new_group_id;
         } else {
             return false;
         }
     } else {
         return false;
     }
 }
예제 #6
0
 /**
  * Adds one or more users to 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 ids as array of int.
  * @return bool | False on failure. True on success.
  */
 public function addToGroup($user_id, $group_id)
 {
     global $fox;
     $db = new FOX_db();
     // CASE 1: Single user_id, single group_id
     // =================================================================
     if (!is_array($user_id) && !is_array($group_id)) {
         // Check if the user is already a member of the group
         if (self::inGroup($user_id, $group_id)) {
             return true;
         }
         // Get the group's keyring
         try {
             $gk = new FOX_uGroupKeyRing();
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 1, 'text' => "FOX_uGroupKeyRing constructor exception", 'data' => array("group_id" => $group_id), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         try {
             $group_keyring = $gk->getKeys($group_id);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 2, 'text' => "FOX_uGroupKeyRing getKeys exception", 'data' => array("group_id" => $group_id), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         // @@@@@@ BEGIN TRANSACTION @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         if ($db->beginTransaction()) {
             // Add the user-group pair to the group members table
             $data = array("user_id" => $user_id, "group_id" => $group_id);
             try {
                 $group_ok = $db->runInsertQuery(self::$struct, $data, $columns = null);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 3, 'text' => "DB insert exception", 'data' => $data, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             // Grant the user the group's keyring
             if ($group_keyring) {
                 $ks = new FOX_uKeyRing();
                 try {
                     $keys_ok = $ks->grantKey($user_id, $group_keyring);
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 4, 'text' => "FOX_uKeyRing exception", 'data' => array("user_id" => $user_id, "group_keyring" => $group_keyring), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
             } else {
                 // Handle groups with no keys in their keyring
                 $keys_ok = true;
             }
             // If all operations were successful, commit the transaction
             if ($group_ok && $keys_ok) {
                 try {
                     $commit_ok = $db->commitTransaction();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 5, '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] = true;
                     $cache_ok = $fox->cache->set("FOX_uGroupMember", $user_id, $this->cache[$user_id]);
                     return $cache_ok;
                 } else {
                     return false;
                 }
             } else {
                 try {
                     $db->rollbackTransaction();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 6, '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)) {
         // Load all of the user's "to be added" groups into the cache
         try {
             self::load($user_id, $group_id);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 7, 'text' => "FOX_uGroupMember load exception", 'data' => array("user_id" => $user_id, "group_id" => $group_id), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         $groups_to_add = array();
         // Create an array of all the groups the user needs to be added to
         foreach ($group_id as $group) {
             if (!$this->cache[$user_id]["keys"][$group]) {
                 $groups_to_add[] = $group;
             }
         }
         unset($group, $in_cache);
         // If the user is already a member of all the requested groups, quit
         if (empty($groups_to_add)) {
             return true;
         }
         // Get combined keyring of groups to add the user to
         try {
             $gk = new FOX_uGroupKeyRing();
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 8, 'text' => "FOX_uGroupKeyRing constructor exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         try {
             $keyring = $gk->getKeys($groups_to_add);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 9, 'text' => "FOX_uGroupKeyRing getKeys exception", 'data' => array("groups_to_add" => $groups_to_add), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         // @@@@@@ BEGIN TRANSACTION @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         try {
             $transaction_started = $db->beginTransaction();
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 10, 'text' => "beginTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         if ($transaction_started) {
             // Add the user-group pairs to the group members table
             $data = array();
             foreach ($groups_to_add as $group) {
                 $data[] = array("user_id" => $user_id, "group_id" => $group);
             }
             unset($group);
             try {
                 $group_ok = $db->runInsertQueryMulti(self::$struct, $data, $columns = null, $ctrl = null);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 11, 'text' => "DB insert exception", 'data' => $data, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             // Grant the user the groups' combined keyrings
             if ($keyring) {
                 $ks = new FOX_uKeyRing();
                 try {
                     $keys_ok = $ks->grantKey($user_id, $keyring);
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 12, 'text' => "FOX_uKeyRing grantKey exception", 'data' => array("user_id" => $user_id, "group_id" => $group_id), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
             } else {
                 // Handle none of the groups having any keys on their keyring
                 $keys_ok = true;
             }
             // If all operations were successful, commit the transaction
             if ($group_ok && $keys_ok) {
                 try {
                     $commit_ok = $db->commitTransaction();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 13, '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 ($groups_to_add as $group) {
                         $this->cache[$user_id]["keys"][$group] = true;
                     }
                     unset($group);
                     $cache_ok = $fox->cache->set("FOX_uGroupMember", $user_id, $this->cache[$user_id]);
                     return $cache_ok;
                 } else {
                     return false;
                 }
             } else {
                 try {
                     $db->rollbackTransaction();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 14, '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 user's to be added to the group into the cache
         try {
             self::load($user_id, $group_id);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 15, 'text' => "FOX_uGroupMember load exception", 'data' => array("user_id" => $user_id, "group_id" => $group_id), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         $users_to_add = array();
         // Create an array of "to be added" users that aren't members of the group yet
         foreach ($user_id as $user) {
             if (!$this->cache[$user]["keys"][$group_id]) {
                 $users_to_add[] = $user;
             }
         }
         unset($user, $in_cache);
         // If there are no users that are not already in the group, quit
         if (empty($users_to_add)) {
             return true;
         }
         // Get the group's keyring
         try {
             $gk = new FOX_uGroupKeyRing();
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 16, 'text' => "FOX_uGroupKeyRing constructor exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         try {
             $group_keyring = $gk->getKeys($group_id);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 17, 'text' => "FOX_uGroupKeyRing getKeys exception", 'data' => array("group_id" => $group_id), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         // @@@@@@ BEGIN TRANSACTION @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         try {
             $transaction_started = $db->beginTransaction();
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 18, 'text' => "beginTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         if ($transaction_started) {
             // Add the user-group pairs to the groupstore db table
             $data = array();
             foreach ($users_to_add as $user) {
                 $data[] = array("user_id" => $user, "group_id" => $group_id);
             }
             unset($user);
             try {
                 $group_ok = $db->runInsertQueryMulti(self::$struct, $data, $columns = null, $ctrl = null);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 19, 'text' => "DB insert exception", 'data' => $data, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             // Grant the group's keyring to each user
             if ($group_keyring) {
                 $ks = new FOX_uKeyRing();
                 $keys_ok = true;
                 foreach ($users_to_add as $user) {
                     try {
                         $grant_ok = $ks->grantKey($user, $group_keyring);
                     } catch (FOX_exception $child) {
                         throw new FOX_exception(array('numeric' => 20, 'text' => "FOX_uKeyRing grantKey exception", 'data' => array("user" => $user, "group_key_ring" => $group_keyring), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                     }
                     if (!$grant_ok) {
                         $keys_ok = false;
                     }
                 }
                 unset($user);
             } else {
                 // Handle the group having no keys on its keyring
                 $keys_ok = true;
             }
             // If all operations were successful, commit the transaction
             if ($group_ok && $keys_ok) {
                 try {
                     $commit_ok = $db->commitTransaction();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 21, 'text' => "commitTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 // Update the cache
                 if ($commit_ok) {
                     $all_ok = true;
                     foreach ($users_to_add as $user) {
                         // Load, update, writeback
                         $this->cache[$user] = $fox->cache->get("FOX_uGroupMember", $user);
                         $this->cache[$user]["keys"][$group_id] = true;
                         $cache_ok = $fox->cache->set("FOX_uGroupMember", $user, $this->cache[$user]);
                         if (!$cache_ok) {
                             $all_ok = false;
                         }
                     }
                     unset($user);
                     return $all_ok;
                 } else {
                     return false;
                 }
             } else {
                 try {
                     $db->rollbackTransaction();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 22, '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 user-group pairs to be added into the cache
         try {
             self::load($user_id, $group_id);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 23, 'text' => "FOX_uGroupMember load exception", 'data' => array("user_id" => $user_id, "group_id" => $group_id), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         // Create an array of user-group pairs that need to be added
         $groups_to_add = array();
         foreach ($user_id as $user) {
             foreach ($group_id as $group) {
                 if (!$this->cache[$user]["keys"][$group]) {
                     $groups_to_add[$user][] = $group;
                 }
             }
         }
         unset($user, $group);
         // @@@@@@ BEGIN TRANSACTION @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         try {
             $transaction_started = $db->beginTransaction();
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 24, 'text' => "beginTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         if ($transaction_started) {
             $all_ok = true;
             try {
                 $gk = new FOX_uGroupKeyRing();
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 25, 'text' => "FOX_uGroupKeyRing constructor exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             foreach ($user_id as $user) {
                 if ($groups_to_add[$user]) {
                     // Add the user-group pairs to the groupstore db table
                     $data = array();
                     $add_group_ids = array();
                     foreach ($groups_to_add[$user] as $group) {
                         $data[] = array("user_id" => $user, "group_id" => $group);
                         $add_group_ids[] = $group;
                     }
                     unset($group);
                     try {
                         $groups_ok = $db->runInsertQueryMulti(self::$struct, $data, $columns = null, $ctrl = null);
                     } catch (FOX_exception $child) {
                         throw new FOX_exception(array('numeric' => 26, 'text' => "DB insert exception", 'data' => $data, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                     }
                     // Get the groups' combined keyring
                     try {
                         $keyring = $gk->getKeys($add_group_ids);
                     } catch (FOX_exception $child) {
                         throw new FOX_exception(array('numeric' => 27, 'text' => "FOX_uGroupKeyRing getKeys exception", 'data' => array("add_group_ids" => $add_group_ids), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                     }
                     // Grant the user the groups' combined keyrings
                     if ($keyring) {
                         $ks = new FOX_uKeyRing();
                         try {
                             $keys_ok = $ks->grantKey($user, $keyring);
                         } catch (FOX_exception $child) {
                             throw new FOX_exception(array('numeric' => 28, 'text' => "FOX_uketRing grantKey exception", 'data' => array("user" => $user, "keyring" => $keyring), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                         }
                     } else {
                         // Handle none of the groups having any keys on their keyring
                         $keys_ok = true;
                     }
                     if ($groups_ok && $keys_ok) {
                         $user_count++;
                     } else {
                         $all_ok = false;
                     }
                 }
             }
             unset($user);
             // If all operations completed successfully, commit the transaction
             if ($all_ok && $user_count >= 1) {
                 try {
                     $commit_ok = $db->commitTransaction();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 29, '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] = true;
                         }
                         $cache_ok = $fox->cache->set("FOX_uGroupMember", $user, $this->cache[$user]);
                         if (!$cache_ok) {
                             $all_ok = false;
                         }
                     }
                     unset($user, $group);
                     return $all_ok;
                 } else {
                     return false;
                 }
             } elseif ($all_ok && $user_count < 1) {
                 try {
                     $db->rollbackTransaction();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 30, 'text' => "rollbackTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 return true;
             } else {
                 try {
                     $db->rollbackTransaction();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 31, 'text' => "beginTransaction 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 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     }
 }
예제 #7
0
 /**
  * Creates a new key, adding it to the database and cache.
  *
  * @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 $key_id | Single key_id as int. Multiple key_id's as array of ints.
  * @return bool | False on failure. True on success but no db rows changed. Int number of rows changed on success.
  */
 public function grantKey($user_id, $key_id)
 {
     global $fox;
     $db = new FOX_db();
     // CASE 1: Single user_id, single key
     // =================================================================
     if (!is_array($user_id) && !is_array($key_id)) {
         // If the user already has the key, return true to indicate no db rows were changed
         if (self::hasKey($user_id, $key_id)) {
             return true;
         } else {
             $data = array("user_id" => $user_id, "key_id" => $key_id);
             try {
                 $rows_changed = $db->runInsertQuery(self::$struct, $data, $columns = null);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 1, 'text' => "DB insert exception", 'data' => array("data" => $data), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             if ($rows_changed) {
                 // Load the user's keyring into the class cache from the persistent cache
                 $this->cache[$user_id] = $fox->cache->get("FOX_uKeyRing", $user_id);
                 // Add the new key
                 $this->cache[$user_id]["keys"][$key_id] = true;
                 // Update the persistent cache
                 $cache_ok = $fox->cache->set("FOX_uKeyRing", $user_id, $this->cache[$user_id]);
                 if ($cache_ok) {
                     return $rows_changed;
                 } else {
                     return false;
                 }
             } else {
                 return $rows_changed;
             }
         }
     }
     // CASE 2: Single user_id, multiple keys
     // =================================================================
     if (!is_array($user_id) && is_array($key_id)) {
         // Load the user's entire keyring into the cache (if not cached already)
         try {
             self::getKeys($user_id);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 2, 'text' => "FOX_uKeyRing getKeys exception", 'data' => array("user_id" => $user_id), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         // Create an array of "to be added" keys the user doesn't have.
         $keys_to_add = array();
         foreach ($key_id as $key) {
             if (!$this->cache[$user_id]["keys"][$key]) {
                 // This test condition gets both missing
                 $keys_to_add[] = $key;
                 // keys (null) and negative keys (false)
             }
         }
         unset($key);
         if (count($keys_to_add) > 0) {
             // Add the new keys to the database
             $data = array();
             foreach ($keys_to_add as $key) {
                 $data[] = array("user_id" => $user_id, "key_id" => $key);
             }
             unset($key);
             try {
                 $rows_changed = $db->runInsertQueryMulti(self::$struct, $data, $columns = null, $ctrl = null);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 3, 'text' => "DB insert exception", 'data' => $data, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             // Update the cache
             if ($rows_changed) {
                 // Load the user's keyring into the class cache from the persistent cache
                 $this->cache[$user_id] = $fox->cache->get("FOX_uKeyRing", $user_id);
                 // Add the new keys to the class cache
                 foreach ($keys_to_add as $key) {
                     $this->cache[$user_id]["keys"][$key] = true;
                 }
                 unset($key);
                 // Update the persistent cache from the class cache
                 $cache_ok = $fox->cache->set("FOX_uKeyRing", $user_id, $this->cache[$user_id]);
                 if ($cache_ok) {
                     return $rows_changed;
                 } else {
                     return false;
                 }
             } else {
                 return $rows_changed;
             }
         } else {
             return true;
         }
         // ENDOF: if( count($keys_to_add) > 0 )
     }
     // CASE 3: Multiple user_id's, single key
     // =================================================================
     if (is_array($user_id) && !is_array($key_id)) {
         $users_to_add = array();
         // Create an array of "to be added" users that don't have the key
         foreach ($user_id as $user) {
             try {
                 $has_key = self::hasKey($user, $key_id);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 4, 'text' => "FOX_uKeyRing hasKey exception", 'data' => array("user" => $user, "key_id" => $key_id), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             if (!$has_key) {
                 $users_to_add[] = $user;
             }
         }
         unset($user);
         if (count($users_to_add) > 0) {
             // Add the new keys to the database
             $data = array();
             foreach ($users_to_add as $user) {
                 $data[] = array("user_id" => $user, "key_id" => $key_id);
             }
             unset($user);
             try {
                 $rows_changed = $db->runInsertQueryMulti(self::$struct, $data, $columns = null, $ctrl = null);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 5, 'text' => "DB insert exception", 'data' => $data, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             // Update the cache
             if ($rows_changed) {
                 $all_ok = true;
                 foreach ($users_to_add as $user) {
                     // Load the user's keyring into the class cache from the persistent cache
                     $this->cache[$user] = $fox->cache->get("FOX_uKeyRing", $user);
                     // Add the new key to the class cache
                     $this->cache[$user]["keys"][$key_id] = true;
                     // Update the persistent cache from the class cache
                     $cache_ok = $fox->cache->set("FOX_uKeyRing", $user, $this->cache[$user]);
                     if (!$cache_ok) {
                         $all_ok = false;
                     }
                 }
                 unset($user);
                 if ($all_ok) {
                     return $rows_changed;
                 } else {
                     return false;
                 }
             } else {
                 return $rows_changed;
             }
         } else {
             return true;
         }
         // ENDOF: if( count($users_to_add) > 0 )
     }
     // CASE 4: Multiple user_id's, multiple keys (all users get same keys)
     // =================================================================
     if (is_array($user_id) && is_array($key_id)) {
         // Load each user's full keyring into the cache (if not cached already)
         try {
             self::getKeysMulti($user_id);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 6, 'text' => "FOX_uKeyRing getKeys exception", 'data' => array("user_id" => $user_id), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         // Create an array of keys that need to be added to each user_id
         $keys_to_add = array();
         foreach ($user_id as $user) {
             foreach ($key_id as $key) {
                 if (!$this->cache[$user]["keys"][$key]) {
                     // This test condition gets both missing
                     $keys_to_add[$user][] = $key;
                     // keys (null) and negative keys (false)
                 }
             }
             unset($key);
         }
         unset($user);
         // Add the new keys to the database
         if (count($keys_to_add) > 0) {
             $data = array();
             foreach ($keys_to_add as $user => $keys) {
                 foreach ($keys as $key) {
                     $data[] = array("user_id" => $user, "key_id" => $key);
                 }
                 unset($key);
             }
             unset($user, $keys);
             try {
                 $rows_changed = $db->runInsertQueryMulti(self::$struct, $data, $columns = null, $ctrl = null);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 7, 'text' => "DB insert exception", 'data' => $data, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             // Update the cache
             if ($rows_changed) {
                 $all_ok = true;
                 foreach ($keys_to_add as $user => $keys) {
                     // Load the user's keyring into the class cache from the persistent cache
                     $this->cache[$user] = $fox->cache->get("FOX_uKeyRing", $user);
                     // Add the new keys to the class cache
                     foreach ($keys as $key) {
                         $this->cache[$user]["keys"][$key] = true;
                     }
                     unset($key);
                     // Update the persistent cache from the class cache
                     $cache_ok = $fox->cache->set("FOX_uKeyRing", $user, $this->cache[$user]);
                     if (!$cache_ok) {
                         $all_ok = false;
                     }
                 }
                 unset($user, $keys);
                 if ($all_ok) {
                     return $rows_changed;
                 } else {
                     return false;
                 }
             } else {
                 return $rows_changed;
             }
         } else {
             return true;
         }
         // ENDOF: if( count($keys_to_add) > 0 )
     }
 }