Browse Source

Zombie users management: allow to disable old users #4652

Laurent Opprecht 13 years ago
parent
commit
da0df39794

+ 3 - 5
main/inc/lib/autoload.class.php

@@ -204,6 +204,7 @@ class Autoload
         $result['Header'] = '/main/inc/lib/header.class.php';
         $result['HotSpot'] = '/main/exercice/hotspot.class.php';
         $result['HotSpotDelineation'] = '/main/exercice/hotspot.class.php';
+        $result['Html'] = '/main/inc/lib/html.class.php';
         $result['Html_Quickform_Rule_Date'] = '/main/inc/lib/pear/HTML/QuickForm/Rule/Date.php';
         $result['Image'] = '/main/inc/lib/image.lib.php';
         $result['ImageWrapper'] = '/main/inc/lib/image.lib.php';
@@ -211,7 +212,6 @@ class Autoload
         $result['Import'] = '/main/inc/lib/import.lib.php';
         $result['IndexManager'] = '/main/inc/lib/userportal.lib.php';
         $result['IndexableChunk'] = '/main/inc/lib/search/IndexableChunk.class.php';
-        $result['ItemPropertyMissing'] = '/main/inc/lib/doctor/actions/item_property_missing.class.php';
         $result['Javascript'] = '/main/inc/lib/javascript.class.php';
         $result['KeyAuth'] = '/main/auth/key/key_auth.class.php';
         $result['LearnpathLink'] = '/main/gradebook/lib/be/learnpathlink.class.php';
@@ -363,10 +363,8 @@ class Autoload
         $result['UserTable'] = '/main/gradebook/lib/fe/usertable.class.php';
         $result['Wiki'] = '/main/coursecopy/classes/wiki.class.php';
         $result['XapianIndexer'] = '/main/inc/lib/search/xapian/XapianIndexer.class.php';
-        $result['ZombieManager'] = '/main/inc/lib/doctor/zombie/zombie_manager.class.php';
-        $result['ZombieQuery'] = '/main/inc/lib/doctor/zombie/zombie_query.class.php';
-        $result['ZombieReport'] = '/main/inc/lib/doctor/zombie/zombie_report.class.php';
-        $result['Zombies'] = '/main/inc/lib/doctor/zombie/zombies.class.php';
+        $result['ZombieManager'] = '/main/inc/lib/zombie/zombie_manager.class.php';
+        $result['ZombieReport'] = '/main/inc/lib/zombie/zombie_report.class.php';
         $result['_Admin'] = '/main/auth/shibboleth/app/model/scaffold/admin.class.php';
         $result['_AdminStore'] = '/main/auth/shibboleth/app/model/scaffold/admin.class.php';
         $result['_IndexableChunk'] = '/main/inc/lib/search/IndexableChunk.class.php';

+ 168 - 0
main/inc/lib/result_set.class.php

@@ -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;
+    }
+
+}

+ 94 - 0
main/inc/lib/zombie/zombie_manager.class.php

@@ -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);
+    }
+
+
+}

+ 323 - 0
main/inc/lib/zombie/zombie_report.class.php

@@ -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;
+        }
+    }
+
+}