Zombie users management: allow to disable old users #4652
parent
34ea55889b
commit
da0df39794
@ -0,0 +1,168 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* ResultSet |
||||
* |
||||
* @license see /license.txt |
||||
* @author Laurent Opprecht <laurent@opprecht.info> for the Univesity of Geneva |
||||
*/ |
||||
class ResultSet implements Countable, Iterator |
||||
{ |
||||
|
||||
/** |
||||
* |
||||
* @param string $sql |
||||
* @return ResultSet |
||||
*/ |
||||
static function create($sql) |
||||
{ |
||||
return new self($sql); |
||||
} |
||||
|
||||
protected $sql = ''; |
||||
protected $handle = null; |
||||
protected $current = false; |
||||
protected $index = -1; |
||||
protected $count = false; |
||||
protected $limit_count = null; |
||||
protected $limit_offset = null; |
||||
protected $orderby_column = null; |
||||
protected $orderby_direction = null; |
||||
|
||||
function __construct($sql, $limit_count = null, $limit_offset = null, $orderby_column = null, $orderby_direction = null) |
||||
{ |
||||
$this->sql = $sql; |
||||
$this->limit_count = $limit_count; |
||||
$this->limit_offset = $limit_offset; |
||||
$this->orderby_column = $orderby_column; |
||||
$this->orderby_direction = $direction; |
||||
} |
||||
|
||||
public function sql() |
||||
{ |
||||
$sql = $this->sql; |
||||
|
||||
$column = $this->orderby_column; |
||||
$direction = $this->orderby_direction; |
||||
|
||||
$offset = $this->limit_offset; |
||||
$count = $this->limit_count; |
||||
if (is_null($column) && is_null($count) && is_null($offset)) { |
||||
return $sql; |
||||
} |
||||
|
||||
if (strpos($sql, ' ORDER ') || strpos($sql, ' LIMIT ') || strpos($sql, ' OFFSET ')) { |
||||
$sql = "SELECT * FROM ($sql) AS dat "; |
||||
}else |
||||
{ |
||||
$sql .= ' '; |
||||
} |
||||
|
||||
if ($column) { |
||||
$sql .= "ORDER BY $column $direction "; |
||||
} |
||||
|
||||
if ($count) { |
||||
$sql .= "LIMIT $count "; |
||||
} |
||||
if ($offset) { |
||||
$sql .= "OFFSET $offset"; |
||||
} |
||||
|
||||
return $sql; |
||||
} |
||||
|
||||
protected function handle() |
||||
{ |
||||
if (is_null($this->handle)) { |
||||
$this->handle = Database::query($this->sql()); |
||||
} |
||||
|
||||
return $this->handle; |
||||
} |
||||
|
||||
public function count() |
||||
{ |
||||
if ($this->count === false) { |
||||
$sql = $this->sql(); |
||||
$sql = "SELECT COUNT(*) AS alpha FROM ($sql) AS dat "; |
||||
$rs = Database :: query($sql); |
||||
$data = Database::fetch_array($rs); |
||||
$count = $data ? $data['alpha'] : 0; |
||||
$this->count = (int) $count; |
||||
} |
||||
return $this->count; |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @param int $count |
||||
* @param int $from |
||||
* @return ResultSet |
||||
*/ |
||||
public function limit($count, $from = 0) |
||||
{ |
||||
$result = clone($this); |
||||
$result->limit_offset = $from; |
||||
$result->limit_count = $count; |
||||
return $result; |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @param int $column |
||||
* @param int $dir |
||||
* @return ResultSet |
||||
*/ |
||||
public function orderby($column, $dir = 'ASC') |
||||
{ |
||||
$result = clone($this); |
||||
$result->orderby_column = $column; |
||||
$result->orderby_direction = $dir; |
||||
return $result; |
||||
} |
||||
|
||||
public function current() |
||||
{ |
||||
return $this->current; |
||||
} |
||||
|
||||
public function key() |
||||
{ |
||||
return $this->index; |
||||
} |
||||
|
||||
public function next() |
||||
{ |
||||
$this->current = Database::fetch_assoc($this->handle()); |
||||
$this->index++; |
||||
return $this->current; |
||||
} |
||||
|
||||
public function rewind() |
||||
{ |
||||
$this->handle = null; |
||||
$this->current = false; |
||||
$this->index = -1; |
||||
$this->next(); |
||||
} |
||||
|
||||
public function valid() |
||||
{ |
||||
return !empty($this->current); |
||||
} |
||||
|
||||
function __clone() |
||||
{ |
||||
$this->reset(); |
||||
} |
||||
|
||||
function reset() |
||||
{ |
||||
$this->handle = null; |
||||
$this->current = false; |
||||
$this->index = -1; |
||||
$this->count = false; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,94 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* ZombieQuery |
||||
* |
||||
* @copyright (c) 2012 University of Geneva |
||||
* @license GNU General Public License - http://www.gnu.org/copyleft/gpl.html |
||||
* @author Laurent Opprecht <laurent@opprecht.info> |
||||
*/ |
||||
class ZombieManager |
||||
{ |
||||
|
||||
static function last_year() |
||||
{ |
||||
$today = time(); |
||||
$day = date('j', $today); |
||||
$month = date('n', $today); |
||||
$year = date('Y', $today) - 1; |
||||
return mktime(0, 0, 0, $month, $day, $year); |
||||
} |
||||
|
||||
/** |
||||
* Returns users whose last login is prior from $ceiling |
||||
* |
||||
* @param int|string $ceiling last login date |
||||
* @param bool $active_only if true returns only active users. Otherwise returns all users. |
||||
* @return ResultSet |
||||
*/ |
||||
static function list_zombies($ceiling, $active_only = true) |
||||
{ |
||||
$ceiling = is_numeric($ceiling) ? (int) $ceiling : strtotime($ceiling); |
||||
$ceiling = date('Y-m-d H:i:s', $ceiling); |
||||
|
||||
$user_table = Database::get_main_table(TABLE_MAIN_USER); |
||||
$login_table = Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_LOGIN); |
||||
|
||||
$sql = 'SELECT |
||||
user.user_id, |
||||
user.firstname, |
||||
user.lastname, |
||||
user.username, |
||||
user.auth_source, |
||||
user.email, |
||||
user.status, |
||||
user.registration_date, |
||||
user.active, |
||||
access.login_date'; |
||||
|
||||
global $_configuration; |
||||
if ($_configuration['multiple_access_urls']) { |
||||
|
||||
$access_url_rel_user_table = Database :: get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER); |
||||
$current_url_id = api_get_current_access_url_id(); |
||||
|
||||
$sql .= " FROM $user_table as user, $login_table as access, $access_url_rel_user_table as url |
||||
WHERE |
||||
access.login_date = (SELECT MAX(a.login_date) |
||||
FROM $login_table as a |
||||
WHERE a.login_user_id = user.user_id |
||||
) AND |
||||
access.login_date <= '$ceiling' AND |
||||
user.user_id = access.login_user_id AND |
||||
url.login_user_id = user.user_id AND url.access_url_id=$current_url_id"; |
||||
} else { |
||||
$sql .= " FROM $user_table as user, $login_table as access |
||||
WHERE |
||||
access.login_date = (SELECT MAX(a.login_date) |
||||
FROM $login_table as a |
||||
WHERE a.login_user_id = user.user_id |
||||
) AND |
||||
access.login_date <= '$ceiling' AND |
||||
user.user_id = access.login_user_id"; |
||||
} |
||||
if($active_only) |
||||
{ |
||||
$sql .= ' AND user.active = 1'; |
||||
} |
||||
|
||||
return ResultSet::create($sql); |
||||
} |
||||
|
||||
static function deactivate_zombies($ceiling) |
||||
{ |
||||
$zombies = self::list_zombies($ceiling); |
||||
$ids = array(); |
||||
foreach($zombies as $zombie) |
||||
{ |
||||
$ids[] = $zombie['user_id']; |
||||
} |
||||
UserManager::deactivate_users($ids); |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,323 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* Description of zombie_report |
||||
* |
||||
* @copyright (c) 2012 University of Geneva |
||||
* @license GNU General Public License - http://www.gnu.org/copyleft/gpl.html |
||||
* @author Laurent Opprecht <laurent@opprecht.info> |
||||
*/ |
||||
class ZombieReport implements Countable |
||||
{ |
||||
/** |
||||
* @return ZombieReport |
||||
*/ |
||||
static function create($additional_parameters = array()) |
||||
{ |
||||
return new self($additional_parameters); |
||||
} |
||||
|
||||
protected $additional_parameters = array(); |
||||
|
||||
function __construct($additional_parameters = array()) |
||||
{ |
||||
$this->additional_parameters = $additional_parameters; |
||||
} |
||||
|
||||
function get_additional_parameters() |
||||
{ |
||||
return $this->additional_parameters; |
||||
} |
||||
|
||||
function get_parameters() |
||||
{ |
||||
|
||||
$result = array( |
||||
'name' => 'zombie_report_parameters', |
||||
'method' => 'GET', |
||||
'attributes' => array('class' => 'well form-horizontal form-search'), |
||||
'items' => array( |
||||
array( |
||||
'name' => 'ceiling', |
||||
'label' => get_lang('LastAccess'), |
||||
'type' => 'datepickerdate', |
||||
'default' => $this->get_ceiling(), |
||||
'rules' => array( |
||||
// array( |
||||
// 'type' => 'required', |
||||
// 'message' => get_lang('Required') |
||||
// ), |
||||
array( |
||||
'type' => 'date', |
||||
'message' => get_lang('Date') |
||||
) |
||||
) |
||||
), |
||||
array( |
||||
'name' => 'active_only', |
||||
'label' => get_lang('ActiveOnly'), |
||||
'type' => 'checkbox', |
||||
'default' => $this->get_active_only() |
||||
), |
||||
array( |
||||
'name' => 'submit_button', |
||||
'type' => 'style_submit_button', |
||||
'value' => get_lang('Search'), |
||||
'attributes' => array('class' => 'search') |
||||
) |
||||
) |
||||
); |
||||
$additional_parameters = $this->get_additional_parameters(); |
||||
foreach ($additional_parameters as $key => $value) { |
||||
$result['items'][] = array( |
||||
'type' => 'hidden', |
||||
'name' => $key, |
||||
'value' => $value |
||||
); |
||||
} |
||||
return $result; |
||||
} |
||||
|
||||
protected $parameters_form = null; |
||||
|
||||
/** |
||||
* |
||||
* @return FormValidator |
||||
*/ |
||||
function get_parameters_form() |
||||
{ |
||||
$parameters = $this->get_parameters(); |
||||
if (empty($parameters)) { |
||||
return null; |
||||
} |
||||
if (empty($this->parameters_form)) { |
||||
$this->parameters_form = FormValidator::create($parameters); |
||||
} |
||||
|
||||
return $this->parameters_form; |
||||
} |
||||
|
||||
function display_parameters($return = false) |
||||
{ |
||||
$form = $this->get_parameters_form(); |
||||
if (empty($form)) { |
||||
return ''; |
||||
} |
||||
|
||||
$result = $form->return_form(); |
||||
if ($return) { |
||||
return $result; |
||||
} else { |
||||
echo $result; |
||||
} |
||||
} |
||||
|
||||
function is_valid() |
||||
{ |
||||
$form = $this->get_parameters_form(); |
||||
if (empty($form)) { |
||||
return true; |
||||
} |
||||
return $form->isSubmitted() == false || $form->validate(); |
||||
} |
||||
|
||||
function get_ceiling() |
||||
{ |
||||
$result = Request::get('ceiling'); |
||||
$result = $result ? $result : ZombieManager::last_year(); |
||||
|
||||
$result = is_array($result) && count($result) == 1 ? reset($result) : $result; |
||||
$result = is_array($result) ? mktime(0, 0, 0, $result['F'], $result['d'], $result['Y']) : $result; |
||||
$result = is_numeric($result) ? (int) $result : $result; |
||||
$result = is_string($result) ? strtotime($result) : $result; |
||||
return $result; |
||||
} |
||||
|
||||
function get_active_only() |
||||
{ |
||||
$result = Request::get('active_only', false); |
||||
$result = $result === 'true' ? true : $result; |
||||
$result = $result === 'false' ? false : $result; |
||||
$result = (bool) $result; |
||||
return $result; |
||||
} |
||||
|
||||
function get_action() |
||||
{ |
||||
|
||||
/** |
||||
* todo check token |
||||
*/ |
||||
$check = Security::check_token('post'); |
||||
Security::clear_token(); |
||||
if (!$check) { |
||||
return 'display'; |
||||
} |
||||
return Request::post('action', 'display'); |
||||
} |
||||
|
||||
function perform_action() |
||||
{ |
||||
$ids = Request::post('id'); |
||||
if (empty($ids)) { |
||||
return $ids; |
||||
} |
||||
|
||||
$action = $this->get_action(); |
||||
$f = array($this, 'action_' . $action); |
||||
if (is_callable($f)) { |
||||
return call_user_func($f, $ids); |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
function action_deactivate($ids) |
||||
{ |
||||
return UserManager::deactivate_users($ids); |
||||
} |
||||
|
||||
function action_activate($ids) |
||||
{ |
||||
return UserManager::activate_users($ids); |
||||
} |
||||
|
||||
function action_delete($ids) |
||||
{ |
||||
return UserManager::delete_users($ids); |
||||
} |
||||
|
||||
function count() |
||||
{ |
||||
if (!$this->is_valid()) { |
||||
return 0; |
||||
} |
||||
|
||||
$ceiling = $this->get_ceiling(); |
||||
$active_only = $this->get_active_only(); |
||||
$items = ZombieManager::list_zombies($ceiling, $active_only); |
||||
return count($items); |
||||
} |
||||
|
||||
function get_data($from, $count, $column, $direction) |
||||
{ |
||||
if (!$this->is_valid()) { |
||||
return array(); |
||||
} |
||||
|
||||
$ceiling = $this->get_ceiling(); |
||||
$active_only = $this->get_active_only(); |
||||
$items = ZombieManager::list_zombies($ceiling, $active_only)->limit($count, $from)->orderby($column, $direction); |
||||
$result = array(); |
||||
foreach ($items as $item) { |
||||
$row = array(); |
||||
$row[] = $item['user_id']; |
||||
$row[] = $item['code']; |
||||
$row[] = $item['firstname']; |
||||
$row[] = $item['lastname']; |
||||
$row[] = $item['username']; |
||||
$row[] = $item['email']; |
||||
$row[] = $item['status']; |
||||
$row[] = $item['auth_source']; |
||||
$row[] = api_format_date($item['registration_date'], DATE_FORMAT_SHORT); |
||||
$row[] = api_format_date($item['login_date'], DATE_FORMAT_SHORT); |
||||
$row[] = $item['active']; |
||||
$result[] = $row; |
||||
} |
||||
return $result; |
||||
} |
||||
|
||||
function display_data($return = false) |
||||
{ |
||||
|
||||
$count = array($this, 'count'); |
||||
$data = array($this, 'get_data'); |
||||
|
||||
$parameters = array(); |
||||
$parameters['sec_token'] = Security::get_token(); |
||||
$parameters['ceiling'] = $this->get_ceiling(); |
||||
$parameters['active_only'] = $this->get_active_only() ? 'true' : 'false'; |
||||
$additional_parameters = $this->get_additional_parameters(); |
||||
$parameters = array_merge($additional_parameters, $parameters); |
||||
|
||||
$table = new SortableTable('users', $count, $data, 1, 50); |
||||
$table->set_additional_parameters($parameters); |
||||
|
||||
$col = 0; |
||||
$table->set_header($col++, '', false); |
||||
$table->set_header($col++, get_lang('Code')); |
||||
$table->set_header($col++, get_lang('FirstName')); |
||||
$table->set_header($col++, get_lang('LastName')); |
||||
$table->set_header($col++, get_lang('LoginName')); |
||||
$table->set_header($col++, get_lang('Email')); |
||||
$table->set_header($col++, get_lang('Profile')); |
||||
$table->set_header($col++, get_lang('Authentication')); |
||||
$table->set_header($col++, get_lang('Registered')); |
||||
$table->set_header($col++, get_lang('LastAccess'), false); |
||||
$table->set_header($col++, get_lang('Active'), false); |
||||
|
||||
$table->set_column_filter(5, array($this, 'format_email')); |
||||
$table->set_column_filter(6, array($this, 'format_status')); |
||||
$table->set_column_filter(10, array($this, 'format_active')); |
||||
|
||||
$table->set_form_actions(array( |
||||
'activate' => get_lang('Activate'), |
||||
'deactivate' => get_lang('Deactivate'), |
||||
'delete' => get_lang('Delete') |
||||
)); |
||||
|
||||
if ($return) { |
||||
return $table->return_table(); |
||||
} else { |
||||
echo $table->return_table(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Table formatter for the active column. |
||||
* |
||||
* @param string $active |
||||
* @return string |
||||
*/ |
||||
function format_active($active) |
||||
{ |
||||
$active = ($active == '1'); |
||||
if ($active) { |
||||
$image = 'accept'; |
||||
$text = get_lang('Yes'); |
||||
} else { |
||||
$image = 'error'; |
||||
$text = get_lang('No'); |
||||
} |
||||
|
||||
$result = Display::return_icon($image . '.png', $text); |
||||
return $result; |
||||
} |
||||
|
||||
function format_status($status) |
||||
{ |
||||
$statusname = api_get_status_langvars(); |
||||
return $statusname[$status]; |
||||
} |
||||
|
||||
function format_email($email) |
||||
{ |
||||
return Display :: encrypted_mailto_link($email, $email); |
||||
} |
||||
|
||||
function display($return = false) |
||||
{ |
||||
$result = $this->display_parameters($return); |
||||
if ($this->perform_action()) { |
||||
if ($return) { |
||||
$result = Display::return_confirmation_message(get_lang('Done')); |
||||
} else { |
||||
Display::display_confirmation_message(get_lang('Done')); |
||||
} |
||||
} |
||||
$result = $this->display_data($return); |
||||
if ($return) { |
||||
return $result; |
||||
} |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue