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