/** delete a group after confirmation
  *
  * this either presents a confirmation dialog to the user OR deletes a group with
  * associated capacities and acls.
  *
  * Note that this routine could have been split into two routines, with the
  * first one displaying the confirmation dialog and the second one 'saving the changes'.
  * However, I think it is counter-intuitive to perform a deletion of data under
  * the name of 'saving'. So, I decided to use the same routine for both displaying
  * the dialog and acting on the dialog.
  *
  * @return void results are returned as output in $this->output
  * @todo since multiple tables are involved, shouldn't we use transaction/rollback/commit?
  *       Q: How well is MySQL suited for transactions? A: Mmmmm.... Which version? Which storage engine?
  */
 function group_delete()
 {
     global $WAS_SCRIPT_NAME, $DB, $USER;
     //
     // 0 -- sanity check
     //
     $group_id = get_parameter_int('group', NULL);
     if (is_null($group_id)) {
         logger(sprintf("%s.%s(): unspecified parameter group", __CLASS__, __FUNCTION__));
         $this->output->add_message(t('error_invalid_parameters', 'admin'));
         $this->groups_overview();
         return;
     }
     //
     // 1 -- bail out if the user pressed cancel button
     //
     if (isset($_POST['button_cancel'])) {
         $this->output->add_message(t('cancelled', 'admin'));
         $this->groups_overview();
         return;
     }
     // 2A -- do not allow the user to remove a group associated with the user
     $where = array('user_id' => intval($USER->user_id), 'group_id' => $group_id);
     if (($record = db_select_single_record('users_groups_capacities', 'capacity_code', $where)) !== FALSE) {
         // Oops, the current user happens to be a member of this group
         $params = $this->get_group_capacity_names($group_id, $record['capacity_code']);
         logger(sprintf("%s.%s(): user attempts to remove group '%s' ('%d', '%s') but she is a '%s'", __CLASS__, __FUNCTION__, $params['{GROUP}'], $group_id, $params['{GROUP_FULL_NAME}'], $params['{CAPACITY}']));
         $this->output->add_message(t('usermanager_delete_group_not_self', 'admin', $params));
         $this->groups_overview();
         return;
     }
     // 2B -- are there any files left in this user's private storage $CFG->datadir.'/groups/'.$path?
     if (($group = $this->get_group_record($group_id)) === FALSE) {
         $this->groups_overview();
         return;
     }
     $path = '/groups/' . $group['path'];
     if (!userdir_is_empty($path)) {
         // At this point we know there are still files associated with this
         // group in the data directory. This is a show stopper; it is up to the
         // admin requesting this delete to get rid of the files first (eg via File Manager)
         logger(sprintf("%s.%s(): data directory '%s' not empty", __CLASS__, __FUNCTION__, $path));
         $params = $this->get_group_capacity_names($group_id);
         $this->output->add_message(t('usermanager_delete_group_dir_not_empty', 'admin', $params));
         $this->groups_overview();
         return;
     }
     //
     // 3 -- user has confirmed delete?
     //
     if (isset($_POST['button_delete']) && isset($_POST['dialog']) && $_POST['dialog'] == GROUPMANAGER_DIALOG_DELETE) {
         $params = $this->get_group_capacity_names($group_id);
         // pick up name before it is gone
         if (userdir_delete($path) && $this->delete_group_capacities_records($group_id)) {
             $this->output->add_message(t('groupmanager_delete_group_success', 'admin', $params));
             $retval = TRUE;
         } else {
             $this->output->add_message(t('groupmanager_delete_group_failure', 'admin', $params));
             $retval = FALSE;
         }
         logger(sprintf("%s.%s(): %s deleting group '%d' %s (%s)", __CLASS__, __FUNCTION__, $retval === FALSE ? 'failure' : 'success', $group_id, $params['{GROUP}'], $params['{GROUP_FULL_NAME}']));
         $this->groups_overview();
         return;
     }
     //
     // 4 -- no delete yet, first show confirmation dialog
     //
     // Dialog is very simple: a simple text showing
     // - the name of the group
     // - the names of the associated capacities with number of users associated
     // - a Delete and a Cancel button
     //
     $dialogdef = array('dialog' => array('type' => F_INTEGER, 'name' => 'dialog', 'value' => GROUPMANAGER_DIALOG_DELETE, 'hidden' => TRUE), 'button_save' => dialog_buttondef(BUTTON_DELETE), 'button_cancel' => dialog_buttondef(BUTTON_CANCEL));
     $params = $this->get_group_capacity_names($group_id);
     $header = t('groupmanager_delete_group_header', 'admin', $params);
     $this->output->add_content('<h2>' . $header . '</h2>');
     $this->output->add_content(t('groupmanager_delete_group_explanation', 'admin'));
     $this->output->add_content('<ul>');
     $this->output->add_content('  <li class="level0">' . t('groupmanager_delete_group_group', 'admin', $params));
     $sql = sprintf("SELECT gc.capacity_code, COUNT(ugc.user_id) AS users " . "FROM %sgroups_capacities gc LEFT JOIN %susers_groups_capacities ugc " . "ON gc.group_id = ugc.group_id AND gc.capacity_code = ugc.capacity_code " . "WHERE gc.group_id = %d " . "GROUP BY gc.capacity_code " . "ORDER BY gc.sort_order", $DB->prefix, $DB->prefix, $group_id);
     if (($DBResult = $DB->query($sql)) !== FALSE) {
         $records = $DBResult->fetch_all_assoc();
         $DBResult->close();
         foreach ($records as $record) {
             $params['{CAPACITY}'] = capacity_name($record['capacity_code']);
             $params['{COUNT}'] = $record['users'];
             $line = t('groupmanager_delete_group_capacity', 'admin', $params);
             $this->output->add_content('  <li class="level0">' . $line);
         }
     }
     $this->output->add_content('</ul>');
     $this->output->add_content(t('delete_are_you_sure', 'admin'));
     $a_params = $this->a_params(TASK_GROUP_DELETE, $group_id);
     $href = href($WAS_SCRIPT_NAME, $a_params);
     $this->output->add_content(dialog_quickform($href, $dialogdef));
     $this->show_menu_group($group_id, TASK_GROUP_DELETE);
     $this->output->add_breadcrumb($WAS_SCRIPT_NAME, $a_params, array('title' => $header), t('groupmanager_delete_group_breadcrumb', 'admin'));
 }
 /** delete an area from ths site after confirmation
  *
  * this either presents a confirmation dialog to the user OR deletes an area.
  * First the user's permissions are checked and also there should be no nodes left
  * in the area before anything is done. Only allowing deletion of an empty area
  * is safety measure: we don't want to accidently delete many many nodes from
  * an area in one go (see also {@link task_node_delete()}). Also, we don't want
  * to introduce orphaned node records (by deleting the area record without deleting
  * nodes).
  *
  * Note that this routine could have been split into two routines, with the
  * first one displaying the confirmation dialog and the second one 'saving the changes'.
  * However, I think it is counter-intuitive to perform a deletion of data under
  * the name of 'saving'. So, I decided to use the same routine for both displaying
  * the dialog and acting on the dialog.
  *
  * @return void results are returned as output in $this->output
  * @todo since multiple tables are involved, shouldn't we use transaction/rollback/commit?
  *       Q: How well is MySQL suited for transactions? A: Mmmmm.... Which version? Which storage engine?
  * @uses $USER
  */
 function area_delete()
 {
     global $USER;
     // 0 -- sane area_id for deletion?
     $area_id = get_parameter_int('area', 0);
     $areas = get_area_records();
     if ($areas === FALSE || !isset($areas[$area_id])) {
         // are they trying to trick us, specifying an invalid area?
         logger("areamanager: weird: user tried to delete non-existing area '{$area_id}'");
         $this->output->add_message(t('invalid_area', 'admin', array('{AREA}' => strval($area_id))));
         $this->area_overview();
         return;
     }
     // 1A -- are we allowed to perform delete operation?
     if (!$USER->has_site_permissions(PERMISSION_SITE_DROP_AREA)) {
         logger("areamanager: user attempted to delete area '{$area_id}' without permission");
         $msg = t('icon_area_delete_access_denied', 'admin');
         $this->output->add_message($msg);
         $this->output->add_popup_bottom($msg);
         $this->area_overview();
         return;
     }
     // 1B -- bail out if the user pressed cancel button
     //
     if (isset($_POST['button_cancel'])) {
         $this->output->add_message(t('cancelled', 'admin'));
         $this->area_overview();
         return;
     }
     // 2 -- basic tests (showstoppers): any nodes or files left?
     $error_count = 0;
     // sentinel
     // 2A -- are there any nodes left in the area?
     $record = db_select_single_record('nodes', 'count(*) as nodes', array('area_id' => $area_id));
     if ($record === FALSE) {
         logger("areamanager: deletion of area '{$area_id}' failed: " . db_errormessage());
         $params = array('{AREA}' => $area_id, '{AREA_FULL_NAME}' => $areas[$area_id]['title']);
         $this->output->add_message(t('error_deleting_area', 'admin', $params));
         ++$error_count;
     } elseif ($record['nodes'] > 0) {
         $nodes = $record['nodes'];
         logger("areamanager: cannot delete area '{$area_id}' because {$nodes} nodes still exist in that area'");
         $params = array('{AREA}' => $area_id, '{AREA_FULL_NAME}' => $areas[$area_id]['title'], '{NODES}' => strval($nodes));
         $this->output->add_message(t('error_deleting_area_not_empty', 'admin', $params));
         ++$error_count;
     }
     // 2B -- are there any files left in this area
     $path = '/areas/' . $areas[$area_id]['path'];
     if (!userdir_is_empty($path)) {
         // At this point we know there are still files associated with this
         // area in the data directory. This is a show stopper; it is up to the
         // admin requesting this delete to get rid of the files first (eg via File Manager)
         logger(sprintf("%s.%s(): data directory '%s' not empty", __CLASS__, __FUNCTION__, $path));
         $params = array('{AREA}' => $area_id, '{AREA_FULL_NAME}' => $areas[$area_id]['title']);
         $this->output->add_message(t('error_deleting_area_dir_not_empty', 'admin', $params));
         ++$error_count;
     }
     if ($error_count > 0) {
         $this->area_overview();
         return;
     }
     // 3 -- actually perform the delete operation OR show the confirmation dialog
     if (isset($_POST['button_delete']) && isset($_POST['dialog']) && $_POST['dialog'] == AREAMANAGER_DIALOG_DELETE) {
         // stage 2 - actual delete confirmed
         $error_count = 0;
         if (!userdir_delete($path)) {
             ++$error_count;
         }
         // clean up a bunch of tables associated with this area in the correct order (to satisfy FK constraints)
         $where = array('area_id' => $area_id);
         $tables = array('themes_areas_properties', 'alerts_areas_nodes', 'acls_areas', 'acls_modules_areas', 'areas');
         // db_start_transaction();
         foreach ($tables as $table) {
             if (($rowcount = db_delete($table, $where)) === FALSE) {
                 logger(sprintf("%s.%s(): delete area '%d' from table '%s' failed: %s", __CLASS__, __FUNCTION__, $area_id, $table, db_errormessage()));
                 ++$error_count;
             } else {
                 logger(sprintf("%s.%s(): delete area '%d' from table '%s' succeeded: %d records deleted", __CLASS__, __FUNCTION__, $area_id, $table, $rowcount), WLOG_DEBUG);
             }
         }
         $params = array('{AREA}' => $area_id, '{AREA_FULL_NAME}' => $areas[$area_id]['title']);
         if ($error_count == 0) {
             // db_commit_transaction()
             logger(sprintf("%s.%s(): successfully deleted area '%d'", __CLASS__, __FUNCTION__, $area_id));
             $this->output->add_message(t('area_deleted', 'admin', $params));
         } else {
             // db_rollback_transaction();
             // break;
             $this->output->add_message(t('error_deleting_area', 'admin', $params));
         }
         $areas = get_area_records(TRUE);
         // force re-read of areas after deletion
         $this->area_overview();
     } else {
         // stage 1 - show confirmation dialog
         $this->show_dialog_confirm_delete($area_id, $areas);
     }
     return;
 }
 /** delete a user after confirmation
  *
  * after some basic tests this either presents a confirmation dialog to the user OR
  * deletes a user with associated acls and other records.
  *
  * Note that this routine could have been split into two routines, with the
  * first one displaying the confirmation dialog and the second one 'saving the changes'.
  * However, I think it is counter-intuitive to perform a deletion of data under
  * the name of 'saving'. So, I decided to use the same routine for both displaying
  * the dialog and acting on the dialog.
  *
  * Note that the (user)files should be removed before the account can be removed,
  * see {@link userdir_is_empty()}. It is up to the user or the admin to remove those files.
  *
  * A special test is performed to prevent users from killing their own account (which would
  * immediately kick them out of admin.php never to be seen again). 
  *
  * @return void results are returned as output in $this->output
  * @todo since multiple tables are involved, shouldn't we use transaction/rollback/commit?
  *       Q: How well is MySQL suited for transactions? A: Mmmmm.... Which version? Which storage engine?
  */
 function user_delete()
 {
     global $WAS_SCRIPT_NAME, $DB, $USER;
     //
     // 0 -- sanity check
     //
     $user_id = get_parameter_int('user', NULL);
     if (is_null($user_id)) {
         logger(sprintf("%s.%s(): unspecified parameter user", __CLASS__, __FUNCTION__));
         $this->output->add_message(t('error_invalid_parameters', 'admin'));
         $this->users_overview();
         return;
     }
     //
     // 1 -- bail out if the user pressed cancel button
     //
     if (isset($_POST['button_cancel'])) {
         $this->output->add_message(t('cancelled', 'admin'));
         $this->users_overview();
         return;
     }
     // 2A -- do not allow the user to commit suicide
     if (intval($USER->user_id) == intval($user_id)) {
         logger(sprintf("%s.%s(): user attempts to kill her own user account", __CLASS__, __FUNCTION__));
         $this->output->add_message(t('usermanager_delete_user_not_self', 'admin'));
         $this->users_overview();
         return;
     }
     // 2B -- are there any files left in this user's private storage $CFG->datadir.'/users/'.$path?
     if (($user = $this->get_user_record($user_id)) === FALSE) {
         $this->users_overview();
         return;
     }
     $path = '/users/' . $user['path'];
     if (!userdir_is_empty($path)) {
         // At this point we know there are still files associated with this
         // user in the data directory. This is a show stopper; it is up to the
         //  admin requesting this delete to get rid of the files first (eg via File Manager)
         logger(sprintf("%s.%s(): data directory '%s' not empty", __CLASS__, __FUNCTION__, $path));
         $params = $this->get_user_names($user_id);
         // pick up username
         $this->output->add_message(t('usermanager_delete_user_dir_not_empty', 'admin', $params));
         $this->users_overview();
         return;
     }
     //
     // 3 -- user has confirmed delete?
     //
     if (isset($_POST['button_delete']) && isset($_POST['dialog']) && intval($_POST['dialog']) == USERMANAGER_DIALOG_DELETE) {
         $params = $this->get_user_names($user_id);
         // pick up name before it is gone
         if (userdir_delete($path) && $this->delete_user_records($user_id)) {
             $this->output->add_message(t('usermanager_delete_user_success', 'admin', $params));
             $retval = TRUE;
         } else {
             $this->output->add_message(t('usermanager_delete_user_failure', 'admin', $params));
             $retval = FALSE;
         }
         logger(sprintf("%s.%s(): %s deleting user '%d' %s (%s)", __CLASS__, __FUNCTION__, $retval === FALSE ? 'failure' : 'success', $user_id, $params['{USERNAME}'], $params['{FULL_NAME}']));
         $this->users_overview();
         return;
     }
     //
     // 4 -- no delete yet, first show confirmation dialog
     //
     // Dialog is very simple: a simple text showing
     // - the name of the user
     // - a Delete and a Cancel button
     //
     $dialogdef = array(array('type' => F_INTEGER, 'name' => 'dialog', 'value' => USERMANAGER_DIALOG_DELETE, 'hidden' => TRUE), dialog_buttondef(BUTTON_DELETE), dialog_buttondef(BUTTON_CANCEL));
     $params = $this->get_user_names($user_id);
     $header = t('usermanager_delete_user_header', 'admin', $params);
     $this->output->add_content('<h2>' . $header . '</h2>');
     $this->output->add_content(t('usermanager_delete_user_explanation', 'admin'));
     $this->output->add_content('<ul>');
     $this->output->add_content('  <li class="level0">' . t('usermanager_delete_user_user', 'admin', $params));
     $this->output->add_content('</ul>');
     $this->output->add_content(t('delete_are_you_sure', 'admin'));
     $a_params = $this->a_params(TASK_USER_DELETE, $user_id);
     $href = href($WAS_SCRIPT_NAME, $a_params);
     $this->output->add_content(dialog_quickform($href, $dialogdef));
     $this->show_menu_user($user_id, TASK_USER_DELETE);
     $this->output->add_breadcrumb($WAS_SCRIPT_NAME, $a_params, array('title' => $header), t('usermanager_delete_user_breadcrumb', 'admin'));
 }