FusionDirectory
class_Action.inc
1 <?php
2 /*
3  This code is part of FusionDirectory (http://www.fusiondirectory.org/)
4  Copyright (C) 2017-2018 FusionDirectory
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10 
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with this program; if not, write to the Free Software
18  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20 
24 class Action
25 {
26  protected $name;
27  protected $label;
28  protected $icon;
29 
30  /* 0, 1, ?, + or * */
31  protected $targets;
32 
33  /* Object types this action is present on */
34  protected $validTypes;
35 
36  protected $acl;
37 
38  /* Booleans */
39  protected $inmenu;
40  protected $inline;
41 
42  protected $callable;
43  protected $enabledCallable;
44 
45  protected $minTargets;
46  protected $maxTargets;
47 
48  protected $separator = FALSE;
49 
50  protected $parent;
51 
52  function __construct (string $name, $label, $icon, string $targets, $callable, array $acls = [], bool $inmenu = TRUE, bool $inline = TRUE, array $validTypes = [])
53  {
54  if ($targets == '0') {
55  $inline = FALSE;
56  }
57 
58  $this->name = $name;
59  $this->label = $label;
60  $this->icon = $icon;
61  $this->targets = $targets;
62  $this->callable = $callable;
63  $this->inmenu = $inmenu;
64  $this->inline = $inline;
65  $this->validTypes = array_map('strtoupper', $validTypes);
66  $this->acl = [];
67  /*
68  * acl may be of the form:
69  * acl (ex: 'd')
70  * attribute:acl (ex: 'userPassword:r')
71  * category/class/acl (ex: 'user/template/r')
72  * category/class/attribute:acl (ex: 'user/user/userPassword:r')
73  * /class/acl (ex: '/SnapshotHandler/c')
74  * /class/attribute:acl (ex: '/template/template_cn:w')
75  * */
76  foreach ($acls as $acl) {
77  $category = NULL;
78  $class = NULL;
79  $attribute = '0';
80  if (strpos($acl, '/') !== FALSE) {
81  list($category, $class, $acl) = explode('/', $acl, 3);
82  }
83  if (strpos($acl, ':') !== FALSE) {
84  list($attribute, $acl) = explode(':', $acl, 2);
85  }
86  $this->acl[] = [
87  'category' => $category,
88  'class' => $class,
89  'attribute' => $attribute,
90  'acl' => str_split($acl),
91  ];
92  }
93 
94  switch ($this->targets) {
95  case '0':
96  $this->minTargets = 0;
97  $this->maxTargets = 0;
98  break;
99  case '1':
100  $this->minTargets = 1;
101  $this->maxTargets = 1;
102  break;
103  case '?':
104  $this->minTargets = 0;
105  $this->maxTargets = 1;
106  break;
107  case '+':
108  $this->minTargets = 1;
109  $this->maxTargets = FALSE;
110  break;
111  case '*':
112  $this->minTargets = 0;
113  $this->maxTargets = FALSE;
114  break;
115  default:
116  throw new FusionDirectoryException('Invalid targets value for action '.$this->name.': '.$this->targets);
117  }
118  }
119 
120  function setParent (management $parent)
121  {
122  $this->parent = $parent;
123  }
124 
125  function getName (): string
126  {
127  return $this->name;
128  }
129 
130  function getLabel ()
131  {
132  return $this->label;
133  }
134 
135  function setSeparator (bool $bool)
136  {
137  $this->separator = $bool;
138  }
139 
140  function setEnableFunction (callable $callable)
141  {
142  $this->enabledCallable = $callable;
143  }
144 
145  function setInMenu (bool $inmenu)
146  {
147  $this->inmenu = $inmenu;
148  }
149 
150  function listActions (): array
151  {
152  return [$this->name];
153  }
154 
155  function execute (management $management, array $action)
156  {
157  if ($this->callable === FALSE) {
158  return;
159  }
160  foreach ($action['targets'] as $targetDn) {
161  if (!$this->hasPermission($management->listing->getEntry($targetDn))) {
162  throw new FusionDirectoryException(sprintf(_('You are not allowed to execute action "%s" on target "%s"'), $this->name, $targetDn));
163  }
164  }
165  if (count($action['targets']) < $this->minTargets) {
166  throw new FusionDirectoryException(sprintf(_('Not enough targets (%d) passed for action "%s"'), count($action['targets']), $this->name));
167  }
168  if (($this->maxTargets !== FALSE) && (count($action['targets']) > $this->maxTargets)) {
169  throw new FusionDirectoryException(sprintf(_('Too many targets (%d) passed for action "%s"'), count($action['targets']), $this->name));
170  }
171  $func = $this->callable;
172  if (!is_array($func)) {
173  $func = [$management, $func];
174  }
175  return call_user_func($func, $action);
176  }
177 
178  function fillMenuItems (array &$actions)
179  {
180  if (!$this->inmenu) {
181  return;
182  }
183 
184  if (!$this->hasPermission()) {
185  return;
186  }
187 
188  $actions[] = [
189  'name' => $this->name,
190  'icon' => $this->icon,
191  'label' => $this->label,
192  'enabled' => $this->isEnabledFor(),
193  'separator' => $this->separator,
194  ];
195  }
196 
197  function fillRowClasses (array &$classes, ListingEntry $entry)
198  {
199  }
200 
201  function renderColumnIcons (ListingEntry $entry): string
202  {
203  if (!$this->inline) {
204  return '';
205  }
206 
207  if (!empty($this->validTypes) && !($entry->isTemplate() && in_array('TEMPLATE', $this->validTypes)) && !in_array($entry->type, $this->validTypes)) {
208  return '';
209  }
210 
211  // Skip the entry completely if there's no permission to execute it
212  if (!$this->hasPermission($entry)) {
213  return '<img src="images/empty.png" alt=" " class="optional"/>';
214  }
215 
216  if (!$this->isEnabledFor($entry)) {
217  return '<img src="'.htmlescape($this->icon.'&disabled=1').'"'.
218  ' title="'.htmlescape($this->label).'" alt="'.htmlescape($this->label).'"/>';
219  }
220 
221  // Render
222  return '<input type="image" src="'.htmlescape($this->icon).'"'.
223  ' title="'.htmlescape($this->label).'" alt="'.htmlescape($this->label).'" name="listing_'.$this->name.'_'.$entry->row.'"/>';
224  }
225 
226  function isEnabledFor (ListingEntry $entry = NULL): bool
227  {
228  if (isset($this->enabledCallable)) {
229  return call_user_func($this->enabledCallable, $this->name, $entry);
230  }
231  return TRUE;
232  }
233 
234  function hasPermission (ListingEntry $entry = NULL): bool
235  {
236  global $ui;
237 
238  if ($entry === NULL) {
239  $dn = $this->parent->listing->getBase();
240  $types = $this->parent->objectTypes;
241  $template = FALSE;
242  } else {
243  $dn = $entry->aclBase;
244  $types = [$entry->getTemplatedType()];
245  $template = $entry->isTemplate();
246  }
247  /*
248  * if category is missing it’s deducted from type (all types are tested for menu actions)
249  * if class is missing it’s deducted from attribute if present, otherwise it’s type mainTab
250  * if attribute is missing 0 is used
251  */
252  foreach ($this->acl as $acl) {
253  $checkAcl = '';
254  if (!empty($acl['category'])) {
255  $checkAcl = $ui->get_permissions($dn, $acl['category'].'/'.$acl['class'], $acl['attribute']);
256  } elseif (empty($acl['class']) && ($acl['attribute'] != '0')) {
257  foreach ($types as $type) {
258  $module = $ui->getAttributeCategory($type, $acl['attribute']);
259  $checkAcl .= $ui->get_permissions($dn, $module, $acl['attribute']);
260  }
261  } else {
262  foreach ($types as $type) {
263  $infos = objects::infos($type);
264  if (!empty($acl['class'])) {
265  /* Class with empty category may be used in special cases like '/SnapshotHandler/c'*/
266  $module = $infos['aclCategory'].'/'.$acl['class'];
267  } elseif ($template) {
268  $module = $infos['aclCategory'].'/template';
269  } else {
270  $module = $infos['aclCategory'].'/'.$infos['mainTab'];
271  }
272  $checkAcl .= $ui->get_permissions($dn, $module, $acl['attribute']);
273  }
274  }
275 
276  // Check rights
277  foreach ($acl['acl'] as $part) {
278  if (strpos($checkAcl, $part) === FALSE) {
279  return FALSE;
280  }
281  }
282  }
283 
284  return TRUE;
285  }
286 }
htmlescape(string $str)
Escape string for HTML output.
Definition: php_setup.inc:32
Action base class.
Parent class for all exceptions thrown in FusionDirectory.
This class contains all the function needed to manage acl.
Definition: class_acl.inc:30
Management base class.