| Total Complexity | 50 |
| Total Lines | 350 |
| Duplicated Lines | 0 % |
| Changes | 0 | ||
Complex classes like admin_accesslog 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_accesslog, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 18 | class admin_accesslog |
||
| 19 | { |
||
| 20 | /** |
||
| 21 | * Which methods of this class can be called as menuation |
||
| 22 | * |
||
| 23 | * @var array |
||
| 24 | */ |
||
| 25 | public $public_functions = array( |
||
| 26 | 'index' => true, |
||
| 27 | 'sessions' => true, |
||
| 28 | ); |
||
| 29 | |||
| 30 | /** |
||
| 31 | * Our storage object |
||
| 32 | * |
||
| 33 | * @var Api\Storage\Base |
||
| 34 | */ |
||
| 35 | protected $so; |
||
| 36 | |||
| 37 | /** |
||
| 38 | * Name of our table |
||
| 39 | */ |
||
| 40 | const TABLE = 'egw_access_log'; |
||
| 41 | /** |
||
| 42 | * Name of app the table is registered |
||
| 43 | */ |
||
| 44 | const APP = 'phpgwapi'; |
||
| 45 | |||
| 46 | /** |
||
| 47 | * Constructor |
||
| 48 | * |
||
| 49 | */ |
||
| 50 | function __construct() |
||
| 51 | { |
||
| 52 | $this->so = new Api\Storage\Base(self::APP,self::TABLE,null,'',true); |
||
| 53 | $this->so->timestamps = array('li', 'lo', 'session_dla', 'notification_heartbeat'); |
||
| 54 | } |
||
| 55 | |||
| 56 | /** |
||
| 57 | * query rows for the nextmatch widget |
||
| 58 | * |
||
| 59 | * @param array $query with keys 'start', 'search', 'order', 'sort', 'col_filter' and |
||
| 60 | * 'session_list' true: all sessions, false: whole access-log, 'active': only sessions with session-status active (browser, no sync) |
||
| 61 | * @param array &$rows returned rows/competitions |
||
| 62 | * @param array &$readonlys eg. to disable buttons based on acl, not use here, maybe in a derived class |
||
| 63 | * @return int total number of rows |
||
| 64 | */ |
||
| 65 | function get_rows($query,&$rows,&$readonlys) |
||
| 66 | { |
||
| 67 | $heartbeat_limit = Api\Session::heartbeat_limit(); |
||
| 68 | |||
| 69 | if ($query['session_list']) // filter active sessions |
||
| 70 | { |
||
| 71 | $query['col_filter']['lo'] = null; // not logged out |
||
| 72 | $query['col_filter'][0] = 'session_dla > '.(int)(time() - $GLOBALS['egw_info']['server']['sessions_timeout']); |
||
| 73 | switch((string)$query['session_list']) |
||
| 74 | { |
||
| 75 | case 'active': // remove status != 'active', eg. CalDAV/eSync |
||
| 76 | $query['col_filter'][1] = "notification_heartbeat > $heartbeat_limit"; |
||
| 77 | $query['col_filter'][3] = "session_php NOT LIKE '% %'"; // remove blocked, bad login, etc |
||
| 78 | break; |
||
| 79 | default: |
||
| 80 | $query['col_filter'][1] = "(notification_heartbeat IS NULL OR notification_heartbeat > $heartbeat_limit)"; |
||
| 81 | break; |
||
| 82 | } |
||
| 83 | $query['col_filter'][2] = 'account_id>0'; |
||
| 84 | } |
||
| 85 | $total = $this->so->get_rows($query,$rows,$readonlys); |
||
| 86 | |||
| 87 | $heartbeat_limit_user = Api\DateTime::server2user($heartbeat_limit, 'ts'); |
||
| 88 | |||
| 89 | foreach($rows as &$row) |
||
| 90 | { |
||
| 91 | $row['sessionstatus'] = 'success'; |
||
| 92 | if ($row['notification_heartbeat'] > $heartbeat_limit_user) |
||
| 93 | { |
||
| 94 | $row['sessionstatus'] = 'active'; |
||
| 95 | } |
||
| 96 | if (stripos($row['session_php'],'blocked') !== false || |
||
| 97 | stripos($row['session_php'],'bad login') !== false || |
||
| 98 | strpos($row['session_php'],' ') !== false) |
||
| 99 | { |
||
| 100 | $row['sessionstatus'] = $row['session_php']; |
||
| 101 | } |
||
| 102 | if ($row['lo']) { |
||
| 103 | $row['total'] = ($row['lo'] - $row['li']) / 60; |
||
| 104 | $row['sessionstatus'] = 'logged out'; |
||
| 105 | } |
||
| 106 | // eg. for bad login or password |
||
| 107 | if (!$row['account_id']) $row['alt_loginid'] = ($row['loginid']?$row['loginid']:lang('none')); |
||
| 108 | |||
| 109 | // do not allow to kill or select own session |
||
| 110 | if ($GLOBALS['egw']->session->sessionid_access_log == $row['sessionid'] && $query['session_list']) |
||
| 111 | { |
||
| 112 | $row['class'] .= ' rowNoDelete '; |
||
| 113 | } |
||
| 114 | // do not allow to delete access log off active sessions |
||
| 115 | if (!$row['lo'] && $row['session_dla'] > time()-$GLOBALS['egw_info']['server']['sessions_timeout'] && |
||
| 116 | in_array($row['sessionstatus'], array('active', 'success')) && !$query['session_list']) |
||
| 117 | { |
||
| 118 | $row['class'] .= ' rowNoDelete '; |
||
| 119 | } |
||
| 120 | $row['sessionstatus'] = lang($row['sessionstatus']); |
||
| 121 | unset($row['session_php']); // for security reasons, do NOT give real PHP sessionid to UI |
||
| 122 | |||
| 123 | $row['os_browser'] = Api\Header\UserAgent::osBrowser($row['user_agent']); |
||
| 124 | } |
||
| 125 | if ($query['session_list']) |
||
| 126 | { |
||
| 127 | $rows['no_total'] = $rows['no_lo'] = true; |
||
| 128 | } |
||
| 129 | $GLOBALS['egw_info']['flags']['app_header'] = lang('Admin').' - '. |
||
| 130 | ($query['session_list'] ? lang('View sessions') : lang('View Access Log')). |
||
| 131 | ($query['col_filter']['account_id'] ? ': '.Api\Accounts::username($query['col_filter']['account_id']) : ''); |
||
| 132 | |||
| 133 | return $total; |
||
| 134 | } |
||
| 135 | |||
| 136 | /** |
||
| 137 | * Display the access log or session list |
||
| 138 | * |
||
| 139 | * @param array $content =null |
||
| 140 | * @param string $msg ='' |
||
| 141 | * @param boolean $sessions_list =false |
||
| 142 | */ |
||
| 143 | function index(array $content=null, $msg='', $sessions_list=false) |
||
| 144 | { |
||
| 145 | |||
| 146 | if (is_array($content)) $sessions_list = $content['nm']['session_list']; |
||
| 147 | |||
| 148 | // check if user has access to requested functionality |
||
| 149 | if ($GLOBALS['egw']->acl->check($sessions_list ? 'current_sessions' : 'access_log_acces',1,'admin')) |
||
| 150 | { |
||
| 151 | $GLOBALS['egw']->redirect_link('/index.php'); |
||
| 152 | } |
||
| 153 | |||
| 154 | if(!isset($content)) |
||
| 155 | { |
||
| 156 | $content['nm'] = array( |
||
| 157 | 'get_rows' => 'admin.admin_accesslog.get_rows', // I method/callback to request the data for the rows eg. 'notes.bo.get_rows' |
||
| 158 | 'no_filter' => True, // I disable the 1. filter |
||
| 159 | 'no_filter2' => True, // I disable the 2. filter (params are the same as for filter) |
||
| 160 | 'no_cat' => True, // I disable the cat-selectbox |
||
| 161 | 'header_left' => false, // I template to show left of the range-value, left-aligned (optional) |
||
| 162 | 'header_right' => false, // I template to show right of the range-value, right-aligned (optional) |
||
| 163 | 'never_hide' => True, // I never hide the nextmatch-line if less then maxmatch entries |
||
| 164 | 'lettersearch' => false, // I show a lettersearch |
||
| 165 | 'start' => 0, // IO position in list |
||
| 166 | 'order' => 'li', // IO name of the column to sort after (optional for the sortheaders) |
||
| 167 | 'sort' => 'DESC', // IO direction of the sort: 'ASC' or 'DESC' |
||
| 168 | 'default_cols' => '!session_action', // I columns to use if there's no user or default pref (! as first char uses all but the named columns), default all columns |
||
| 169 | 'csv_fields' => false, // I false=disable csv export, true or unset=enable it with auto-detected fieldnames, |
||
| 170 | //or array with name=>label or name=>array('label'=>label,'type'=>type) pairs (type is a eT widget-type) |
||
| 171 | 'actions' => $this->get_actions($sessions_list), |
||
| 172 | 'placeholder_actions' => false, |
||
| 173 | 'row_id' => 'sessionid', |
||
| 174 | ); |
||
| 175 | if ((int)$_GET['account_id']) |
||
| 176 | { |
||
| 177 | $content['nm']['col_filter']['account_id'] = (int)$_GET['account_id']; |
||
| 178 | } |
||
| 179 | if ($sessions_list) |
||
| 180 | { |
||
| 181 | $content['nm']['order'] = 'session_dla'; |
||
| 182 | $content['nm']['options-selectcols'] = array( |
||
| 183 | 'lo' => false, |
||
| 184 | 'total' => false, |
||
| 185 | ); |
||
| 186 | } |
||
| 187 | $content['nm']['session_list'] = $sessions_list; |
||
| 188 | } |
||
| 189 | //error_log(__METHOD__. ' accesslog =>' . array2string($content['nm']['selected'])); |
||
| 190 | if ($content['nm']['action']) |
||
| 191 | { |
||
| 192 | if ($content['nm']['select_all']) |
||
| 193 | { |
||
| 194 | // get the whole selection |
||
| 195 | $query = array( |
||
| 196 | 'search' => $content['nm']['search'], |
||
| 197 | 'col_filter' => $content['nm']['col_filter'] |
||
| 198 | ); |
||
| 199 | |||
| 200 | @set_time_limit(0); // switch off the execution time limit, as it's for big selections to small |
||
|
|
|||
| 201 | $query['num_rows'] = -1; // all |
||
| 202 | $all = $readonlys = array(); |
||
| 203 | $this->get_rows($query,$all,$readonlys); |
||
| 204 | $content['nm']['selected'] = array(); |
||
| 205 | foreach($all as $session) |
||
| 206 | { |
||
| 207 | $content['nm']['selected'][] = $session[$content['nm']['row_id']]; |
||
| 208 | } |
||
| 209 | } |
||
| 210 | if (!count($content['nm']['selected']) && !$content['nm']['select_all']) |
||
| 211 | { |
||
| 212 | $msg = lang('You need to select some entries first!'); |
||
| 213 | } |
||
| 214 | else |
||
| 215 | { |
||
| 216 | $success = $failed = $action = $action_msg = null; |
||
| 217 | if ($this->action($content['nm']['action'],$content['nm']['selected'] |
||
| 218 | ,$success,$failed,$action_msg,$msg)) |
||
| 219 | { // In case of action success |
||
| 220 | switch ($action_msg) |
||
| 221 | { |
||
| 222 | case'deleted': |
||
| 223 | $msg = lang('%1 log entries deleted.',$success); |
||
| 224 | break; |
||
| 225 | case'killed': |
||
| 226 | $msg = lang('%1 sessions killed',$success); |
||
| 227 | } |
||
| 228 | } |
||
| 229 | elseif($failed) // In case of action failiure |
||
| 230 | { |
||
| 231 | switch ($action_msg) |
||
| 232 | { |
||
| 233 | case'deleted': |
||
| 234 | $msg = lang('Error deleting log entry!'); |
||
| 235 | break; |
||
| 236 | case'killed': |
||
| 237 | $msg = lang('Permission denied!'); |
||
| 238 | } |
||
| 239 | } |
||
| 240 | } |
||
| 241 | } |
||
| 242 | |||
| 243 | $content['msg'] = $msg; |
||
| 244 | $content['percent'] = 100.0 * $GLOBALS['egw']->db->query( |
||
| 245 | 'SELECT ((SELECT COUNT(*) FROM '.self::TABLE.' WHERE lo != 0) / COUNT(*)) FROM '.self::TABLE, |
||
| 246 | __LINE__,__FILE__)->fetchColumn(); |
||
| 247 | |||
| 248 | $tmpl = new Etemplate('admin.accesslog'); |
||
| 249 | $tmpl->exec('admin.admin_accesslog.index', $content, array(), $readonlys, array( |
||
| 250 | 'nm' => $content['nm'], |
||
| 251 | )); |
||
| 252 | } |
||
| 253 | |||
| 254 | /** |
||
| 255 | * Apply an action to multiple logs |
||
| 256 | * |
||
| 257 | * @param type $action |
||
| 258 | * @param type $checked |
||
| 259 | * @param type $use_all |
||
| 260 | * @param type $success |
||
| 261 | * @param int $failed |
||
| 262 | * @param type $action_msg |
||
| 263 | * @return type number of failed |
||
| 264 | */ |
||
| 265 | function action($action,$checked,&$success,&$failed,&$action_msg) |
||
| 305 | } |
||
| 306 | |||
| 307 | /** |
||
| 308 | * Get actions / context menu for index |
||
| 309 | * |
||
| 310 | * Changes here, require to log out, as $content['nm'] get stored in session! |
||
| 311 | * |
||
| 312 | * @return array see nextmatch_widget::egw_actions() |
||
| 313 | */ |
||
| 314 | private static function get_actions($sessions_list) |
||
| 315 | { |
||
| 316 | $group = 0; |
||
| 317 | if ($sessions_list) |
||
| 318 | { |
||
| 319 | // error_log(__METHOD__. $sessions_list); |
||
| 320 | $actions= array( |
||
| 321 | 'kill' => array( |
||
| 322 | 'caption' => 'Kill', |
||
| 323 | 'confirm' => 'Kill this session', |
||
| 324 | 'confirm_multiple' => 'Kill these sessions', |
||
| 325 | 'group' => $group, |
||
| 326 | 'disableClass' => 'rowNoDelete', |
||
| 327 | ), |
||
| 328 | ); |
||
| 329 | |||
| 330 | } |
||
| 331 | else |
||
| 332 | { |
||
| 333 | $actions= array( |
||
| 334 | 'delete' => array( |
||
| 335 | 'caption' => 'Delete', |
||
| 336 | 'confirm' => 'Delete this entry', |
||
| 337 | 'confirm_multiple' => 'Delete these entries', |
||
| 338 | 'group' => $group, |
||
| 339 | 'disableClass' => 'rowNoDelete', |
||
| 340 | ), |
||
| 341 | ); |
||
| 342 | } |
||
| 343 | // Automatic select all doesn't work with only 1 action |
||
| 344 | $actions['select_all'] = array( |
||
| 345 | 'caption' => 'Select all', |
||
| 346 | //'checkbox' => true, |
||
| 347 | 'hint' => 'Select all entries', |
||
| 348 | 'enabled' => true, |
||
| 349 | 'shortcut' => array( |
||
| 350 | 'keyCode' => 65, // A |
||
| 351 | 'ctrl' => true, |
||
| 352 | 'caption' => lang('Ctrl').'+A' |
||
| 353 | ), |
||
| 354 | 'group' => $group++, |
||
| 355 | ); |
||
| 356 | return $actions; |
||
| 357 | } |
||
| 358 | |||
| 359 | /** |
||
| 360 | * Display session list |
||
| 361 | * |
||
| 362 | * @param array $content =null |
||
| 363 | * @param string $msg ='' |
||
| 364 | */ |
||
| 365 | function sessions(array $content=null, $msg='') |
||
| 368 | } |
||
| 369 | } |
||
| 370 |
If you suppress an error, we recommend checking for the error condition explicitly: