1<?php 2 3namespace ceLTIc\LTI\DataConnector; 4 5use ceLTIc\LTI; 6use ceLTIc\LTI\ConsumerNonce; 7use ceLTIc\LTI\Context; 8use ceLTIc\LTI\ResourceLink; 9use ceLTIc\LTI\ResourceLinkShare; 10use ceLTIc\LTI\ResourceLinkShareKey; 11use ceLTIc\LTI\ToolConsumer; 12use ceLTIc\LTI\UserResult; 13 14/** 15 * Class to provide a connection to a persistent store for LTI objects 16 * 17 * This class assumes no data persistence - it should be extended for specific database connections. 18 * 19 * @author Stephen P Vickers <stephen@spvsoftwareproducts.com> 20 * @copyright SPV Software Products 21 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 22 */ 23class DataConnector 24{ 25 26 /** 27 * Default name for database table used to store tool consumers. 28 */ 29 const CONSUMER_TABLE_NAME = 'lti2_consumer'; 30 31 /** 32 * Default name for database table used to store contexts. 33 */ 34 const CONTEXT_TABLE_NAME = 'lti2_context'; 35 36 /** 37 * Default name for database table used to store resource links. 38 */ 39 const RESOURCE_LINK_TABLE_NAME = 'lti2_resource_link'; 40 41 /** 42 * Default name for database table used to store users. 43 */ 44 const USER_RESULT_TABLE_NAME = 'lti2_user_result'; 45 46 /** 47 * Default name for database table used to store resource link share keys. 48 */ 49 const RESOURCE_LINK_SHARE_KEY_TABLE_NAME = 'lti2_share_key'; 50 51 /** 52 * Default name for database table used to store nonce values. 53 */ 54 const NONCE_TABLE_NAME = 'lti2_nonce'; 55 56 /** 57 * Database connection. 58 * 59 * @var object|resource $db 60 */ 61 protected $db = null; 62 63 /** 64 * Prefix for database table names. 65 * 66 * @var string $dbTableNamePrefix 67 */ 68 protected $dbTableNamePrefix = ''; 69 70 /** 71 * SQL date format (default = 'Y-m-d') 72 * 73 * @var string $dateFormat 74 */ 75 protected $dateFormat = 'Y-m-d'; 76 77 /** 78 * SQL time format (default = 'H:i:s') 79 * 80 * @var string $timeFormat 81 */ 82 protected $timeFormat = 'H:i:s'; 83 84 /** 85 * Class constructor 86 * 87 * @param object|resource $db Database connection object 88 * @param string $dbTableNamePrefix Prefix for database table names (optional, default is none) 89 */ 90 public function __construct($db, $dbTableNamePrefix = '') 91 { 92 $this->db = $db; 93 $this->dbTableNamePrefix = $dbTableNamePrefix; 94 } 95 96### 97### ToolConsumer methods 98### 99 100 /** 101 * Load tool consumer object. 102 * 103 * @param ToolConsumer $consumer ToolConsumer object 104 * 105 * @return bool True if the tool consumer object was successfully loaded 106 */ 107 public function loadToolConsumer($consumer) 108 { 109 $consumer->secret = 'secret'; 110 $consumer->enabled = true; 111 $now = time(); 112 $consumer->created = $now; 113 $consumer->updated = $now; 114 115 return true; 116 } 117 118 /** 119 * Save tool consumer object. 120 * 121 * @param ToolConsumer $consumer Consumer object 122 * 123 * @return bool True if the tool consumer object was successfully saved 124 */ 125 public function saveToolConsumer($consumer) 126 { 127 $consumer->updated = time(); 128 129 return true; 130 } 131 132 /** 133 * Delete tool consumer object. 134 * 135 * @param ToolConsumer $consumer Consumer object 136 * 137 * @return bool True if the tool consumer object was successfully deleted 138 */ 139 public function deleteToolConsumer($consumer) 140 { 141 $consumer->initialize(); 142 143 return true; 144 } 145 146 /** 147 * Load tool consumer objects. 148 * 149 * @return ToolConsumer[] Array of all defined ToolConsumer objects 150 */ 151 public function getToolConsumers() 152 { 153 return array(); 154 } 155 156### 157### Context methods 158### 159 160 /** 161 * Load context object. 162 * 163 * @param Context $context Context object 164 * 165 * @return bool True if the context object was successfully loaded 166 */ 167 public function loadContext($context) 168 { 169 $now = time(); 170 $context->created = $now; 171 $context->updated = $now; 172 173 return true; 174 } 175 176 /** 177 * Save context object. 178 * 179 * @param Context $context Context object 180 * 181 * @return bool True if the context object was successfully saved 182 */ 183 public function saveContext($context) 184 { 185 $context->updated = time(); 186 187 return true; 188 } 189 190 /** 191 * Delete context object. 192 * 193 * @param Context $context Context object 194 * 195 * @return bool True if the Context object was successfully deleted 196 */ 197 public function deleteContext($context) 198 { 199 $context->initialize(); 200 201 return true; 202 } 203 204### 205### ResourceLink methods 206### 207 208 /** 209 * Load resource link object. 210 * 211 * @param ResourceLink $resourceLink ResourceLink object 212 * 213 * @return bool True if the resource link object was successfully loaded 214 */ 215 public function loadResourceLink($resourceLink) 216 { 217 $now = time(); 218 $resourceLink->created = $now; 219 $resourceLink->updated = $now; 220 221 return true; 222 } 223 224 /** 225 * Save resource link object. 226 * 227 * @param ResourceLink $resourceLink ResourceLink object 228 * 229 * @return bool True if the resource link object was successfully saved 230 */ 231 public function saveResourceLink($resourceLink) 232 { 233 $resourceLink->updated = time(); 234 235 return true; 236 } 237 238 /** 239 * Delete resource link object. 240 * 241 * @param ResourceLink $resourceLink ResourceLink object 242 * 243 * @return bool True if the resource link object was successfully deleted 244 */ 245 public function deleteResourceLink($resourceLink) 246 { 247 $resourceLink->initialize(); 248 249 return true; 250 } 251 252 /** 253 * Get array of user objects. 254 * 255 * Obtain an array of UserResult objects for users with a result sourcedId. The array may include users from other 256 * resource links which are sharing this resource link. It may also be optionally indexed by the user ID of a specified scope. 257 * 258 * @param ResourceLink $resourceLink Resource link object 259 * @param bool $localOnly True if only users within the resource link are to be returned (excluding users sharing this resource link) 260 * @param int $idScope Scope value to use for user IDs 261 * 262 * @return UserResult[] Array of UserResult objects 263 */ 264 public function getUserResultSourcedIDsResourceLink($resourceLink, $localOnly, $idScope) 265 { 266 return array(); 267 } 268 269 /** 270 * Get array of shares defined for this resource link. 271 * 272 * @param ResourceLink $resourceLink ResourceLink object 273 * 274 * @return ResourceLinkShare[] Array of ResourceLinkShare objects 275 */ 276 public function getSharesResourceLink($resourceLink) 277 { 278 return array(); 279 } 280 281### 282### ConsumerNonce methods 283### 284 285 /** 286 * Load nonce object. 287 * 288 * @param ConsumerNonce $nonce Nonce object 289 * 290 * @return bool True if the nonce object was successfully loaded 291 */ 292 public function loadConsumerNonce($nonce) 293 { 294 return false; // assume the nonce does not already exist 295 } 296 297 /** 298 * Save nonce object. 299 * 300 * @param ConsumerNonce $nonce Nonce object 301 * 302 * @return bool True if the nonce object was successfully saved 303 */ 304 public function saveConsumerNonce($nonce) 305 { 306 return true; 307 } 308 309### 310### ResourceLinkShareKey methods 311### 312 313 /** 314 * Load resource link share key object. 315 * 316 * @param ResourceLinkShareKey $shareKey ResourceLink share key object 317 * 318 * @return bool True if the resource link share key object was successfully loaded 319 */ 320 public function loadResourceLinkShareKey($shareKey) 321 { 322 return true; 323 } 324 325 /** 326 * Save resource link share key object. 327 * 328 * @param ResourceLinkShareKey $shareKey Resource link share key object 329 * 330 * @return bool True if the resource link share key object was successfully saved 331 */ 332 public function saveResourceLinkShareKey($shareKey) 333 { 334 return true; 335 } 336 337 /** 338 * Delete resource link share key object. 339 * 340 * @param ResourceLinkShareKey $shareKey Resource link share key object 341 * 342 * @return bool True if the resource link share key object was successfully deleted 343 */ 344 public function deleteResourceLinkShareKey($shareKey) 345 { 346 return true; 347 } 348 349### 350### UserResult methods 351### 352 353 /** 354 * Load user object. 355 * 356 * @param UserResult $userresult UserResult object 357 * 358 * @return bool True if the user object was successfully loaded 359 */ 360 public function loadUserResult($userresult) 361 { 362 $now = time(); 363 $userresult->created = $now; 364 $userresult->updated = $now; 365 366 return true; 367 } 368 369 /** 370 * Save user object. 371 * 372 * @param UserResult $userresult UserResult object 373 * 374 * @return bool True if the user object was successfully saved 375 */ 376 public function saveUserResult($userresult) 377 { 378 $userresult->updated = time(); 379 380 return true; 381 } 382 383 /** 384 * Delete user object. 385 * 386 * @param UserResult $userresult UserResult object 387 * 388 * @return bool True if the user object was successfully deleted 389 */ 390 public function deleteUserResult($userresult) 391 { 392 $userresult->initialize(); 393 394 return true; 395 } 396 397### 398### Other methods 399### 400 401 /** 402 * Create data connector object. 403 * 404 * A data connector provides access to persistent storage for the different objects. 405 * 406 * Names of tables may be given a prefix to allow multiple versions to share the same schema. A separate sub-class is defined for 407 * each different database connection - the class to use is determined by inspecting the database object passed, but this can be overridden 408 * (for example, to use a bespoke connector) by specifying a type. If no database is passed then this class is used which acts as a dummy 409 * connector with no persistence. 410 * 411 * @param object|resource $db A database connection object or string (optional, default is no persistence) 412 * @param string $dbTableNamePrefix Prefix for database table names (optional, default is none) 413 * @param string $type The type of data connector (optional, default is based on $db parameter) 414 * 415 * @return DataConnector Data connector object 416 */ 417 public static function getDataConnector($db = null, $dbTableNamePrefix = '', $type = '') 418 { 419 if (is_null($dbTableNamePrefix)) { 420 $dbTableNamePrefix = ''; 421 } 422 if (!is_null($db) && empty($type)) { 423 if (is_object($db)) { 424 $type = get_class($db); 425 } elseif (is_resource($db)) { 426 $type = strtok(get_resource_type($db), ' '); 427 } 428 } 429 $type = strtolower($type); 430 if ($type === 'pdo') { 431 if ($db->getAttribute(\PDO::ATTR_DRIVER_NAME) === 'pgsql') { 432 $type .= '_pgsql'; 433 } elseif ($db->getAttribute(\PDO::ATTR_DRIVER_NAME) === 'oci') { 434 $type .= '_oci'; 435 } 436 } 437 if (!empty($type)) { 438 $type = "DataConnector_{$type}"; 439 } else { 440 $type = 'DataConnector'; 441 } 442 $type = "\\ceLTIc\\LTI\\DataConnector\\{$type}"; 443 $dataConnector = new $type($db, $dbTableNamePrefix); 444 445 return $dataConnector; 446 } 447 448 /** 449 * Generate a random string. 450 * 451 * The generated string will only comprise letters (upper- and lower-case) and digits. 452 * 453 * @param int $length Length of string to be generated (optional, default is 8 characters) 454 * 455 * @return string Random string 456 */ 457 public static function getRandomString($length = 8) 458 { 459 $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 460 461 $value = ''; 462 $charsLength = strlen($chars) - 1; 463 464 for ($i = 1; $i <= $length; $i++) { 465 $value .= $chars[rand(0, $charsLength)]; 466 } 467 468 return $value; 469 } 470 471 /** 472 * Escape a string for use in a database query. 473 * 474 * Any single quotes in the value passed will be replaced with two single quotes. If a null value is passed, a string 475 * of 'null' is returned (which will never be enclosed in quotes irrespective of the value of the $addQuotes parameter. 476 * 477 * @param string $value Value to be escaped 478 * @param bool $addQuotes If true the returned string will be enclosed in single quotes (optional, default is true) 479 * 480 * @return string The escaped string. 481 */ 482 public function escape($value, $addQuotes = true) 483 { 484 return static::quoted($value, $addQuotes); 485 } 486 487 /** 488 * Quote a string for use in a database query. 489 * 490 * Any single quotes in the value passed will be replaced with two single quotes. If a null value is passed, a string 491 * of 'null' is returned (which will never be enclosed in quotes irrespective of the value of the $addQuotes parameter. 492 * 493 * @param string $value Value to be quoted 494 * @param bool $addQuotes If true the returned string will be enclosed in single quotes (optional, default is true) 495 * 496 * @return string The quoted string. 497 */ 498 public static function quoted($value, $addQuotes = true) 499 { 500 if (is_null($value)) { 501 $value = 'null'; 502 } else { 503 $value = str_replace('\'', '\'\'', $value); 504 if ($addQuotes) { 505 $value = "'{$value}'"; 506 } 507 } 508 509 return $value; 510 } 511 512 /** 513 * Return a hash of a consumer key for values longer than 255 characters. 514 * 515 * @param string $key 516 * @return string 517 */ 518 protected static function getConsumerKey($key) 519 { 520 $len = strlen($key); 521 if ($len > 255) { 522 $key = 'sha512:' . hash('sha512', $key); 523 } 524 525 return $key; 526 } 527 528} 529