1<?php 2 3/* Copyright (c) 1998-2019 ILIAS open source, Extended GPL, see docs/LICENSE */ 4 5/** 6 * Access handler for portfolio 7 * 8 * @author Jörg Lützenkirchen <luetzenkirchen@leifos.com> 9 */ 10class ilPortfolioAccessHandler implements ilWACCheckingClass 11{ 12 /** 13 * @var ilLanguage 14 */ 15 protected $lng; 16 17 /** 18 * @var ilObjUser 19 */ 20 protected $user; 21 22 /** 23 * @var ilRbacReview 24 */ 25 protected $rbacreview; 26 27 /** 28 * @var ilSetting 29 */ 30 protected $settings; 31 32 /** 33 * @var ilDB 34 */ 35 protected $db; 36 37 /** 38 * @var ilAccessHandler 39 */ 40 protected $access; 41 42 public function __construct() 43 { 44 global $DIC; 45 46 $this->lng = $DIC->language(); 47 $this->user = $DIC->user(); 48 $this->rbacreview = $DIC->rbac()->review(); 49 $this->settings = $DIC->settings(); 50 $this->db = $DIC->database(); 51 $this->access = $DIC->access(); 52 $lng = $DIC->language(); 53 $lng->loadLanguageModule("wsp"); 54 } 55 56 /** 57 * check access for an object 58 * 59 * @param string $a_permission 60 * @param string $a_cmd 61 * @param int $a_node_id 62 * @param string $a_type (optional) 63 * @return bool 64 */ 65 public function checkAccess($a_permission, $a_cmd, $a_node_id, $a_type = "") 66 { 67 $ilUser = $this->user; 68 69 return $this->checkAccessOfUser($ilUser->getId(), $a_permission, $a_cmd, $a_node_id, $a_type); 70 } 71 72 /** 73 * check access for an object 74 * 75 * @param integer $a_user_id 76 * @param string $a_permission 77 * @param string $a_cmd 78 * @param int $a_node_id 79 * @param string $a_type (optional) 80 * @return bool 81 */ 82 public function checkAccessOfUser($a_user_id, $a_permission, $a_cmd, $a_node_id, $a_type = "") 83 { 84 $rbacreview = $this->rbacreview; 85 $ilUser = $this->user; 86 $ilSetting = $this->settings; 87 88 // #20310 89 if (!$ilSetting->get("enable_global_profiles") && $ilUser->getId() == ANONYMOUS_USER_ID) { 90 return false; 91 } 92 93 // #12059 94 if (!$ilSetting->get('user_portfolios')) { 95 return false; 96 } 97 98 // :TODO: create permission for parent node with type ?! 99 100 $pf = new ilObjPortfolio($a_node_id, false); 101 if (!$pf->getId()) { 102 return false; 103 } 104 105 // portfolio owner has all rights 106 if ($pf->getOwner() == $a_user_id) { 107 return true; 108 } 109 110 // #11921 111 if (!$pf->isOnline()) { 112 return false; 113 } 114 115 // other users can only read 116 if ($a_permission == "read" || $a_permission == "visible") { 117 // get all objects with explicit permission 118 $objects = self::_getPermissions($a_node_id); 119 if ($objects) { 120 // check if given user is member of object or has role 121 foreach ($objects as $obj_id) { 122 switch ($obj_id) { 123 case ilWorkspaceAccessGUI::PERMISSION_ALL: 124 return true; 125 126 case ilWorkspaceAccessGUI::PERMISSION_ALL_PASSWORD: 127 // check against input kept in session 128 if (self::getSharedNodePassword($a_node_id) == self::getSharedSessionPassword($a_node_id) || 129 $a_permission == "visible") { 130 return true; 131 } 132 break; 133 134 case ilWorkspaceAccessGUI::PERMISSION_REGISTERED: 135 if ($ilUser->getId() != ANONYMOUS_USER_ID) { 136 return true; 137 } 138 break; 139 140 default: 141 switch (ilObject::_lookupType($obj_id)) { 142 case "grp": 143 // member of group? 144 if (ilGroupParticipants::_getInstanceByObjId($obj_id)->isAssigned($a_user_id)) { 145 return true; 146 } 147 break; 148 149 case "crs": 150 // member of course? 151 if (ilCourseParticipants::_getInstanceByObjId($obj_id)->isAssigned($a_user_id)) { 152 return true; 153 } 154 break; 155 156 case "role": 157 // has role? 158 if ($rbacreview->isAssigned($a_user_id, $obj_id)) { 159 return true; 160 } 161 break; 162 163 case "usr": 164 // direct assignment 165 if ($a_user_id == $obj_id) { 166 return true; 167 } 168 break; 169 } 170 break; 171 } 172 } 173 } 174 } 175 176 return false; 177 } 178 179 /** 180 * Set permissions after creating node/object 181 * 182 * @param int $a_parent_node_id 183 * @param int $a_node_id 184 */ 185 public function setPermissions($a_parent_node_id, $a_node_id) 186 { 187 // nothing to do as owner has irrefutable rights to any portfolio object 188 } 189 190 /** 191 * Add permission to node for object 192 * 193 * @param int $a_node_id 194 * @param int $a_object_id 195 * @param string $a_extended_data 196 */ 197 public function addPermission($a_node_id, $a_object_id, $a_extended_data = null) 198 { 199 $ilDB = $this->db; 200 $ilUser = $this->user; 201 202 // current owner must not be added 203 if ($a_object_id == $ilUser->getId()) { 204 return; 205 } 206 207 $ilDB->manipulate("INSERT INTO usr_portf_acl (node_id, object_id, extended_data, tstamp)" . 208 " VALUES (" . $ilDB->quote($a_node_id, "integer") . ", " . 209 $ilDB->quote($a_object_id, "integer") . "," . 210 $ilDB->quote($a_extended_data, "text") . "," . 211 $ilDB->quote(time(), "integer") . ")"); 212 213 // portfolio as profile 214 $this->syncProfile($a_node_id); 215 } 216 217 /** 218 * Remove permission[s] (for object) to node 219 * 220 * @param int $a_node_id 221 * @param int $a_object_id 222 */ 223 public function removePermission($a_node_id, $a_object_id = null) 224 { 225 $ilDB = $this->db; 226 227 $query = "DELETE FROM usr_portf_acl" . 228 " WHERE node_id = " . $ilDB->quote($a_node_id, "integer"); 229 230 if ($a_object_id) { 231 $query .= " AND object_id = " . $ilDB->quote($a_object_id, "integer"); 232 } 233 234 $ilDB->manipulate($query); 235 236 // portfolio as profile 237 $this->syncProfile($a_node_id); 238 } 239 240 /** 241 * Get all permissions to node 242 * 243 * @param int $a_node_id 244 * @return array 245 */ 246 public function getPermissions($a_node_id) 247 { 248 return self::_getPermissions($a_node_id); 249 } 250 251 /** 252 * Get all permissions to node 253 * 254 * @param int $a_node_id 255 * @return array 256 */ 257 public static function _getPermissions($a_node_id) 258 { 259 global $DIC; 260 261 $ilDB = $DIC->database(); 262 263 $set = $ilDB->query("SELECT object_id FROM usr_portf_acl" . 264 " WHERE node_id = " . $ilDB->quote($a_node_id, "integer")); 265 $res = array(); 266 while ($row = $ilDB->fetchAssoc($set)) { 267 $res[] = $row["object_id"]; 268 } 269 return $res; 270 } 271 272 public function hasRegisteredPermission($a_node_id) 273 { 274 $ilDB = $this->db; 275 276 $set = $ilDB->query("SELECT object_id FROM usr_portf_acl" . 277 " WHERE node_id = " . $ilDB->quote($a_node_id, "integer") . 278 " AND object_id = " . $ilDB->quote(ilWorkspaceAccessGUI::PERMISSION_REGISTERED, "integer")); 279 return (bool) $ilDB->numRows($set); 280 } 281 282 public function hasGlobalPermission($a_node_id) 283 { 284 $ilDB = $this->db; 285 286 $set = $ilDB->query("SELECT object_id FROM usr_portf_acl" . 287 " WHERE node_id = " . $ilDB->quote($a_node_id, "integer") . 288 " AND object_id = " . $ilDB->quote(ilWorkspaceAccessGUI::PERMISSION_ALL, "integer")); 289 return (bool) $ilDB->numRows($set); 290 } 291 292 public function hasGlobalPasswordPermission($a_node_id) 293 { 294 $ilDB = $this->db; 295 296 $set = $ilDB->query("SELECT object_id FROM usr_portf_acl" . 297 " WHERE node_id = " . $ilDB->quote($a_node_id, "integer") . 298 " AND object_id = " . $ilDB->quote(ilWorkspaceAccessGUI::PERMISSION_ALL_PASSWORD, "integer")); 299 return (bool) $ilDB->numRows($set); 300 } 301 302 public function getObjectsIShare($a_online_only = true) 303 { 304 $ilDB = $this->db; 305 $ilUser = $this->user; 306 307 $res = array(); 308 309 $sql = "SELECT obj.obj_id" . 310 " FROM object_data obj" . 311 " JOIN usr_portfolio prtf ON (prtf.id = obj.obj_id)" . 312 " JOIN usr_portf_acl acl ON (acl.node_id = obj.obj_id)" . 313 " WHERE obj.owner = " . $ilDB->quote($ilUser->getId(), "integer"); 314 315 if ($a_online_only) { 316 $sql .= " AND prtf.is_online = " . $ilDB->quote(1, "integer"); 317 } 318 319 $set = $ilDB->query($sql); 320 while ($row = $ilDB->fetchAssoc($set)) { 321 $res[] = $row["obj_id"]; 322 } 323 324 return $res; 325 } 326 327 public static function getPossibleSharedTargets() 328 { 329 global $DIC; 330 331 $ilUser = $DIC->user(); 332 333 $grp_ids = ilParticipants::_getMembershipByType($ilUser->getId(), "grp"); 334 $crs_ids = ilParticipants::_getMembershipByType($ilUser->getId(), "crs"); 335 336 $obj_ids = array_merge($grp_ids, $crs_ids); 337 $obj_ids[] = $ilUser->getId(); 338 $obj_ids[] = ilWorkspaceAccessGUI::PERMISSION_REGISTERED; 339 $obj_ids[] = ilWorkspaceAccessGUI::PERMISSION_ALL; 340 $obj_ids[] = ilWorkspaceAccessGUI::PERMISSION_ALL_PASSWORD; 341 342 return $obj_ids; 343 } 344 345 public function getSharedOwners() 346 { 347 $ilUser = $this->user; 348 $ilDB = $this->db; 349 350 $obj_ids = $this->getPossibleSharedTargets(); 351 352 $user_ids = array(); 353 $set = $ilDB->query("SELECT DISTINCT(obj.owner), u.lastname, u.firstname, u.title" . 354 " FROM object_data obj" . 355 " JOIN usr_portfolio prtf ON (prtf.id = obj.obj_id)" . 356 " JOIN usr_portf_acl acl ON (acl.node_id = obj.obj_id)" . 357 " JOIN usr_data u on (u.usr_id = obj.owner)" . 358 " WHERE " . $ilDB->in("acl.object_id", $obj_ids, "", "integer") . 359 " AND obj.owner <> " . $ilDB->quote($ilUser->getId(), "integer") . 360 " AND prtf.is_online = " . $ilDB->quote(1, "integer") . 361 " ORDER BY u.lastname, u.firstname, u.title"); 362 while ($row = $ilDB->fetchAssoc($set)) { 363 $user_ids[$row["owner"]] = $row["lastname"] . ", " . $row["firstname"]; 364 if ($row["title"]) { 365 $user_ids[$row["owner"]] .= ", " . $row["title"]; 366 } 367 } 368 369 return $user_ids; 370 } 371 372 public function getSharedObjects($a_owner_id) 373 { 374 $ilDB = $this->db; 375 376 $obj_ids = $this->getPossibleSharedTargets(); 377 378 $res = array(); 379 $set = $ilDB->query("SELECT obj.obj_id, obj.owner" . 380 " FROM object_data obj" . 381 " JOIN usr_portfolio prtf ON (prtf.id = obj.obj_id)" . 382 " JOIN usr_portf_acl acl ON (acl.node_id = obj.obj_id)" . 383 " WHERE " . $ilDB->in("acl.object_id", $obj_ids, "", "integer") . 384 " AND obj.owner = " . $ilDB->quote($a_owner_id, "integer") . 385 " AND prtf.is_online = " . $ilDB->quote(1, "integer")); 386 while ($row = $ilDB->fetchAssoc($set)) { 387 $res[$row["obj_id"]] = $row["obj_id"]; 388 } 389 390 return $res; 391 } 392 393 public function getShardObjectsDataForUserIds(array $a_owner_ids) 394 { 395 $ilDB = $this->db; 396 397 $obj_ids = $this->getPossibleSharedTargets(); 398 399 $res = array(); 400 401 $set = $ilDB->query("SELECT obj.obj_id, obj.owner, obj.title" . 402 " FROM object_data obj" . 403 " JOIN usr_portfolio prtf ON (prtf.id = obj.obj_id)" . 404 " JOIN usr_portf_acl acl ON (acl.node_id = obj.obj_id)" . 405 " WHERE " . $ilDB->in("acl.object_id", $obj_ids, "", "integer") . 406 " AND " . $ilDB->in("obj.owner", $a_owner_ids, "", "integer") . 407 " AND prtf.is_online = " . $ilDB->quote(1, "integer")); 408 while ($row = $ilDB->fetchAssoc($set)) { 409 $res[$row["owner"]][$row["obj_id"]] = $row["title"]; 410 } 411 412 return $res; 413 } 414 415 public function findSharedObjects(array $a_filter = null, array $a_crs_ids = null, array $a_grp_ids = null) 416 { 417 $ilDB = $this->db; 418 $ilUser = $this->user; 419 if (!$a_filter["acl_type"]) { 420 $obj_ids = $this->getPossibleSharedTargets(); 421 } else { 422 switch ($a_filter["acl_type"]) { 423 case "all": 424 $obj_ids = array(ilWorkspaceAccessGUI::PERMISSION_ALL); 425 break; 426 427 case "password": 428 $obj_ids = array(ilWorkspaceAccessGUI::PERMISSION_ALL_PASSWORD); 429 break; 430 431 case "registered": 432 $obj_ids = array(ilWorkspaceAccessGUI::PERMISSION_REGISTERED); 433 break; 434 435 case "course": 436 $obj_ids = $a_crs_ids; 437 break; 438 439 case "group": 440 $obj_ids = $a_grp_ids; 441 break; 442 443 case "user": 444 $obj_ids = array($ilUser->getId()); 445 break; 446 } 447 } 448 449 $res = array(); 450 451 $sql = "SELECT obj.obj_id,obj.title,obj.owner" . 452 ",acl.object_id acl_type, acl.tstamp acl_date" . 453 " FROM object_data obj" . 454 " JOIN usr_portfolio prtf ON (prtf.id = obj.obj_id)" . 455 " JOIN usr_portf_acl acl ON (acl.node_id = obj.obj_id)" . 456 " WHERE " . $ilDB->in("acl.object_id", $obj_ids, "", "integer") . 457 " AND obj.owner <> " . $ilDB->quote($ilUser->getId(), "integer") . 458 " AND obj.type = " . $ilDB->quote("prtf", "text") . 459 " AND prtf.is_online = " . $ilDB->quote(1, "integer"); 460 461 if ($a_filter["title"] && strlen($a_filter["title"]) >= 3) { 462 $sql .= " AND " . $ilDB->like("obj.title", "text", "%" . $a_filter["title"] . "%"); 463 } 464 if ($a_filter["user"] && strlen($a_filter["user"]) >= 3) { 465 $usr_ids = array(); 466 $set = $ilDB->query("SELECT usr_id FROM usr_data" . 467 " WHERE (" . $ilDB->like("login", "text", "%" . $a_filter["user"] . "%") . " " . 468 "OR " . $ilDB->like("firstname", "text", "%" . $a_filter["user"] . "%") . " " . 469 "OR " . $ilDB->like("lastname", "text", "%" . $a_filter["user"] . "%") . " " . 470 "OR " . $ilDB->like("email", "text", "%" . $a_filter["user"] . "%") . ")"); 471 while ($row = $ilDB->fetchAssoc($set)) { 472 $usr_ids[] = $row["usr_id"]; 473 } 474 if (!sizeof($usr_ids)) { 475 return; 476 } 477 $sql .= " AND " . $ilDB->in("obj.owner", $usr_ids, "", "integer"); 478 } 479 480 if ($a_filter["acl_date"]) { 481 $dt = $a_filter["acl_date"]->get(IL_CAL_DATE); 482 $dt = new ilDateTime($dt . " 00:00:00", IL_CAL_DATETIME); 483 $sql .= " AND acl.tstamp > " . $ilDB->quote($dt->get(IL_CAL_UNIX), "integer"); 484 } 485 486 if ($a_filter["crsgrp"]) { 487 $part = ilParticipants::getInstanceByObjId($a_filter['crsgrp']); 488 $part = $part->getParticipants(); 489 if (!sizeof($part)) { 490 return; 491 } 492 $sql .= " AND " . $ilDB->in("obj.owner", $part, "", "integer"); 493 } 494 495 // we use the oldest share date 496 $sql .= " ORDER BY acl.tstamp"; 497 498 $set = $ilDB->query($sql); 499 while ($row = $ilDB->fetchAssoc($set)) { 500 if (!isset($res[$row["obj_id"]])) { 501 $row["acl_type"] = array($row["acl_type"]); 502 $res[$row["obj_id"]] = $row; 503 } else { 504 $res[$row["obj_id"]]["acl_type"][] = $row["acl_type"]; 505 } 506 } 507 508 return $res; 509 } 510 511 public static function getSharedNodePassword($a_node_id) 512 { 513 global $DIC; 514 515 $ilDB = $DIC->database(); 516 517 $set = $ilDB->query("SELECT extended_data FROM usr_portf_acl" . 518 " WHERE node_id = " . $ilDB->quote($a_node_id, "integer") . 519 " AND object_id = " . $ilDB->quote(ilWorkspaceAccessGUI::PERMISSION_ALL_PASSWORD, "integer")); 520 $res = $ilDB->fetchAssoc($set); 521 if ($res) { 522 return $res["extended_data"]; 523 } 524 } 525 526 public static function keepSharedSessionPassword($a_node_id, $a_password) 527 { 528 $_SESSION["ilshpw_" . $a_node_id] = $a_password; 529 } 530 531 public static function getSharedSessionPassword($a_node_id) 532 { 533 return $_SESSION["ilshpw_" . $a_node_id]; 534 } 535 536 protected function syncProfile($a_node_id) 537 { 538 $ilUser = $this->user; 539 540 // #12845 541 if (ilObjPortfolio::getDefaultPortfolio($ilUser->getId()) == $a_node_id) { 542 $has_registered = $this->hasRegisteredPermission($a_node_id); 543 $has_global = $this->hasGlobalPermission($a_node_id); 544 545 // not published anymore - remove portfolio as profile 546 if (!$has_registered && !$has_global) { 547 $ilUser->setPref("public_profile", "n"); 548 $ilUser->writePrefs(); 549 ilObjPortfolio::setUserDefault($ilUser->getId()); 550 } 551 // adapt profile setting 552 else { 553 $new_pref = "y"; 554 if ($has_global) { 555 $new_pref = "g"; 556 } 557 if ($ilUser->getPref("public_profile") != $new_pref) { 558 $ilUser->setPref("public_profile", $new_pref); 559 $ilUser->writePrefs(); 560 } 561 } 562 } 563 } 564 565 566 /** 567 * @param ilWACPath $ilWACPath 568 * 569 * @return bool 570 */ 571 public function canBeDelivered(ilWACPath $ilWACPath) 572 { 573 $ilUser = $this->user; 574 $ilAccess = $this->access; 575 576 if (preg_match("/\\/prtf_([\\d]*)\\//uism", $ilWACPath->getPath(), $results)) { 577 // portfolio (custom) 578 $obj_id = $results[1]; 579 if (ilObject::_lookupType($obj_id) == "prtf") { 580 if ($this->checkAccessOfUser($ilUser->getId(), "read", "view", $obj_id, "prtf")) { 581 return true; 582 } 583 } 584 // portfolio template (RBAC) 585 else { 586 $ref_ids = ilObject::_getAllReferences($obj_id); 587 foreach ($ref_ids as $ref_id) { 588 if ($ilAccess->checkAccessOfUser($ilUser->getId(), "read", "view", $ref_id, "prtt", $obj_id)) { 589 return true; 590 } 591 } 592 } 593 } 594 595 return false; 596 } 597 598 /** 599 * Is portfolio editing (general feature) enabled 600 */ 601 public function editPortfolios() : bool 602 { 603 return (bool) $this->settings->get('user_portfolios'); 604 } 605 606} 607