1<?php
2// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
3//
4// All Rights Reserved. See copyright.txt for details and a complete list of authors.
5// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
6// $Id$
7
8class Category_Manipulator
9{
10	private $objectType;
11	private $objectId;
12
13	private $current = [];
14	private $managed = [];
15	private $unmanaged = [];
16	private $constraints = [
17		'required' => [],
18	];
19	private $new = [];
20
21	private $prepared = false;
22	private $overrides = [];
23	private $overrideAll = false;
24
25	function __construct($objectType, $objectId)
26	{
27		$this->objectType = $objectType;
28		$this->objectId = $objectId;
29	}
30
31	function addRequiredSet(array $categories, $default, $filter = null, $type = null)
32	{
33		$categories = array_unique($categories);
34		$this->constraints['required'][] = [
35			'set' => $categories,
36			'default' => $default,
37			'filter' => $filter,
38			'type' => $type
39		];
40	}
41
42	function overrideChecks()
43	{
44		$this->overrideAll = true;
45	}
46
47	function setCurrentCategories(array $categories)
48	{
49		$this->current = $categories;
50	}
51
52	function setManagedCategories(array $categories)
53	{
54		$this->managed = $categories;
55	}
56
57	function setUnmanagedCategories(array $categories)
58	{
59		$this->unmanaged = $categories;
60	}
61
62	function setNewCategories(array $categories)
63	{
64		$this->new = $categories;
65	}
66
67	function getAddedCategories()
68	{
69		$this->prepare();
70
71		$attempt = array_diff($this->new, $this->current);
72		return $this->filter($attempt, 'add_object');
73	}
74
75	function getRemovedCategories()
76	{
77		$this->prepare();
78
79		$attempt = array_diff($this->current, $this->new);
80		return $this->filter($attempt, 'remove_object');
81	}
82
83
84	/*
85	 * Check wether the given permission is allowed for the given categories.
86	 * Note: The group in question requires also the _global_ permission 'modify_object_categories'
87	 * which could be given to a parent object like parent Tracker of a TrackerItem.
88	 * @param array $categories - requested categories
89	 * @param string  $permission - required permission for that category. Ie. 'add_category'
90	 * @return array $authorizedCategories - filterd list of given $categories that have proper permissions set.
91	 */
92	private function filter($categories, $permission)
93	{
94		$objectperms = Perms::get(['type' => $this->objectType, 'object' => $this->objectId]);
95		$canModifyObject = $objectperms->modify_object_categories;
96
97		$out = [];
98		foreach ($categories as $categ) {
99			$perms = Perms::get(['type' => 'category', 'object' => $categ]);
100			$hasCategoryPermission = $perms->$permission;
101
102			if ($this->overrideAll || ($canModifyObject && $hasCategoryPermission) || in_array($categ, $this->overrides)) {
103				$out[] = $categ;
104			}
105		}
106
107		return $out;
108	}
109
110
111	private function prepare()
112	{
113		if ($this->prepared) {
114			return;
115		}
116
117		$categories = $this->managed;
118		Perms::bulk(['type' => 'category'], 'object', $categories);
119
120		if (count($this->managed)) {
121			$base = array_diff($this->current, $this->managed);
122			$managed = array_intersect($this->new, $this->managed);
123			$this->new = array_merge($base, $managed);
124		}
125
126		if (count($this->unmanaged)) {
127			$base = array_intersect($this->current, $this->unmanaged);
128			$managed = array_diff($this->new, $this->unmanaged);
129			$this->new = array_merge($base, $managed);
130		}
131
132		$this->applyConstraints();
133
134		$this->prepared = true;
135	}
136
137	private function applyConstraints()
138	{
139		foreach ($this->constraints['required'] as $constraint) {
140			$set = $constraint['set'];
141			$default = $constraint['default'];
142			$filter = $constraint['filter'];
143			$type = $constraint['type'];
144
145			$interim = array_intersect($this->new, $set);
146
147			if (! empty($type) && $type != $this->objectType) {
148				return;
149			}
150
151			if (! empty($filter)) {
152				$objectlib = TikiLib::lib('object');
153				$info = $objectlib->get_info($this->objectType, $this->objectId);
154				if (! preg_match($filter, $info['title'])) {
155					return;
156				}
157			}
158
159			if (count($interim) == 0 && ! in_array($default, $this->new)) {
160				$this->new[] = $default;
161				$this->overrides[] = $default;
162			}
163		}
164	}
165}
166