type == '') { return null; } return SloodleObjectConfig::ForObjectType( $this->type ); } /* Queues an object */ function queue($address, $task, $msg, $ttr = 30, $clear = false, $priority=1000) { require_once(SLOODLE_LIBROOT.'/beanstalk/Beanstalk.php'); global $CFG; // Make a unique name for the sites queue $sitequeue = defined('SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK_PREFIX') ? SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK_PREFIX : ''; $tube = $sitequeue.'-'.md5($address); //$tube = 'default'; //$tube = md5($address); /* $sbhost = ( defined(SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK_HOST) && SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK_HOST ) ? SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK_HOST : '127.0.0.1'; $sbport = ( defined(SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK_PORT) && SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK_PORT ) ? SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK_PORT : 11300; $sbtimeout = ( defined(SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK_TIMEOUT) && SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK_TIMEOUT ) ? SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK_TIMEOUT : 1; $sbpersistent = ( defined(SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK_PERSISTENT) && SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK_PERSISTENT ) ? SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK_PERSISTENT : true; $sbconfig = array( 'persistent' => $sbpersistent, 'host' => $sbhost, 'port' => $sbport, 'timeout' => $sbtimeout ); */ //$sb = new Socket_Beanstalk( $sbconfig ); $sb = new Socket_Beanstalk(); if (!$sb->connect()) { return false; } $sb->choose($tube); // Jobs will be handled more-or-less first-in, first-out, unless superseced by a new job and cleared. // //$priority = time(); $header = $task.'|'.$address; $msg = $header."\n".$msg; if (!$pid = $sb->put($priority, 0, $ttr, $msg)) { return false; } // Delete all jobs with an early pid than the one we just put in there. if ($clear) { while ($job = $sb->peekReady()) { if ($job['id'] >= $pid) { break; } // Only delete jobs for the same task. if (strpos( $job['body'], $header) == 0) { $sb->delete($job['id']); } } } return true; } // Returns true if the install has a functioning method for queuing messages to be sent asynchronously. // As of 2012-03-30, this will be a message queue using Beanstalk. // In future we may do a more Moodle-standard mysql queue, plus a cron like chatd.php function isQueueActive() { if ( defined('SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK') && (SLOODLE_MESSAGE_QUEUE_SERVER_BEANSTALK != '') ) { return true; } return false; } // sends a curl message to our objects httpinurl // If async is set, if possible it will be queued for sending later. // public function sendMessage($msg, $async = false, $replaceQueued = false, $task = 'async_message', $priority=1000, $timeout = 20){ SloodleDebugLogger::log('HTTP-IN', $msg); if ($async && $this->isQueueActive()) { //$this->httpinurl if ($this->queue($this->httpinurl, $task, $msg, 3, $replaceQueued)) { return array('info'=>null, 'result'=>'QUEUED'); } } $ch = curl_init(); // initialize curl handle curl_setopt($ch, CURLOPT_URL, $this->httpinurl ); // set url to post to curl_setopt($ch, CURLOPT_FAILONERROR,0); curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); // return into a variable curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); curl_setopt($ch, CURLOPT_POST, 1); // set POST method curl_setopt($ch, CURLOPT_POSTFIELDS,$msg); // add POST fields if ($proxy = $this->httpProxyURL()) { curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1); curl_setopt($ch, CURLOPT_PROXY, $this->httpProxyURL() ); } $result = curl_exec($ch); // run the whole process $info = curl_getinfo($ch); curl_close($ch); return array('info'=>$info,'result'=>$result); } /* Request a list of inventory from the rezzer. NB The format is slightly different to the one used by the vending machine. $include_sloodle can be set to false to ignore things beginning with SLOODLE. Hopefully this will give us a list of third-party objects that we don't already know about. */ public function listInventory( $include_sloodle = true ) { $response = new SloodleResponse(); $response->set_status_code(1); $response->set_status_descriptor('SYSTEM'); $response->set_request_descriptor('LIST_INVENTORY'); $response->add_data_line('do:list_inventory'); //create message - NB for some reason render_to_string changes the string by reference instead of just returning it. $renderStr=""; $response->render_to_string($renderStr); //send message to httpinurl $reply = $this->sendMessage($renderStr); if ( !( $reply['info']['http_code'] == 200 ) ) { return false; } $result = $reply['result']; $ret = array(); $lines = explode("\n", $result); array_shift($lines); foreach($lines as $l) { if (!$include_sloodle) { if (preg_match('/^SLOODLE/', $l)) { continue; } } $ret[] = $l; } return $ret; } // Sends a message to the object telling it to derez itself. // Doesn't deletes the active_object record, because it doesn't know whether the message will get through or not. // Return true on successful queuing, false on failure public function queueDeRez() { if (!$this->isQueueActive()) { return false; } if (!$message = $this->deRezMessage()) { return false; } $async = true; $reply = $this->sendMessage($message, $async, $async, 'derez'); return true; } private function deRezMessage() { if (!$this->httpinurl) { return false; } //build response string $response = new SloodleResponse(); $response->set_status_code(1); $response->set_status_descriptor('SYSTEM'); $response->set_request_descriptor('DEREZ'); $response->set_http_in_password($this->httpinpassword); //$response->set_return_url(); $response->add_data_line('do:derez'); //create message - NB for some reason render_to_string changes the string by reference instead of just returning it. $renderStr=""; $response->render_to_string($renderStr); //send message to httpinurl return $renderStr; } // Sends a message to the object telling it to derez itself. // Deletes the active_object record if successful. // NB the object can't report back whether it successfully derezzed itself, because it no longer exists. // We'll go by whether it acknowledged the derez command, which is as close as we can get. // Return true on success, false on failure public function deRez() { if (!$message = $this->deRezMessage()) { return false; } $async = false; $reply = $this->sendMessage($message, $async, $async, 'derez'); if (!$async) { if ( !( $reply['info']['http_code'] == 200 ) ) { return false; } } return $this->delete(); } // Sends a message to the object telling it to refresh its configuration. // Return true on success, false on failure public function refreshConfig($async = false) { if (!$this->httpinurl) { return false; } //build response string $response = new SloodleResponse(); $response->set_status_code(1); $response->set_status_descriptor('SYSTEM'); $response->set_request_descriptor('REFRESH_CONFIG'); $response->add_data_line('do:requestconfig'); //create message - NB for some reason render_to_string changes the string by reference instead of just returning it. $renderStr=""; $response->render_to_string($renderStr); //send message to httpinurl $reply = $this->sendMessage($renderStr, $async, $async); if ( !$async && ( !( $reply['info']['http_code'] == 200 ) ) ) { return false; } return true; } /* * writes config data to a response object, and sends it this objects httpinurl * @var $extraParameters array() * */ public function sendConfig( $extraParameters = NULL, $async = false ) { global $CFG; //construct the body if ($extraParameters === NULL) { $extraParameters = array(); } //build response string $response = new SloodleResponse(); $response->add_data_line(array('set:sloodlecontrollerid', $this->controllerid)); $response->add_data_line(array('set:sloodlecoursename_short', $this->course->get_short_name())); $response->add_data_line(array('set:sloodlecoursename_full', $this->course->get_short_name())); $response->add_data_line(array('set:sloodlepwd', $this->uuid.'|'.$this->password)); // NB We need to prepend the UUID - otherwise Sloodle treats it like a prim password $response->add_data_line(array('set:sloodleserverroot', $CFG->wwwroot)); $response->add_data_line(array('set:sloodleobjtype', $this->type)); if ($this->layoutentryid) { $response->add_data_line(array('set:position', $this->position, $this->rotation, $this->rezzeruuid)); //$response->add_data_line(array('set:rezzeruuid', $this->rezzeruuid)); //$response->add_data_line(array('set:rotation', $this->rotation)); $response->add_data_line(array('set:layoutentryid', $this->layoutentryid)); } //search for setings for this object $settings = sloodle_get_records('sloodle_object_config', 'object', $this->id); if (!$settings) { // Error: no configuration settings... there should be at least one indicating the type /* $response->set_status_code(-103); $response->set_status_descriptor('SYSTEM'); $response->add_data_line('Object not configured yet.'); $renderStr=""; //create message $response->render_to_string($renderStr); //send message to httpinurl return $this->sendMessage($renderStr); */ } $layoutentry = NULL; $position = NULL; $rotation = NULL; // Output each setting foreach ($settings as $s) { $response->add_data_line(array('set:'.$s->name, $s->value)); }//end foreach foreach( $extraParameters as $n => $v) { $response->add_data_line( 'set:'.$n.'|'.$v ); }//endforeach $response->set_status_code(1); $response->set_refresh_seconds(SLOODLE_PING_INTERVAL); /* If we send the object a CONFIG_PERSISTENT descriptor, it will keep the config and use it if re-rezzed or copied. This is set in init.php. Normally this would be on, but if you're developing a set to give to other people, you want it off Otherwise it will use your server details, rather than getting new ones. */ $request_descriptor = (defined('SLOODLE_ENABLE_OBJECT_PERSISTANCE') && SLOODLE_ENABLE_OBJECT_PERSISTANCE) ? 'CONFIG_PERSISTENT' : 'CONFIG'; $response->set_status_descriptor('CONFIG'); $response->set_request_descriptor($request_descriptor); $response->set_http_in_password($this->httpinpassword); $renderStr=""; $response->render_to_string($renderStr); //curl send this to our object's httpinurl return $this->sendMessage( $renderStr, $async, $async); } // Deletes a record from the active object table. // Returns true on success. // NB If you need to get rid of the record from world as well, use deRez(). // deRez will call this function in turn. public function delete() { if (!$this->id) { return false; } // Delete all config entries and the object record itself return ( sloodle_delete_records('sloodle_object_config', 'object', $this->id) && sloodle_delete_records('sloodle_active_object', 'id', $this->id) ); } public function save(){ //write local data to a new or existing record //search for id //if exists update $result = sloodle_get_record("sloodle_active_object",'id',$this->id); if (!$result ) { //id,controllerid,userid,uuid,password,name,type,timeupdated // No - insert a new record $result = new stdClass(); $result->controllerid = $this->controllerid; $result->userid = $this->userid; $result->uuid = $this->uuid; $result->httpinurl = $this->httpinurl; $result->httpinpassword = $this->httpinpassword; $result->position = $this->position; $result->rotation = $this->rotation; $result->rezzeruuid = $this->rezzeruuid; $result->layoutentryid = $this->layoutentryid; $result->password = $this->password; $result->name = $this->name; $result->type = $this->type; $result->mediakey = $this->mediakey; $result->lastmessagetimestamp = $this->lastmessagetimestamp; $success = sloodle_insert_record('sloodle_active_object', $result ); } else { $result->controllerid = $this->controllerid; $result->userid = $this->userid; $result->uuid = $this->uuid; $result->httpinurl = $this->httpinurl; $result->position = $this->position; $result->rotation = $this->rotation; $result->rezzeruuid = $this->rezzeruuid; $result->layoutentryid = $this->layoutentryid; $result->password = $this->password; $result->name = $this->name; $result->type = $this->type; $result->httpinpassword = $this->httpinpassword; $result->mediakey = $this->mediakey; $result->lastmessagetimestamp = $this->lastmessagetimestamp; if (sloodle_update_record('sloodle_active_object', $result)) { $success = $result->id; } }//end else $this->course = new SloodleCourse(); $this->course->load_by_controller($this->controllerid); return $success; }//end function // Load data for the specified id // Return true on success, false on fail public function load( $id) { $rec = sloodle_get_record('sloodle_active_object','id',$id); if ($rec) { $this->loadFromRecord($rec); return true; } return false; } // Load data for the specified UUID // Return true on success, false on fail public function loadByUUID( $uuid, $lazy = false ) { //SloodleDebugLogger::log('DEBUG', "loading for uuid :$uuid:"); $rec = sloodle_get_record('sloodle_active_object','uuid',$uuid); if ($rec) { //SloodleDebugLogger::log('DEBUG', "loaded for uuid :$uuid:"); $this->loadFromRecord($rec, $lazy); return true; } return false; } public function loadFromRecord($rec, $lazy = false) { $this->id = $rec->id; $this->controllerid = $rec->controllerid; $this->userid = $rec->userid; $this->uuid = $rec->uuid; $this->password = $rec->password; $this->name = $rec->name; $this->type = $rec->type; $this->timeupdated = $rec->timeupdated; $this->httpinurl = $rec->httpinurl; $this->httpinpassword = $rec->httpinpassword; $this->layoutentryid = $rec->layoutentryid; $this->position = $rec->position; $this->rotation = $rec->rotation; $this->rezzeruuid = $rec->rezzeruuid; $this->mediakey = $rec->mediakey; $this->lastmessagetimestamp = $rec->lastmessagetimestamp; // TODO: This is probably mostly not needed. // We should fetch this lazily when we need the course name or whatever it is we want here. if ($this->controllerid && !$lazy) { $this->course = new SloodleCourse(); $this->course->load_by_controller($this->controllerid); } return true; } // Configures an activeobject for its layoutentryid, which should already have been set, if there is one. // TODO: This functionality is partially duplicated in controller->configure_object_from_layout_entry($authid, $layout_entry_id, $rezzeruuid = null) // Remove it from there, and do it all here. // NB This doesn't actually send the config to the object - you need to call sendConfig separately to do that. public function configure_for_layout() { // We should already have been inserted when this gets called. if (!$id = $this->id) { return false; } if (!$layoutentryid = $this->layoutentryid) { return false; } $existingconfigs = $this->config_name_config_hash(); $done_configs = array(); $configs = sloodle_get_records('sloodle_layout_entry_config','layout_entry', $layoutentryid); $ok = true; if (count($configs) > 0) { foreach($configs as $config) { $name = $config->name; if ( isset($existingconfigs[ $name ])) { // No change if ($existingconfigs[ $name ]->value == $config->value) { $done_configs[ $name ] = true; continue; } else { $updated_config = $existingconfigs[ $name ]; $updated_config->value = $config->value; sloodle_update_record( 'sloodle_object_config', $updated_config); $done_configs[ $name ] = true; } } else { $config->id = null; $config->object = $this->id; if (!sloodle_insert_record('sloodle_object_config',$config)) { $ok = false; } } } } // Original configs that are no longer in the layout, so we'll kill them. if (count($existingconfigs) > 0) { foreach($existingconfigs as $config) { if (!isset($done_configs[ $config->name ] )) { sloodle_delete_records('sloodle_object_config', 'id', $config->id); } } } return $ok; } /** * Updates the last active timer on an object. * @return bool True if successful, or false if not. * Replaces controller->ping_object(). */ public function recordAccess() { if (!$this->id) { return false; } $this->timeupdated = time(); return sloodle_update_record('sloodle_active_object', $this); } public function config_value( $name ) { if ($config = sloodle_get_record('sloodle_object_config', 'object', $this->id, 'name', $name)) { return $config->value; } $config = sloodle_get_record('sloodle_object_config', 'object', $this->id); return null; } public function requirement_failures( $interaction, $multiplier, $userid, $useruuid) { if ( !$userid = intval($userid) ) { return array(); } if ( $multiplier == 0 ) { return array(); } if (!$controllerid = intval($this->controllerid) ) { return array(); } $all_configs = sloodle_get_records('sloodle_object_config', 'object', $this->id); if (count($all_configs) == 0) { // Nothing to do here return array(); } $all_module_classes = sloodle_available_modules(); if (count($all_module_classes) == 0) { return array(); } $failures = array(); foreach($all_module_classes as $module_class) { // Find each of the tasks we have to handle for the interaction. // This information is stored in the object config $relevant_configs = array(); if (!method_exists($module_class,'RequirementConfigNames')) { continue; } $relevant_config_names = call_user_func(array($module_class, 'RequirementConfigNames')); if (count($relevant_config_names) == 0) { continue; } foreach($relevant_config_names as $configname) { $fieldname = $configname.'_'.$interaction; foreach($all_configs as $c) { if ($c->name == $fieldname) { $relevant_configs[ $configname] = $c->value; } } } if (count($relevant_configs) == 0) { // Nothing to do here continue; } //SloodleDebugLogger::log('DEBUG', "about to check RequirementFailures for $module_class, ".join($relevant_configs,':') ); $module_failures = call_user_func_array( array($module_class, 'RequirementFailures'), array( $relevant_configs, $controllerid, $multiplier, $userid, $this->uuid )); //SloodleDebugLogger::log('DEBUG', "done checking RequirementFailures for $module_class"); if($module_failures) { $failures = array_merge($failures, $module_failures); } } //SloodleDebugLogger::log('DEBUG', "done checking all RequirementFailures "); return $failures; } /* Modules (awards, tracker etc) may specify that they can handle certain kinds of events. The object can be configred to trigger those events when particular interactions happen. For example, the awards module has an event called "sloodleawardsdeposit_numpoints". If this is called, we should give the user that number of points. */ public function process_interaction( $interaction, $multiplier, $userid, $useruuid ) { if ( !$userid = intval($userid) ) { return false; } if ( $multiplier == 0 ) { return false; } if (!$controllerid = intval($this->controllerid) ) { return false; } $all_configs = sloodle_get_records('sloodle_object_config', 'object', $this->id); if (count($all_configs) == 0) { return true; } $all_module_classes = sloodle_available_modules(); foreach($all_module_classes as $module_class) { if (!class_exists($module_class)) { continue; } if (!method_exists($module_class, 'ActionConfigNames')) { continue; } // Find each of the tasks we have to handle for the interaction. // This information is stored in the object config $relevant_configs = array(); $relevant_config_names = call_user_func(array($module_class, 'ActionConfigNames')); if (count($relevant_config_names) == 0) { continue; } /* $relevant_config_names = array( 'sloodleawardsdeposit_numpoints', 'sloodleawardsdeposit_currency', 'sloodleawardswithdraw_numpoints', 'sloodleawardswithdraw_currency' ); */ foreach($relevant_config_names as $configname) { $fieldname = $configname.'_'.$interaction; //SloodleDebugLogger::log('DEBUG', "looking for confir $fieldname"); foreach($all_configs as $c) { if ($c->name == $fieldname) { $relevant_configs[ $configname] = $c->value; } } } if (count($relevant_configs) == 0) { // Nothing to do here continue; } if (!method_exists($module_class, 'ProcessActions')) { continue; } //SloodleDebugLogger::log('DEBUG', "calling ProcessActions for $module_class"); call_user_func_array( array($module_class, 'ProcessActions'), array( $relevant_configs, $controllerid, $multiplier, $userid, $useruuid, $this->uuid )); } return true; } /* Notify any active objects that are interested in the action $action. ...so that an object can tell us what it's interested in hearing about. @notification_action - the name of whatever action just happened. Interested objects state this in their definition.php. @success_code - the code to be sent to the object. @controllerid - the controller we should filter for, if there is one. @userid - the ID of the user concerned. Probably only used in callbacks. @params - any parameters to be sent to the objects, in name=>value pairs. @addtimestampparams - tells us to append a timestamp to the bottom of the message to help the object tell if it's getting the latest message, or if messages are out of order. @exactlyonce - tells the function to return as soon as it successfully sends. This is used when trying to send an IM to the user, which we only want to do once even if there are multiple objects in-world that could handle it. */ function NotifySubscriberObjects( $notification_action, $success_code, $controllerid, $userid, $params, $addtimestampparams, $exactlyonce = false ) { global $CFG; $interested_object_types = SloodleObjectConfig::TypesOfObjectsRequiringNotification( $notification_action ); //$interested_object_types = array('SLOODLE Scoreboard'); if (count($interested_object_types) == 0) { // Nobody cares. // This is a failure if we had to send exactly one message, as with instant message notifications. // Otherwise it's a success: We notified everybody who cared, which turned out to be nobody. return !$exactlyonce; } $instr = ''; $delim = ''; $queryparams = $controllerid ? array(intval($controllerid)) : array(); foreach($interested_object_types as $ot) { $queryparams[] = $ot; $instr .= $delim.'?'; $delim = ','; } $queryparams[] = time() - (3600+600); // Ping time and then some. All objects that are alive should have updated within this time. $sql = "select a.* from {$CFG->prefix}sloodle_active_object a where a.controllerid=? and a.httpinurl IS NOT NULL and a.type in ($instr) and a.timeupdated>? order by a.timeupdated desc;"; if (!$controllerid) { $sql = "select a.* from {$CFG->prefix}sloodle_active_object a WHERE a.httpinurl IS NOT NULL and a.type in ($instr) and a.timeupdated>? order by a.timeupdated desc;"; } $recs = sloodle_get_records_sql_params($sql, $queryparams); $msg = "$success_code\n"; $callback_objects = array(); foreach($recs as $rec) { $ao = new SloodleActiveObject(); $ao->loadFromRecord( $rec ); /* If we just have a notify message specified, send a simple NOTIFICATION saying what just happened. If we want to trigger something more substantial, we should have a notify_callback defined. This means we'll want to pass control to a function specified in the object's definition. We'll do these in bulk later, so that if there are a lot of objects that all require the same message they can be done together. */ $def = $ao->objectDefinition(); if ( isset($def->notify_callbacks) && isset($def->notify_callbacks[$notification_action]) ) { $callback = $def->notify_callbacks[$notification_action]; if (!isset($callback_objects[ $callback] )) { $callback_objects[ $callback ] = array(); } $callback_objects[ $callback ][] = $ao; continue; } /* There's no callback specified for this object, so just send it a simple message. */ $response = new SloodleResponse(); $response->set_status_code($success_code); $response->set_status_descriptor('NOTIFICATION'); $response->set_request_descriptor('NOTIFICATION'); $response->set_http_in_password($ao->httpinpassword); foreach($params as $n=>$v) { $response->add_data_line($n.'|'.$v); } // We can set extra fields to allow the object to keep track of messages being sent. // That way if a message fails to get delivered, the lastmessagesendtimestamp won't match what the object remembers // ...and the object will know it needs to do something to catch up, like polling the server. $ts = time(); if ($addtimestampparams) { $response->add_data_line("lastmessagetimestamp|".$ao->lastmessagetimestamp); $response->add_data_line("messagetimestamp|".$ts); } $renderStr=""; $response->render_to_string($renderStr); // If we're doing exactly once, we'll need to send synchronously so we can check the notification worked. // Otherwise, send asynchronously if it's turned on. $async = (defined('SLOODLE_ASYNC_SEND_MESSAGES') && SLOODLE_ASYNC_SEND_MESSAGES && !$exactlyonce); // If this stuff fails, tough. We did our best. if ($resarr = $ao->sendMessage($renderStr, $async, $async)) { if($resarr['info']['http_code'] == 200){ $ao->lastmessagetimestamp = time(); $ao->save(); if ($exactlyonce) { return true; } } } } //SloodleDebugLogger::log('DEBUG', "calling callbacs for ".count($callback_objects)." callbacks"); if (count($callback_objects) > 0) { foreach($callback_objects as $callback => $aoarr) { //SloodleDebugLogger::log('DEBUG', "calling callback $callback for ".count($aoarr)." objects"); $result = call_user_func_array( $callback, array( $aoarr, $notification_action, $success_code, $controllerid, $userid, $params, $addtimestampparams ) ); if ($result && $exactlyonce) { return true; } } } // If we were supposed to do this exactly once and not have succeeded, that's a failure. if ($exactlyonce) { return false; } // If we didn't specify the number and nothing went wrong, that's a success, even if we managed to contact zero objects. return true; } // Return objects that say they have the capability to do the task // Since restricts to how recently they've been active. // Criteria allows for config filters, eg. by sloodle module id. //$objects = SloodleActiveObject::ObjectsCapableOfTaskActiveSince('asdf',time()-60,array('sloodlemoduleid',123)); function ObjectsCapableOfTaskActiveSince($task, $sincets, $criteria) { global $CFG; $defs = SloodleObjectConfig::DefinitionsOfObjectsCapableOf($task); if (empty($defs)) { return array(); } $instr = ''; $delim = ''; $queryparams = array(); //array(intval($controllerid)); foreach($defs as $ot) { $queryparams[] = $ot->type(); $instr .= $delim.'?'; $delim = ','; } $queryparams[] = $sincets; // time() - (3600+600); // Ping time and then some. All objects that are alive should have updated within this time. $wherepairs = array(); $criteriastring = ''; if (count($criteria) > 0) { foreach($criteria as $field => $value) { $wherepairs[] = "c.name=? and c.value=?"; $queryparams[] = $field; $queryparams[] = $value; } $criteriastring = ' AND '.implode(' AND ', $wherepairs); } $sql = "select a.* from {$CFG->prefix}sloodle_active_object a inner join {$CFG->prefix}sloodle_object_config c on a.id=c.object where a.httpinurl IS NOT NULL and a.type in ($instr) and a.timeupdated>? $criteriastring order by a.timeupdated desc;"; $recs = sloodle_get_records_sql_params($sql, $queryparams); $ret = array(); foreach($recs as $rec) { $ao = new SloodleActiveObject(); $ao->loadFromRecord( $rec, $lazy = true ); $ret[] = $ao; } return $ret; } // An array of config objects, keyed by config name function config_name_config_hash() { $id = $this->id; // If the ID is empty, then we have no configuration settings to get if (empty($id)) return array(); $recs = sloodle_get_records('sloodle_object_config', 'object', $id); if (!$recs) return false; $config = array(); foreach ($recs as $r) { $config[$r->name] = $r; } return $config; } // An array of config values, keyed by config name function config_name_value_hash() { if (!$config_hash = $this->config_name_config_hash()) { return false; } if (count($config_hash) == 0) { return array(); } $config = array(); foreach ($config_hash as $r) { $config[$r->name] = $r->value; } return $config; } function has_custom_config() { return ( $this->objectDefinition() != null ); } function uuids_of_children_missing_since($oldestts, $youngestts) { global $CFG; $sql = "select id, uuid from {$CFG->prefix}sloodle_active_object where rezzeruuid=? and timeupdated > ? and timeupdated < ? order by timeupdated asc;"; $recs = sloodle_get_records_sql_params($sql, array($this->uuid, $oldestts, $youngestts)); $uuids = array(); if ( ($recs) && (count($recs) > 0) ) { foreach($recs as $rec) { $uuids[] = $rec->uuid; } } return $uuids; } function layoutentryids_to_uuids_of_currently_rezzed($layoutid) { global $CFG; $sql = "select ao.id as id, ao.layoutentryid as layoutentryid, ao.uuid as uuid from {$CFG->prefix}sloodle_active_object ao inner join {$CFG->prefix}sloodle_layout_entry le on ao.layoutentryid=le.id where ao.rezzeruuid=? and le.layout=? order by timeupdated desc"; $recs = sloodle_get_records_sql_params($sql, array($this->uuid, $layoutid)); $layoutentryhash = array(); if ( ($recs) && (count($recs) > 0) ) { foreach($recs as $rec) { $leid = $rec->layoutentryid; $uuid = $rec->uuid; if (!isset($layoutentryhash[ $leid ] )){ $layoutentryhash[$leid] = array(); } $layoutentryhash[$leid][] = $rec->uuid; } } return $layoutentryhash; } /* Take an array of active objects ...and return only the ones with the specified name/value config. */ function FilterForConfigNameValue( $aos, $name, $value ) { global $CFG; if (!count($aos)) { return array(); } $params = array($name, $value); $placeholders = array(); foreach($aos as $ao) { if (!$id = intval($ao->id)) { continue; } $params[] = $id; $placeholders[] = '?'; } if (!count($placeholders)) { return array(); } $query = "select object as objid from {$CFG->prefix}sloodle_object_config where name=? and value=? and object in (".join(',',$placeholders).");"; $recs = sloodle_get_records_sql_params($query, $params); if (!is_array($recs) || (!count($recs))) { return array(); } $wantobjids = array(); foreach($recs as $rec) { $wantobjids[ $rec->objid ] = true; } $ret = array(); foreach($aos as $ao) { $id = $ao->id; if (isset($wantobjids[$id])) { $ret[] = $ao; } } return $ret; } } ?>