$CFG * @return */ public static function get_list_protocols() { global $CFG; $protocols = array(); $directorypath = $CFG->dirroot . "/webservice"; if( $dh = opendir($directorypath)) { while( false !== ($file = readdir($dh))) { if( $file == '.' || $file == '..' || $file == 'CVS') { // Skip '.' and '..' continue; } $path = $directorypath . '/' . $file; ///browse the subfolder if( is_dir($path) ) { require_once($path."/lib.php"); $classname = $file."_server"; $protocols[] = new $classname; } ///retrieve api.php file else { continue; } } closedir($dh); } return $protocols; } /** * Temporary Authentication method to be modified/removed * @global $DB * @param $token * @return */ public static function mock_check_token($token) { //fake test if ($token == 456) { ///retrieve the user global $DB; $user = $DB->get_record('user', array('username'=>'wsuser', 'mnethostid'=>1)); if (empty($user)) { return false; } return $user; } else { return false; } } /** * Retrieve all external.php from Moodle (except the one of the exception list) * @param $ * @param $directorypath * @return boolean true if n */ public static function setListApiFiles( &$files, $directorypath ) { global $CFG; if(is_dir($directorypath)){ //check that we are browsing a folder not a file if( $dh = opendir($directorypath)) { while( false !== ($file = readdir($dh))) { if( $file == '.' || $file == '..') { // Skip '.' and '..' continue; } $path = $directorypath . '/' . $file; ///browse the subfolder if( is_dir($path) ) { webservice_lib::setListApiFiles($files, $path); } ///retrieve api.php file else if ($file == "external.php") { $files[] = $path; } } closedir($dh); } } } /** * Generate web service description array from the phpdoc for a given class * @param string $file the class file * @param string $class the class name * @return array description * * ------- Example ------- * Docnlock: @ subparam string $params:searches->search - the string to search * $params is considered as the first element, searches the second, and search the terminal * Except the terminal element, all other will be generated as an array * => left element are generated as an associative array. * If the following character is ':' so the right element is a key named 'multiple:element_name' * If the following character is '->' so the right element will be named 'element_name' * Rule: If a key is named 'multiple:xxx' other key must be 'multiple:yyy' Docblock of mock_function --------------------------- @ param array|struct $params @ subparam string $params:searches->search - the string to search @ subparam string $params:searches->search2 optional - optional string to search @ subparam string $params:searches->search3 - the string to search @ subparam string $params:airport->planes:plane->company->employees:employee->name - name of a employee of a company of a plane of an airport @ return array users @ subreturn integer $users:user->id @ subreturn integer $users:user->auth Generated description array --------------------------- description["mock_function"]=> array(3) { ["params"]=> array(2) { ["multiple:searches"]=> array(2) { ["search"]=> string(6) "string" ["search3"]=> string(6) "string" } ["multiple:airport"]=> array(1) { ["planes"]=> array(1) { ["multiple:plane"]=> array(1) { ["company"]=> array(1) { ["employees"]=> array(1) { ["multiple:employee"]=> array(1) { ["name"]=> string(6) "string" } } } } } } } ["optional"]=> array(1) { ["multiple:searches"]=> array(1) { ["search2"]=> string(6) "string" } } ["return"]=> array(1) { ["multiple:user"]=> array(13) { ["id"]=> string(7) "integer" ["auth"]=> string(7) "integer" } } */ public static function generate_webservice_description($file, $class){ require_once($file); require_once "Zend/Loader.php"; Zend_Loader::registerAutoload(); $reflection = Zend_Server_Reflection::reflectClass($class); $description = array(); foreach($reflection->getMethods() as $method){ $docBlock = $method->getDocComment(); //retrieve the return and add it into the description if not array|object preg_match_all('/@return\s+(\w+)\s+((?:\$)?\w+)/', $docBlock, $returnmatches); //retrieve the subparam and subreturn preg_match_all('/\s*\*\s*@(subparam|subreturn)\s+(\w+)\s+(\$\w+(?::\w+|->\w+)+)((?:\s+(?:optional|required|multiple))*)/', $docBlock, $matches); /// process every @subparam and @subreturn line of the docblock for($i=0;$igetName()])) { $description[$method->getName()] = array(); } /// directly set description[method][return] if the return value is a primary type if (strpos($returnmatches[1][0] ,"object")===false && strpos($returnmatches[1][0],"array")===false) { $description[$method->getName()]['return'] = array($returnmatches[2][0] => $returnmatches[1][0]); } ///algorythm parts ///1. We compare the string to the description array /// When we find a attribut that is not in the description, we retrieve all the rest of the string ///2. We create the missing part of the description array, starting from the end of the rest of the string ///3. We add the missing part to the description array ///Part 1. /// extract the first part into $param (has to be $params in the case of @subparam, or anything in the case of $subreturn) /// extract the second part if (strpos($matches[3][$i], "->")===false || (strpos($matches[3][$i], ":")!==false && (strpos($matches[3][$i], ":") < strpos($matches[3][$i], "->")))) { $separator = ":"; $separatorsize=1; } else { $separator = "->"; $separatorsize=2; } $param = substr($matches[3][$i],1,strpos($matches[3][$i], $separator)-1); //first element/part/array //for example for the line @subparam string $params:students->student->name // @params is the first element/part/array of this docnlock line // students is the second element/part/array // ... // name is the terminal element, this element will be generated as String here $otherparam = substr($matches[3][$i],strpos($matches[3][$i], $separator)+$separatorsize); //rest of the line $parsingdesc = $description[$method->getName()]; //$pasingdesc is the current position of the algorythm into the description array //it is used to check if a element already exist into the description array if (!empty($parsingdesc) && array_key_exists($descriptiontype, $parsingdesc)){ $parsingdesc = $parsingdesc[$descriptiontype]; } $descriptionpath=array(); //we save in this variable the description path (e.g all keys to go deep into the description array) //it will be used to know where to add a new part the description array $creationfinished = false; //it's used to stop the algorythm when we find a new element that we can add to the descripitoin unset($type); /// try to extract the other elements and add them to the descripition id there are not already in the description while(!$creationfinished && (strpos($otherparam, ":") || strpos($otherparam, "->"))) { if (strpos($otherparam, "->")===false || (strpos($otherparam, ":")!==false && (strpos($otherparam, ":") < strpos($otherparam, "->")))) { $type = $separator; $separator = ":"; $separatorsize=1; } else { $type = $separator; $separator = "->"; $separatorsize=2; } $param = substr($otherparam,0,strpos($otherparam, $separator)); $otherparam = substr($otherparam,strpos($otherparam, $separator)+$separatorsize); if ($type==":") { /// this element is not already in the description array yet and it is a non associative array /// we add it (and its sub structure) to the description array if (!array_key_exists('multiple:'.$param, $parsingdesc)){ $desctoadd = webservice_lib::create_end_of_descriptionline(":".$param.$separator.$otherparam, $matches[2][$i]); if(empty($descriptionpath) ) { if (empty($description[$method->getName()]) || !array_key_exists($descriptiontype, $description[$method->getName()])) { $desctoadd = array($descriptiontype => $desctoadd); } $paramtoadd = $descriptiontype; } else { $paramtoadd = 'multiple:'.$param; } webservice_lib::add_end_of_description($paramtoadd, $desctoadd, $description[$method->getName()], $descriptionpath); $creationfinished = true; // we do not want to keep going to parse this line, // neither add again the terminal element of the line to the descripiton } else { if(empty($descriptionpath)) { $descriptionpath[] = $descriptiontype; } $descriptionpath[] = 'multiple:'.$param; $parsingdesc = $parsingdesc['multiple:'.$param]; } } else { /// this element is not in the description array yet and it is a associative array /// we add it (and its sub structure) to the description array if (!array_key_exists($param, $parsingdesc)){ $desctoadd = webservice_lib::create_end_of_descriptionline("->".$param.$separator.$otherparam, $matches[2][$i]); if(empty($descriptionpath)) { if (empty($description[$method->getName()]) || !array_key_exists($descriptiontype, $description[$method->getName()])) { $desctoadd = array($descriptiontype => $desctoadd); } $paramtoadd = $descriptiontype; } else { $paramtoadd = $param; } webservice_lib::add_end_of_description($paramtoadd, $desctoadd, $description[$method->getName()], $descriptionpath); $creationfinished = true; // we do not want to keep going to parse this line, // neither add again the terminal element of the line to the descripiton } else { if(empty($descriptionpath)) { $descriptionpath[] = $descriptiontype; } $descriptionpath[] = $param; $parsingdesc = $parsingdesc[$param]; } } } /// Add the "terminal" element of the line to the description array if (!$creationfinished) { if (!empty($type) && $type==":") { $desctoadd = webservice_lib::create_end_of_descriptionline($separator.$otherparam, $matches[2][$i]); if(empty($descriptionpath)) { if (empty($description[$method->getName()]) || !array_key_exists($descriptiontype, $description[$method->getName()])) { $desctoadd = array($descriptiontype => $desctoadd); } $paramtoadd = $descriptiontype; } else { $paramtoadd = 'multiple:'.$param; } webservice_lib::add_end_of_description($paramtoadd, $desctoadd, $description[$method->getName()], $descriptionpath); } else { $desctoadd = webservice_lib::create_end_of_descriptionline($separator.$otherparam, $matches[2][$i]); if(empty($descriptionpath)) { if (empty($description[$method->getName()]) || !array_key_exists($descriptiontype, $description[$method->getName()])) { $desctoadd = array($descriptiontype => $desctoadd); } $paramtoadd = $descriptiontype; } else { $paramtoadd = $param; } webservice_lib::add_end_of_description($paramtoadd, $desctoadd, $description[$method->getName()], $descriptionpath); } } } } // echo "
";
//                var_dump($description);
//                echo "
"; return $description; } /** * Add a description part to the descripition array * @param $param * @param $desctoadd * @param $descriptionlevel * @param $descriptionpath * @param $level */ public static function add_end_of_description($param, $desctoadd, &$descriptionlevel, $descriptionpath, $level= 0){ if (sizeof($descriptionpath)==0 || sizeof($descriptionpath)==$level+1) { if (is_array($descriptionlevel) && !empty($descriptionlevel)) { foreach($desctoadd as $key=>$value) { if ($key!="params" && $key!="optional" && $key!="return") { //TODO $descriptionlevel[$param][$key] = $value; } else { $descriptionlevel[$param] = $value; } } } else { $descriptionlevel = $desctoadd; } } else { webservice_lib::add_end_of_description($param, $desctoadd, &$descriptionlevel[$descriptionpath[$level]], $descriptionpath, $level+1); } } /** * We create a description part for the description array * Structure explained in the "generate_webservice_description" dockblock * @param $stringtoadd * @param $type * @return */ public static function create_end_of_descriptionline($stringtoadd, $type) { if (strrpos($stringtoadd, "->")===false || (strrpos($stringtoadd, ":")!==false && (strrpos($stringtoadd, ":") > strrpos($stringtoadd, "->")))) { $separator = ":"; $separatorsize=1; } else { $separator = "->"; $separatorsize=2; } $param = substr($stringtoadd,strrpos($stringtoadd, $separator)+$separatorsize); $result = array( $param => $type); $otherparam = substr($stringtoadd,0,strlen($stringtoadd)-strlen($param)-$separatorsize); while(strrpos($otherparam, ":")!==false || strrpos($otherparam, "->")!==false) { if (strrpos($otherparam, "->")===false || (strrpos($otherparam, ":")!==false && (strrpos($otherparam, ":") > strrpos($otherparam, "->")))) { $separator = ":"; $separatorsize=1; } else { $separator = "->"; $separatorsize=2; } $param = substr($otherparam,strrpos($otherparam, $separator)+$separatorsize); $otherparam = substr($otherparam,0,strrpos($otherparam, $separator)); if ($separator==":") { $result = array('multiple:'.$param => $result); } else { $result = array($param => $result); } } return $result; } /** * Check if the Moodle site has the web service protocol enable * @global object $CFG * @param string $protocol */ function display_webservices_availability($protocol){ global $CFG; $available = true; echo get_string('webservicesenable','webservice').": "; if (empty($CFG->enablewebservices)) { echo "".get_string('fail','webservice').""; $available = false; } else { echo "".get_string('ok','webservice').""; } echo "
"; foreach(webservice_lib::get_list_protocols() as $wsprotocol) { if (strtolower($wsprotocol->get_protocolname()) == strtolower($protocol)) { echo get_string('protocolenable','webservice',array($wsprotocol->get_protocolname())).": "; if ( get_config($wsprotocol-> get_protocolname(), "enable")) { echo "".get_string('ok','webservice').""; } else { echo "".get_string('fail','webservice').""; $available = false; } echo "
"; continue; } } //check debugging if ($CFG->debugdisplay) { echo "".get_string('debugdisplayon','webservice').""; $available = false; } return $available; } } /** * Web Service server base class */ abstract class webservice_server { /** * Web Service Protocol name (eg. SOAP, REST, XML-RPC,...) * @var String */ private $protocolname; public function __construct() { } abstract public function run(); public function get_protocolname() { return $this->protocolname; } public function set_protocolname($protocolname) { $this->protocolname = $protocolname; } public function get_enable() { return get_config($this->get_protocolname(), "enable"); } public function set_enable($enable) { set_config("enable", $enable, $this->get_protocolname()); } } /** * Temporary authentication class to be removed/modified */ class ws_authentication { /** * * @param array|struct $params * @return integer */ function tmp_get_token($params) { if ($params['username'] == 'wsuser' && $params['password'] == 'wspassword') { return '456'; } else { throw new moodle_exception('wrongusernamepassword'); } } } /** * Form for web service user settings (administration) */ final class wsuser_form extends moodleform { protected $username; /** * Definition of the moodleform */ public function definition() { global $DB; $this->username = $this->_customdata['username']; $mform =& $this->_form; $strrequired = get_string('required'); $mform->addElement('hidden', 'username', $this->username); $param = new stdClass(); $param->username = $this->username; $wsuser = $DB->get_record("user", array("username" => $this->username)); $mform->addElement('text', 'ipwhitelist', get_string('ipwhitelist', 'admin'), array('value'=>get_user_preferences("ipwhitelist", "", $wsuser->id),'size' => '40')); $mform->addElement('static', null, '', get_string('ipwhitelistdesc','admin', $param)); $this->add_action_buttons(true, get_string('savechanges','admin')); } } ?>