| Total Complexity | 64 |
| Total Lines | 467 |
| Duplicated Lines | 0 % |
| Changes | 0 | ||
Complex classes like admin_acl often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use admin_acl, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 23 | class admin_acl |
||
| 24 | { |
||
| 25 | /** |
||
| 26 | * Methods callable via menuaction |
||
| 27 | * @var array |
||
| 28 | */ |
||
| 29 | public $public_functions = array( |
||
| 30 | 'index' => true, |
||
| 31 | ); |
||
| 32 | |||
| 33 | /** |
||
| 34 | * Reference to global Acl class (instanciated for current user) |
||
| 35 | * |
||
| 36 | * @var Acl |
||
| 37 | */ |
||
| 38 | protected $acl; |
||
| 39 | |||
| 40 | /** |
||
| 41 | * Constructor |
||
| 42 | */ |
||
| 43 | public function __construct() |
||
| 44 | { |
||
| 45 | $this->acl = $GLOBALS['egw']->acl; |
||
| 46 | } |
||
| 47 | |||
| 48 | /** |
||
| 49 | * Save run rights and refresh opener |
||
| 50 | * |
||
| 51 | * @param array $content |
||
| 52 | */ |
||
| 53 | protected function save_run_rights(array $content) |
||
| 54 | { |
||
| 55 | $old_apps = array_keys($this->acl->get_user_applications($content['acl_account'], false, false)); |
||
| 56 | // add new apps |
||
| 57 | $added_apps = array_diff($content['apps'], $old_apps); |
||
| 58 | foreach($added_apps as $app) |
||
| 59 | { |
||
| 60 | $this->acl->add_repository($app, 'run', $content['acl_account'], 1); |
||
| 61 | } |
||
| 62 | // remove no longer checked apps |
||
| 63 | $removed_apps = array_diff($old_apps, $content['apps']); |
||
| 64 | $deleted_ids = array(); |
||
| 65 | foreach($removed_apps as $app) |
||
| 66 | { |
||
| 67 | $this->acl->delete_repository($app, 'run', $content['acl_account']); |
||
| 68 | $deleted_ids[] = $app.':'.$content['acl_account'].':run'; |
||
| 69 | } |
||
| 70 | //error_log(__METHOD__."() apps=".array2string($content['apps']).", old_apps=".array2string($old_apps).", added_apps=".array2string($added_apps).", removed_apps=".array2string($removed_apps)); |
||
| 71 | |||
| 72 | if (!$added_apps && !$removed_apps) |
||
| 73 | { |
||
| 74 | // nothing changed --> nothing to do/notify |
||
| 75 | } |
||
| 76 | elseif (!$old_apps) |
||
| 77 | { |
||
| 78 | Framework::refresh_opener(lang('ACL added.'), 'admin', null, 'add'); |
||
| 79 | } |
||
| 80 | elseif (!$added_apps) |
||
| 81 | { |
||
| 82 | Framework::refresh_opener(lang('ACL deleted.'), 'admin', $deleted_ids, 'delete'); |
||
| 83 | } |
||
| 84 | else |
||
| 85 | { |
||
| 86 | Framework::refresh_opener(lang('ACL updated.'), 'admin', null, 'edit'); |
||
| 87 | } |
||
| 88 | } |
||
| 89 | |||
| 90 | /** |
||
| 91 | * Save rights and refresh opener |
||
| 92 | * |
||
| 93 | * @param array $content |
||
| 94 | */ |
||
| 95 | protected function save_rights(array $content) |
||
| 96 | { |
||
| 97 | // assamble rights again |
||
| 98 | $rights = (int)$content['preserve_rights']; |
||
| 99 | foreach($content['acl'] as $right) |
||
| 100 | { |
||
| 101 | $rights |= $right; |
||
| 102 | } |
||
| 103 | $id = !empty($content['id']) ? $content['id'] : |
||
| 104 | $content['acl_appname'].':'.$content['acl_account'].':'.$content['acl_location']; |
||
| 105 | //error_log(__METHOD__."() id=$id, acl=".array2string($content['acl'])." --> rights=$rights"); |
||
| 106 | |||
| 107 | if ($this->acl->get_specific_rights_for_account($content['acl_account'], $content['acl_location'], $content['acl_appname']) == $rights) |
||
| 108 | { |
||
| 109 | // nothing changed --> nothing to do |
||
| 110 | } |
||
| 111 | elseif (!$rights) // all rights removed --> delete it |
||
| 112 | { |
||
| 113 | $this->acl->delete_repository($content['acl_appname'], $content['acl_location'], $content['acl_account']); |
||
| 114 | Framework::refresh_opener(lang('ACL deleted.'), 'admin', $id, 'delete'); |
||
| 115 | } |
||
| 116 | else |
||
| 117 | { |
||
| 118 | $this->acl->add_repository($content['acl_appname'], $content['acl_location'], $content['acl_account'], $rights); |
||
| 119 | if ($content['id']) |
||
| 120 | { |
||
| 121 | Framework::refresh_opener(lang('ACL updated.'), 'admin', $id, 'edit'); |
||
| 122 | } |
||
| 123 | else |
||
| 124 | { |
||
| 125 | Framework::refresh_opener(lang('ACL added.'), 'admin', $id, 'add'); |
||
| 126 | } |
||
| 127 | } |
||
| 128 | } |
||
| 129 | |||
| 130 | /** |
||
| 131 | * Callback for nextmatch to fetch Acl |
||
| 132 | * |
||
| 133 | * @param array $query |
||
| 134 | * @param array &$rows=null |
||
| 135 | * @return int total number of rows available |
||
| 136 | */ |
||
| 137 | public static function get_rows(array $query, array &$rows=null) |
||
| 138 | { |
||
| 139 | $so_sql = new Api\Storage\Base('phpgwapi', Acl::TABLE, null, '', true); |
||
| 140 | |||
| 141 | // client queries destinct rows by their row-id |
||
| 142 | if (isset($query['col_filter']['id'])) |
||
| 143 | { |
||
| 144 | $query['col_filter'][] = $GLOBALS['egw']->db->concat('acl_appname',"':'",'acl_account',"':'",'acl_location'). |
||
| 145 | ' IN ('.implode(',', array_map(array($GLOBALS['egw']->db, 'quote'), (array)$query['col_filter']['id'])).')'; |
||
| 146 | unset($query['col_filter']['id']); |
||
| 147 | } |
||
| 148 | else |
||
| 149 | { |
||
| 150 | $memberships = $GLOBALS['egw']->accounts->memberships($query['account_id'], true); |
||
| 151 | $memberships[] = $query['account_id']; |
||
| 152 | |||
| 153 | Api\Cache::setSession(__CLASS__, 'state', array( |
||
| 154 | 'account_id' => $query['account_id'], |
||
| 155 | 'filter' => $query['filter'], |
||
| 156 | 'acl_appname' => $query['filter2'], |
||
| 157 | )); |
||
| 158 | |||
| 159 | if ($GLOBALS['egw_info']['user']['preferences']['admin']['acl_filter'] != $query['filter']) |
||
| 160 | { |
||
| 161 | $GLOBALS['egw']->preferences->add('admin', 'acl_filter', $query['filter']); |
||
| 162 | $GLOBALS['egw']->preferences->save_repository(false,'user',false); |
||
| 163 | } |
||
| 164 | switch($query['filter']) |
||
| 165 | { |
||
| 166 | case 'run': |
||
| 167 | $query['col_filter']['acl_location'] = 'run'; |
||
| 168 | $query['col_filter']['acl_account'] = $memberships; |
||
| 169 | break; |
||
| 170 | default: |
||
| 171 | case 'other': |
||
| 172 | //$query['col_filter'][] = "acl_location!='run'"; |
||
| 173 | // remove everything not an account-id in location, like category based acl |
||
| 174 | if (substr($GLOBALS['egw']->db->Type, 0, 5) == 'mysql') |
||
| 175 | { |
||
| 176 | $query['col_filter'][] = "acl_location REGEXP '^-?[0-9]+$'"; |
||
| 177 | } |
||
| 178 | else |
||
| 179 | { |
||
| 180 | $query['col_filter'][] = "acl_location SIMILAR TO '-?[0-9]+'"; |
||
| 181 | } |
||
| 182 | // get apps not using group-acl (eg. Addressbook) or using it only partialy (eg. InfoLog) |
||
| 183 | $not_enum_group_acls = Api\Hooks::process('not_enum_group_acls', array(), true); |
||
| 184 | //error_log(__METHOD__."(filter=$query[filter]) not_enum_group_acl=".array2string($not_enum_group_acls)); |
||
| 185 | if ($not_enum_group_acls) |
||
| 186 | { |
||
| 187 | $sql = '(CASE acl_appname'; |
||
| 188 | foreach($not_enum_group_acls as $app => $groups) |
||
| 189 | { |
||
| 190 | if ($groups === true) |
||
| 191 | { |
||
| 192 | $check = $query['account_id']; |
||
| 193 | } |
||
| 194 | else |
||
| 195 | { |
||
| 196 | $check = array_diff($memberships, $groups); |
||
| 197 | //error_log(__METHOD__."() app=$app, array_diff(memberships=".array2string($memberships).", groups=".array2string($groups).")=".array2string($check)); |
||
| 198 | if (!$check) continue; // would give sql error otherwise! |
||
| 199 | } |
||
| 200 | $sql .= ' WHEN '.$GLOBALS['egw']->db->quote($app).' THEN '.$GLOBALS['egw']->db->expression(Acl::TABLE, array( |
||
| 201 | 'acl_account' => $check, |
||
| 202 | )); |
||
| 203 | } |
||
| 204 | $sql .= ' ELSE '; |
||
| 205 | } |
||
| 206 | $sql .= $GLOBALS['egw']->db->expression(Acl::TABLE, array( |
||
| 207 | 'acl_account' => $memberships, |
||
| 208 | )); |
||
| 209 | if ($not_enum_group_acls) $sql .= ' END)'; |
||
| 210 | $query['col_filter'][] = $sql; |
||
| 211 | break; |
||
| 212 | |||
| 213 | case 'own': |
||
| 214 | $query['col_filter']['acl_location'] = $memberships; |
||
| 215 | break; |
||
| 216 | } |
||
| 217 | // do NOT list group-memberships and other non-ACL stuff here |
||
| 218 | $query['col_filter']['acl_appname'] = $query['filter2']; |
||
| 219 | if (empty($query['col_filter']['acl_appname']) && $query['filter'] != 'run') |
||
| 220 | { |
||
| 221 | //$query['col_filter'][] = "NOT acl_appname IN ('phpgw_group','sqlfs')"; |
||
| 222 | $query['col_filter']['acl_appname'] = array_keys($query['acl_rights']); |
||
| 223 | } |
||
| 224 | } |
||
| 225 | $readonlys = array(); |
||
| 226 | $total = $so_sql->get_rows($query, $rows, $readonlys); |
||
| 227 | |||
| 228 | foreach($rows as &$row) |
||
| 229 | { |
||
| 230 | // generate a row-id |
||
| 231 | $row['id'] = $row['acl_appname'].':'.$row['acl_account'].':'.$row['acl_location']; |
||
| 232 | |||
| 233 | if ($row['acl_location'] == 'run') |
||
| 234 | { |
||
| 235 | $row['acl1'] = lang('run'); |
||
| 236 | } |
||
| 237 | else |
||
| 238 | { |
||
| 239 | if ($app !== $row['acl_appname']) Api\Translation::add_app($row['app_name']); |
||
| 240 | foreach($query['acl_rights'][$row['acl_appname']] as $val => $label) |
||
| 241 | { |
||
| 242 | if ($row['acl_rights'] & $val) |
||
| 243 | { |
||
| 244 | $row['acl'.$val] = lang($label); |
||
| 245 | } |
||
| 246 | } |
||
| 247 | } |
||
| 248 | if (!self::check_access($row['acl_account'], $row['acl_location'], false)) // false: do NOT throw an exception! |
||
| 249 | { |
||
| 250 | $row['class'] = 'rowNoEdit'; |
||
| 251 | } |
||
| 252 | //error_log(__METHOD__."() $n: ".array2string($row)); |
||
| 253 | } |
||
| 254 | //error_log(__METHOD__."(".array2string($query).") returning ".$total); |
||
| 255 | |||
| 256 | // Get supporting or all apps for filter2 depending on filter |
||
| 257 | if($query['filter'] == 'run') |
||
| 258 | { |
||
| 259 | $rows['sel_options']['filter2'] = array( |
||
| 260 | '' => lang('All applications'), |
||
| 261 | )+Etemplate\Widget\Select::app_options('enabled'); |
||
| 262 | } |
||
| 263 | else |
||
| 264 | { |
||
| 265 | $rows['sel_options']['filter2'] = array( |
||
| 266 | array('value' => '', 'label' => lang('All applications')) |
||
| 267 | ); |
||
| 268 | $apps = Api\Hooks::process(array( |
||
| 269 | 'location' => 'acl_rights', |
||
| 270 | 'owner' => $query['account_id'], |
||
| 271 | ), array(), true); |
||
| 272 | foreach(array_keys($apps) as $appname) |
||
| 273 | { |
||
| 274 | $rows['sel_options']['filter2'][] = array( |
||
| 275 | 'value' => $appname, |
||
| 276 | 'label' => lang($appname) |
||
| 277 | ); |
||
| 278 | } |
||
| 279 | usort($rows['sel_options']['filter2'], function($a,$b) { |
||
| 280 | return strcasecmp($a['label'], $b['label']); |
||
| 281 | }); |
||
| 282 | } |
||
| 283 | |||
| 284 | return $total; |
||
| 285 | } |
||
| 286 | |||
| 287 | /** |
||
| 288 | * Check if current user has access to ACL setting of a given location |
||
| 289 | * |
||
| 290 | * @param int $account_id numeric account-id |
||
| 291 | * @param int|string $location =null numeric account-id or "run" |
||
| 292 | * @param boolean $throw =true if true, throw an exception if no access, instead of just returning false |
||
| 293 | * @return boolean true if access is granted, false if notification_bo |
||
| 294 | * @throws Api\Exception\NoPermission |
||
| 295 | */ |
||
| 296 | public static function check_access($account_id, $location=null, $throw=true) |
||
| 297 | { |
||
| 298 | static $admin_access=null; |
||
| 299 | static $own_access=null; |
||
| 300 | if (is_null($admin_access)) |
||
| 301 | { |
||
| 302 | $admin_access = isset($GLOBALS['egw_info']['user']['apps']['admin']) && |
||
| 303 | !$GLOBALS['egw']->acl->check('account_access', 64, 'admin'); // ! because this denies access! |
||
| 304 | $own_access = $admin_access || isset($GLOBALS['egw_info']['user']['apps']['preferences']); |
||
| 305 | } |
||
| 306 | if (!(int)$account_id || !((int)$account_id == (int)$GLOBALS['egw_info']['user']['account_id'] && $location !== 'run' ? |
||
| 307 | $own_access : $admin_access)) |
||
| 308 | { |
||
| 309 | if ($throw) throw new Api\Exception\NoPermission(lang('Permission denied!!!')); |
||
| 310 | return false; |
||
| 311 | } |
||
| 312 | return true; |
||
| 313 | } |
||
| 314 | |||
| 315 | /** |
||
| 316 | * Get the list of applications allowed for the given user |
||
| 317 | * |
||
| 318 | * The list of applications is added to the json response |
||
| 319 | * |
||
| 320 | * @param int $account_id |
||
| 321 | */ |
||
| 322 | public static function ajax_get_app_list($account_id) |
||
| 330 | } |
||
| 331 | |||
| 332 | /** |
||
| 333 | * Change (add, modify, delete) an ACL entry |
||
| 334 | * |
||
| 335 | * Checks access and throws an exception, if a change is attempted without proper access |
||
| 336 | * |
||
| 337 | * @param string|array $ids "$app:$account:$location" string used as row-id in list |
||
| 338 | * @param int $rights null to delete, or new rights |
||
| 339 | * @param array $values Additional values from UI |
||
| 340 | * @param string $etemplate_exec_id to check against CSRF |
||
| 341 | * @throws Api\Exception\NoPermission |
||
| 342 | */ |
||
| 343 | public static function ajax_change_acl($ids, $rights, $values, $etemplate_exec_id) |
||
| 344 | { |
||
| 345 | Api\Etemplate\Request::csrfCheck($etemplate_exec_id, __METHOD__, func_get_args()); |
||
| 346 | |||
| 347 | try { |
||
| 348 | foreach((array)$ids as $id) |
||
| 349 | { |
||
| 350 | list($app, $account_id, $location) = explode(':', $id, 3); |
||
| 351 | |||
| 352 | self::check_access($account_id, $location); // throws exception, if no rights |
||
| 353 | |||
| 354 | $acl = $GLOBALS['egw']->acl; |
||
| 355 | |||
| 356 | if($location == 'run') |
||
| 357 | { |
||
| 358 | $right_list = array(1 => 'run'); |
||
| 359 | } |
||
| 360 | else |
||
| 361 | { |
||
| 362 | $right_list = Api\Hooks::single(array( |
||
| 363 | 'location' => 'acl_rights', |
||
| 364 | 'owner' => $location, |
||
| 365 | ), $app); |
||
| 366 | } |
||
| 367 | $current = (int)$acl->get_specific_rights_for_account($account_id,$location,$app); |
||
| 368 | foreach(array_keys((array)$right_list) as $right) |
||
| 369 | { |
||
| 370 | $have_it = !!($current & $right); |
||
| 371 | $set_it = !!($rights & $right); |
||
| 372 | if($have_it == $set_it) continue; |
||
| 373 | $data = array( |
||
| 374 | 'allow' => $set_it, |
||
| 375 | 'account' => $account_id, |
||
| 376 | 'app' => $app, |
||
| 377 | 'location' => $location, |
||
| 378 | 'rights' => (int)$right |
||
| 379 | // This is the documentation from policy app |
||
| 380 | )+(array)$values['admin_cmd']; |
||
| 381 | if($location == 'run') |
||
| 382 | { |
||
| 383 | $cmd = new admin_cmd_account_app($set_it,$account_id, $app, (array)$values['admin_cmd']); |
||
| 384 | } |
||
| 385 | else |
||
| 386 | { |
||
| 387 | $cmd = new admin_cmd_acl($data); |
||
| 388 | } |
||
| 389 | $cmd->run(); |
||
| 390 | } |
||
| 391 | } |
||
| 392 | if (!(int)$rights) |
||
| 393 | { |
||
| 394 | if (count($ids) > 1) |
||
| 395 | { |
||
| 396 | $msg = lang('%1 ACL entries deleted.', count($ids)); |
||
| 397 | } |
||
| 398 | else |
||
| 399 | { |
||
| 400 | $msg = lang('ACL entry deleted.'); |
||
| 401 | } |
||
| 402 | } |
||
| 403 | else |
||
| 404 | { |
||
| 405 | $msg = lang('ACL updated'); |
||
| 406 | } |
||
| 407 | Api\Json\Response::get()->data(array( |
||
| 408 | 'msg' => $msg, |
||
| 409 | 'ids' => $ids, |
||
| 410 | 'type' => !(int)$rights ? 'delete' : 'add', |
||
| 411 | )); |
||
| 412 | } |
||
| 413 | catch (Exception $e) { |
||
| 414 | Api\Json\Response::get()->call('egw.message', $e->getMessage(), 'error'); |
||
| 415 | } |
||
| 416 | } |
||
| 417 | |||
| 418 | /** |
||
| 419 | * New index page |
||
| 420 | * |
||
| 421 | * @param array $_content =null |
||
| 422 | */ |
||
| 423 | public function index(array $_content=null) |
||
| 424 | { |
||
| 425 | unset($_content); // not used, required by function signature |
||
| 426 | |||
| 427 | $tpl = new Etemplate('admin.acl'); |
||
| 428 | |||
| 429 | $content = array(); |
||
| 430 | $account_id = isset($_GET['account_id']) && (int)$_GET['account_id'] ? |
||
| 431 | (int)$_GET['account_id'] : $GLOBALS['egw_info']['user']['account_id']; |
||
| 432 | $content['nm'] = array( |
||
| 433 | 'get_rows' => 'admin_acl::get_rows', |
||
| 434 | 'no_cat' => true, |
||
| 435 | 'filter' => !empty($_GET['acl_filter']) ? $_GET['acl_filter'] : |
||
| 436 | ($GLOBALS['egw_info']['flags']['currentapp'] != 'admin' ? 'other' : |
||
| 437 | $GLOBALS['egw_info']['user']['preferences']['admin']['acl_filter']), |
||
| 438 | 'filter2' => !empty($_GET['acl_app']) ? $_GET['acl_app'] : '', |
||
| 439 | 'lettersearch' => false, |
||
| 440 | 'order' => 'acl_appname', |
||
| 441 | 'sort' => 'ASC', |
||
| 442 | 'row_id' => 'id', |
||
| 443 | 'account_id' => $account_id, |
||
| 444 | 'actions' => self::get_actions(), |
||
| 445 | 'acl_rights' => Api\Hooks::process(array( |
||
| 446 | 'location' => 'acl_rights', |
||
| 447 | 'owner' => $account_id, |
||
| 448 | ), array(), true), |
||
| 449 | ); |
||
| 450 | $user = Api\Accounts::username($content['nm']['account_id']); |
||
| 451 | $sel_options = array( |
||
| 452 | 'filter' => array( |
||
| 453 | 'other' => lang('Access to %1 data by others', $user), |
||
| 454 | 'own' => lang('%1 access to other data', $user), |
||
| 455 | 'run' => lang('%1 run rights for applications', $user), |
||
| 456 | ) |
||
| 457 | ); |
||
| 458 | |||
| 459 | // Set this so if loaded via preferences, js is still properly |
||
| 460 | // loaded into global app.admin |
||
| 461 | $GLOBALS['egw_info']['flags']['currentapp'] = 'admin'; |
||
| 462 | |||
| 463 | $tpl->exec('admin.admin_acl.index', $content, $sel_options, array(), array(), 2); |
||
| 464 | } |
||
| 465 | |||
| 466 | /** |
||
| 467 | * Get actions for ACL |
||
| 468 | * |
||
| 469 | * @return array |
||
| 470 | */ |
||
| 471 | static function get_actions() |
||
| 490 | ), |
||
| 491 | ); |
||
| 492 | } |
||
| 494 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)or! empty(...)instead.