<?php
// $Id$

/**
  * @file
  * Enable BOINC features related to processing work and credit.
  */


/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *
 * Includes that provide supporting functions
 *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */

require_once('includes/boincwork.forms.inc');
require_once('includes/boincwork.helpers.inc');
require_once('includes/boincwork.rules.inc');


/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *
 * Hooks into core modules
 *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */

/**
  * Implementation of hook_menu().
  */
function boincwork_menu() {
  require_boinc('util');
  $items['account/prefs'] = array(
    'title' => 'Computing',
    'description' => '',
    'page callback' => 'generalprefs_page',
    'access callback' => 'user_is_logged_in',
    'type' => MENU_NORMAL_ITEM
  );
  $items['account/prefs/computing'] = array(
    'title' => 'Computing',
    'page callback' => 'generalprefs_page',
    'access callback' => 'user_is_logged_in',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => 0
  );
  $items['account/prefs/project'] = array(
    'title' => 'Project',
    'page callback' => 'projectprefs_page',
    'access callback' => 'user_is_logged_in',
    'type' => MENU_LOCAL_TASK,
    'weight' => 5
  );
  $items['account/prefs/community'] = array(
    'title' => 'Community',
    'page callback' => 'communityprefs_page',
    'access callback' => 'user_is_logged_in',
    'type' => MENU_LOCAL_TASK,
    'weight' => 10
  );
  $items['account/prefs/privacy'] = array(
    'title' => 'Privacy',
    'page callback' => 'privacyprefs_page',
    'access callback' => 'user_is_logged_in',
    'type' => MENU_LOCAL_TASK,
    'weight' => 15
  );
  if (module_exists('ignore_user')) {
    $items['account/prefs/privacy/ignore_user/add'] = array(
      'title' => 'Add from ignore list',
      'description' => 'Add user that you with to ignore to your ignore list.',
      'page callback' => 'boincwork_ignore_user_add_user',
      'access callback' => 'user_access',
      'access arguments' => array('ignore user'),
      'type' => MENU_CALLBACK,
    );
    $items['account/prefs/privacy/ignore_user/remove'] = array(
      'title' => 'Remove from ignore list',
      'description' => 'Remove user from your ignore list.',
      'page callback' => 'boincwork_ignore_user_remove_user',
      'access callback' => 'user_access',
      'access arguments' => array('ignore user'),
      'type' => MENU_CALLBACK,
    );
  }// endif module_exists
  $items['account/certs'] = array(
    'title' =>'Account certificate',
    'page callback' => 'boincwork_certificates',
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK
  );
  $items['admin/boinc/prefs/general'] = array(
    'title' => 'Preferences: General',
    'description' => 'Set options for BOINC preference set pages',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('boincwork_admin_prefs_options_form'),
    'access arguments' => array('administer site configuration'),
    'type' => MENU_NORMAL_ITEM,
    'file' => 'boincwork.admin.inc'
  );
  $items['admin/boinc/prefs/presets'] = array(
    'title' => 'Preferences: Presets',
    'description' => 'Set values for BOINC preference set presets.',
    'page callback' => 'boincwork_admin_prefs_presets_page',
    'access arguments' => array('administer site configuration'),
    'type' => MENU_NORMAL_ITEM,
    'file' => 'boincwork.admin.inc'
  );
  $items['admin/boinc/prefs/upload'] = array(
    'title' => 'Preferences: Project-specific XML upload',
    'description' => 'Upload XML configuration for project specific preferences.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('boincwork_admin_prefs_upload_form'),
    'access arguments' => array('administer site configuration'),
    'type' => MENU_NORMAL_ITEM,
    'file' => 'boincwork.admin.inc'
  );
  $items['host/%/delete'] = array(
    'title' => 'Delete host',
    'page callback' => 'boincwork_host_delete',
    'page arguments' => array(1),
    'access callback' => 'user_is_logged_in',
    'type' => MENU_CALLBACK,
  );
  $items['host/%/log'] = array(
    'title' => 'Host log',
    'page callback' => 'boincwork_host_log',
    'page arguments' => array(1),
    'access callback' => 'user_is_logged_in',
    'type' => MENU_CALLBACK,
  );
  $items['host/%/merge'] = array(
    'title' => 'Merge computer',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('boincwork_host_merge_form', 1),
    'access callback' => 'user_is_logged_in',
    'type' => MENU_CALLBACK,
  );
  $items['host/%/set-venue/%'] = array(
    'title' => 'Set host venue',
    'page callback' => 'boincwork_host_set_venue',
    'page arguments' => array(1,3),
    'access callback' => 'user_is_logged_in',
    'type' => MENU_CALLBACK,
  );
  $items['user/%/mobile'] = array(
    'title' => 'Mobile stats',
    'page callback' => 'boincwork_mobile_stats',
    'page arguments' => array(1),
    'access callback' => 'user_is_logged_in',
    'type' => MENU_CALLBACK
  );
  $items['server_status.php'] = array(
    'title' => 'Server status',
    'page callback' => 'boincwork_server_status',
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK
  );
  $items['job_file.php'] = array(
    'title' => 'Job file input',
    'page callback' => 'boincwork_job_file',
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK
  );
  $items['get_output.php'] = array(
    'title' => 'Get output file',
    'page callback' => 'boincwork_get_output',
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK
  );
  $items['get_project_config.php'] = array(
    'title' => 'Project config',
    'page callback' => 'boincwork_get_project_config',
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK
  );
  $items['submit_rpc_handler.php'] = array(
    'title' => 'Remote job submission',
    'page callback' => 'boincwork_submit_rpc_handler',
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK
  );
  $items['userw.php'] = array(
    'title' => 'User WAP',
    'page callback' => 'boincwork_user_wap',
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK
  );
  $items['account/tasks/%/%'] = array(
    'title' => 'Account Tasks Table',
    'description' => '',
    'page callback' => 'boincwork_account_task_table',
    'page arguments' => array(2,3),
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK,
  );
  $items['host/%/tasks/%/%'] = array(
    'title' => 'Host Tasks Table',
    'description' => '',
    'page callback' => 'boincwork_host_task_table',
    'page arguments' => array(1,3,4),
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK,
  );
  // Workunit task table disabled
  //$items['workunit/%/tasks/%/%'] = array(
  //  'title' => 'Workunit Tasks Table',
  //  'description' => '',
  //  'page callback' => 'boincwork_workunit_task_table',
  //  'page arguments' => array(1,3,4),
  //  'access arguments' => array('access content'),
  //  'type' => MENU_CALLBACK,
  //);
  return $items;
}

/**
 * Implementation of hook_theme().
 */
function boincwork_theme() {
  return array(
    'boincwork_privacyprefs_form' => array(
      'arguments' => array('form'),
    ),
  );
}

/**
* Implementation of hook_views_api().
*/
function boincwork_views_api() {
  return array(
    'api' => 2.0,
    'path' => drupal_get_path('module', 'boincwork')
  );
}

/**
 * Implementation of hook_locale().
 */
function boincwork_locale($op = 'groups', $group = NULL) {
  switch ($op) {
    case 'groups':
      return array('project' => bts('Project'));
    case 'info':
      $info['project']['refresh callback'] = 'boincwork_locale_refresh';
      $info['project']['format'] = FALSE;
      return $info;
  }
}

/**
 * Refresh strings.
 */
function boincwork_locale_refresh() {
  // Mimic process of adding project specific prefs to the project preferences
  // form -- this parses the prefs XML and calls i18nstrings_update()
  $form = array();
  $prefs = array(
    'project_specific' => array(),
  );
  boincwork_add_project_specific_prefs($form, $prefs);
  return TRUE; // Meaning it completed with no issues
}


/**
 * Implementation of hook_privatemsg_message_view_alter
 */

function boincwork_privatemsg_message_view_alter(&$vars) {
  global $user;

  $author = $vars['message']['author'];
  if (!isset($vars['message']['thread_id'])) {
    // No thread id, this is probably only a preview
    return;
  }
  $thread_id = $vars['message']['thread_id'];

  if ($user->uid != $author->uid) {
    if ($vars['message']['is_blocked']) {
      $vars['message_actions']['unignore_user'] = array(
        'title' => bts('Stop Ignoring User', array(), NULL, 'boinc:ignore-user-remove'),
        'href' => 'account/prefs/privacy/ignore_user/remove/'. $author->uid,
        'query' => 'destination=messages/view/' . $thread_id,
      );
    }
    else {
      $vars['message_actions']['ignore_user'] = array(
        'title' => bts('Ignore User', array(), NULL, 'boinc:ignore-user-add'),
        'href' => 'account/prefs/privacy/ignore_user/add/'. $author->uid,
        'query' => 'destination=messages/view/' . $thread_id,
      );
    }
  }
}

/**
 * Implementation of hook_cron()
 */
function boincwork_cron() {
  // Delete expired hosts in the BOINC database, host_delete table.
  require_boinc('boinc_db');
  $num_deleted = BoincHostDeleted::delete_expired();
  if ($num_deleted>0) {
    watchdog('boincwork', "Deleted ${num_deleted} hosts from host_deleted table", WATCHDOG_NOTICE);
  }
}

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *
 * Page callbacks from hook_menu()
 *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */

/**
  * General preferences menu callback.
  * Called when user goes to edit preferences page
  */
function generalprefs_page($action = null, $venue = null, $advanced = FALSE) {

  // Keep the venue selected across preference pages
  boincwork_select_venue($venue);

  $pref_sets = array('generic', 'home', 'school', 'work');
  $output = null;
  // Set the page title
  $title = 'Computing';
  drupal_set_title($title);

  switch ($action) {

  case 'clear':
    // Remove settings from this preference set
    if ($venue AND $venue != 'generic') {
      boincwork_save_prefs(NULL, 'general', $venue);
      drupal_set_message(t('Settings for the "@name" preference set have been
        cleared', array('@name' => ucfirst($venue))));
      // Set the generic preference set as active
      $_SESSION['prefs venue'] = 'generic';
    }
    drupal_goto();
    break;

  case 'combined':
    // Compare preference sets; tabular view

    foreach ($pref_sets as $pref_set) {
      $form_state = array();
      $prefs[$pref_set] = drupal_retrieve_form('boincwork_generalprefs_form', $form_state, $pref_set);
      drupal_prepare_form('boincwork_generalprefs_form', $prefs[$pref_set], $form_state);
    }

    $output .= '<p>' . bts('These apply to all BOINC projects in which you participate.', array(), NULL, 'boinc:account-preferences-computing') . '<br/>';
    $output .= bts('On computers attached to multiple projects, the most recently modified preferences will be used.', array(), NULL, 'boinc:account-preferences-computing') . '</p>';
    $output .= '<p>' . bts('Preferences last modified: @mod_time', array('@mod_time' => pretty_time_str($prefs['generic']['prefs']['modified']['#value'])), NULL, 'boinc:account-preferences') . '</p>';
    $output .= '<h2>' . bts('Combined preferences', array(), NULL, 'boinc:account-preferences') . ' ' . l('(' . bts('Switch View', array(), NULL, 'boinc:account-preferences') . ')', 'account/prefs/computing') . '</h2>';

    $output .= '<table class="preferences combined">';

    $prefs_table = boincwork_make_prefs_table($prefs['generic']['prefs']['advanced']);

    foreach ($prefs_table as $category => $section) {
      $output .= '<tr class="section-heading">';
      $output .= "<td>{$section['name']}</td>";
      foreach ($pref_sets as $pref_set) {
        $output .= '<td>' . $pref_set . '</td>';
      }
      $output .= '</tr>';
      foreach ($section['elements'] as $name => $setting) {
        // Output the setting name and description, with an ugly exception
        // made for preferences with special formatting
        $special_map = array(
          'start_hour' => 'end_hour',
          'net_start_hour'=> 'net_end_hour',
          'daily_xfer_limit_mb' => 'daily_xfer_period_days',
        );
        $special_delimiter = array(
          'start_hour' => bts('and', array(), NULL, 'boinc:account-preference'),
          'net_start_hour'=> bts('and', array(), NULL, 'boinc:account-preference'),
          'daily_xfer_limit_mb' => bts('every', array(), NULL, 'boinc:account-preference'),
        );
        $special = isset($special_map[$name]);
        $very_special = in_array($name, $special_map);
        if ($very_special) {
          continue;
        }
        $output .= '<tr>';
        $output .= '<td>';
        $output .= "<div class=\"title\">{$setting['name']}</div>";
        $output .= "<div class=\"description\">{$setting['description']}</div>";
        $output .= '</td>';
        // Output values for each preference set, again with ugly hacks for
        // time range preferences
        foreach ($pref_sets as $pref_set) {
          if (($prefs[$pref_set]) AND
              $prefs[$pref_set]['#established'] AND
              isset($prefs[$pref_set]['prefs']['advanced'][$category])) {
            $pref_setting = $prefs[$pref_set]['prefs']['advanced'][$category][$name];
            $value = isset($pref_setting['#options']) ? $pref_setting['#options'][$pref_setting['#default_value']] : $pref_setting['#default_value'];
            if ($value == '') {
              $value = '---';
            }
            if (!isset($pref_setting['#field_suffix'])) {
              $pref_setting['#field_suffix'] = '';
            }
            if (!$special) {
              $output .= "<td>{$value} {$pref_setting['#field_suffix']}</td>";
            }
            else {
              // The "very special" case where we merge two prefs
              $second_pref = $special_map[$name];
              $second_pref_setting = $prefs[$pref_set]['prefs']['advanced'][$category][$second_pref];
              $second_value = isset($second_pref_setting['#options']) ? $second_pref_setting['#options'][$second_pref_setting['#default_value']] : $second_pref_setting['#default_value'];
              $output .= "<td>{$value} {$pref_setting['#field_suffix']} {$special_delimiter[$name]}" .
                " {$second_value} {$second_pref_setting['#field_suffix']} </td>";
            }
          }
          else  {
            $output .= '<td>---</td>';
          }
        }
        $output .= '</tr>';
      }
    }

    // Empty line above action links... :/
    $output .= '<tr>';
    $output .= '<td>&nbsp;</td>';
    $output .= '<td></td>';
    $output .= '<td></td>';
    $output .= '<td></td>';
    $output .= '<td></td>';
    $output .= '</td>';

    // Show Add / Edit links for each preference set
    $output .= '<tr>';
    $output .= '<td></td>';
    foreach ($pref_sets as $pref_set) {
      $action_text = ($prefs[$pref_set]['#established']) ? bts('Edit', array(), NULL, 'boinc:form-edit') : bts('Add', array(), NULL, 'boinc:form-add');
      $output .= '<td><ul class="tab-list"><li class="first tab">';
      $output .= l($action_text, "account/prefs/computing/edit/{$pref_set}/1",
        array('fragment' => "")
      );
      // Show Clear links for established preference sets
      if ($pref_set != 'generic' AND $prefs[$pref_set]['#established']) {
        $output .= ' </li><li class="tab"> ' . l(bts('Clear', array(), NULL, 'boinc:form-clear'), "account/prefs/computing/clear/{$pref_set}",
          array(
            'query' => drupal_get_destination(),
            'attributes' => array(
              'onclick' => 'return confirm(\'' . bts('This will remove all of your settings from the "@name" preference set. Are you sure?',
                array('@name' => ucfirst($pref_set)), NULL, 'boinc:account-computing-preferences') . '\')'
            )
          )
        );
      }
      $output .= '</li></ul></td>';
    }
    $output .= '</tr>';

    $output .= '</table>';

    break;

  case 'edit':
  default:

    // Return the HTML generated from the $form data structure.
    if (function_exists('jump_quickly')) {
      $path = 'account/prefs/computing/edit';
      $venues = array(
        "{$path}/generic" => bts('Generic', array(), NULL, 'boinc:account-preferences-location'),
        "{$path}/home" => bts('Home', array(), NULL, 'boinc:account-preferences-location:-1:ignoreoverwrite'),
        "{$path}/school" => bts('School', array(), NULL, 'boinc:account-preferences-location'),
        "{$path}/work" => bts('Work', array(), NULL, 'boinc:account-preferences-location')
      );
      variable_set('jump_use_js_venues-Array', 1);
      drupal_add_js(drupal_get_path('module', 'jump') . '/jump.js');
      drupal_add_js(drupal_get_path('theme', 'boinc') . '/js/prefs.js', 'theme');

      $output .= '<div id="venue-selector" class="simple-form-controls">';
      $output .= '  <div class="form-item venue">';
      $output .= '    <label>Preference set:</label>';
      $output .= jump_quickly($venues, 'venues', 1, "{$path}/{$venue}");
      $output .= '  </div>';
      $output .= '</div>';
    }
    $output .= drupal_get_form('boincwork_generalprefs_form', $venue, NULL, $advanced);

    // If viewing the edit page for a preference set that doesn't
    // exist, inform the user that preferences are not set.
    $form_state = array();
    $current_set = drupal_retrieve_form('boincwork_generalprefs_form', $form_state, $venue);
    drupal_prepare_form('boincwork_generalprefs_form', $current_set, $form_state);

    if (!$current_set['#established']) {
      drupal_set_message(bts(
          "No preferences found for set '@venue'. Click SAVE CHANGES below to save the following preferences to your account.",
          array( '@venue' => $venue, ),
          NULL, 'boinc:account-preferences'), 'status');
    }

    break;
  }

  return $output;
}

/**
  * Project preferences menu callback
  * Called when user goes to edit project preferences page.
  */
function projectprefs_page($action = null, $venue = null) {

  // Keep the venue selected across preference pages
  boincwork_select_venue($venue);

  require_boinc(array('util', 'prefs'));
  global $project_has_beta;
  $pref_sets = array('generic', 'home', 'school', 'work');
  $output = null;

  $title = 'Project';
  drupal_set_title($title);

  switch ($action) {

  case 'clear':
    // Remove settings from this preference set
    if ($venue AND $venue != 'generic') {
      boincwork_save_prefs(NULL, 'project', $venue);
      drupal_set_message(t('Settings for the "@name" preference set have been
        cleared', array('@name' => ucfirst($venue))));

      // Set the generic preference set as active
      $_SESSION['prefs venue'] = 'generic';

      // If the user has removed their default preference set, make it generic
      boincwork_set_default_venue();
    }
    drupal_goto();
    break;

  case 'combined':

    // Compare preference sets; tabular view

    global $user;
    $account = user_load($user->uid);
    $boincuser = BoincUser::lookup_id($account->boincuser_id);

    foreach ($pref_sets as $pref_set) {
      $form_state = array();
      $prefs[$pref_set] = drupal_retrieve_form('boincwork_projectprefs_form', $form_state, $pref_set);
      drupal_prepare_form('boincwork_projectprefs_form', $prefs[$pref_set], $form_state);
    }

    $output .= '<p>' . bts('Preferences last modified: @mod_time', array('@mod_time' => pretty_time_str($prefs['generic']['modified']['#value'])), NULL, 'boinc:account-preferences') . '</p>';
    $output .= '<h2>' . bts('Combined preferences', array(), NULL, 'boinc:account-preferences') . ' ' . l('(' . bts('Switch View', array(), NULL, 'boinc:account-preferences') . ')', 'account/prefs/project') . '</h2>';

    $output .= '<table class="preferences combined">';

    $prefs_table = boincwork_make_prefs_table($prefs['generic']);

    foreach ($prefs_table as $category => $section) {
      $output .= '<tr class="section-heading">';
      $output .= "<td>{$section['name']}</td>";
      foreach ($pref_sets as $pref_set) {
        $output .= '<td>' . $pref_set . '</td>';
      }
      $output .= '</tr>';
      foreach ($section['elements'] as $name => $setting) {
        $output .= '<tr>';
        $output .= '<td>';
        $output .= "<div class=\"title\">{$setting['name']}</div>";
        $output .= "<div class=\"description\">{$setting['description']}</div>";
        $output .= '</td>';
        foreach ($pref_sets as $pref_set) {
          if (($prefs[$pref_set]) AND
              $prefs[$pref_set]['#established'] AND
              isset($prefs[$pref_set][$category])) {
            $pref_setting = $prefs[$pref_set][$category][$name];
            $value = isset($pref_setting['#options']) ? $pref_setting['#options'][$pref_setting['#default_value']] : $pref_setting['#default_value'];
            if ($value == '') $value = '---';
            if (!isset($pref_setting['#field_suffix'])) $pref_setting['#field_suffix'] = '';
            $output .= "<td>{$value} {$pref_setting['#field_suffix']}</td>";
          } else $output .= '<td>---</td>';
        }
        $output .= '</tr>';
      }
    }

    // Empty line above action links... :/
    $output .= '<tr>';
    $output .= '<td>&nbsp;</td>';
    $output .= '<td></td>';
    $output .= '<td></td>';
    $output .= '<td></td>';
    $output .= '<td></td>';
    $output .= '</td>';

    // Show Add / Edit links for each preference set
    $output .= '<tr>';
    $output .= '<td></td>';
    foreach ($pref_sets as $pref_set) {
      $action_text = ($prefs[$pref_set]['#established']) ? bts('Edit', array(), NULL, 'boinc:form-edit') : bts('Add', array(), NULL, 'boinc:form-add');
      $output .= '<td><ul class="tab-list"><li class="first tab">';
      $output .= l($action_text, "account/prefs/project/edit/{$pref_set}");
      // Show Clear links for established preference sets
      if ($pref_set != 'generic' AND $prefs[$pref_set]['#established']) {
        $output .= ' </li><li class="tab"> ' . l(bts('Clear', array(), NULL, 'boinc:form-clear'), "account/prefs/project/clear/{$pref_set}",
          array(
            'query' => drupal_get_destination(),
            'attributes' => array(
              'onclick' => 'return confirm(\'' . bts('This will remove all of your settings from the "@name" preference set. Are you sure?',
                array('@name' => ucfirst($pref_set)), NULL, 'boinc:account-preferences-project') . '\')'
            )
          )
        );
      }
      $output .= '</li></ul></td>';
    }

    $output .= '</table>';

    break;

  case 'set-default':
    // Set this preference set as the one to use for any new hosts attached
    // to the user account
    boincwork_set_default_venue($venue);
    drupal_set_message( bts('The primary preference set has been changed to "@set"', array('@set' => $venue), NULL, 'boinc:account-preferences-project') );
    drupal_goto('account/prefs/project/combined');
    break;

  case 'edit':
  default:

    // Return the HTML generated from the $form data structure.
    require_boinc('util');

    if (function_exists('jump_quickly')) {
      $path = 'account/prefs/project/edit';
      $venues = array(
        "{$path}/generic" => bts('Generic', array(), NULL, 'boinc:account-preferences-location'),
        "{$path}/home" => bts('Home', array(), NULL, 'boinc:account-preferences-location:-1:ignoreoverwrite'),
        "{$path}/school" => bts('School', array(), NULL, 'boinc:account-preferences-location'),
        "{$path}/work" => bts('Work', array(), NULL, 'boinc:account-preferences-location')
      );
      variable_set('jump_use_js_venues-Array', 1);
      drupal_add_js(drupal_get_path('module', 'jump') . '/jump.js');
      drupal_add_js(drupal_get_path('theme', 'boinc') . '/js/prefs.js', 'theme');

      $output .= '<div id="venue-selector" class="simple-form-controls">';
      $output .= '  <div class="form-item venue">';
      $output .= '    <label>Preference set:</label>';
      $output .= jump_quickly($venues, 'venues', 1, "{$path}/{$venue}");
      $output .= '  </div>';
      $output .= '</div>';
    }
    $output .= drupal_get_form('boincwork_projectprefs_form', $venue);

    // If viewing the edit page for a preference set that doesn't
    // exist, inform the user that preferences are not set.
    $form_state = array();
    $current_set = drupal_retrieve_form('boincwork_projectprefs_form', $form_state, $venue);
    drupal_prepare_form('boincwork_projectprefs_form', $current_set, $form_state);

    if (!$current_set['#established']) {
      drupal_set_message(bts(
          "No preferences found for set '@venue'. Click SAVE CHANGES below to save the following preferences to your account.",
          array( '@venue' => $venue, ),
          NULL, 'boinc:account-preferences'), 'status');
    }

    break;

  }
  return $output;
}

/**
  * Community preferences menu callback
  * Called when user goes to edit community preferences page.
  */
function communityprefs_page($action = null) {

  require_boinc(array('util', 'prefs'));
  $output = null;

  $title = 'Community';
  drupal_set_title($title);

  //$output .= '<h2>Community preferences</h2>';

  $output .= drupal_get_form('communityprefs_form');

  return $output;
}

/**
  * Privacy preferences menu callback
  * Called when user goes to edit privacy preferences page.
  */
function privacyprefs_page($action = null) {

  require_boinc(array('util', 'prefs'));
  $output = null;
  $title = 'Privacy';
  drupal_set_title($title);

  switch ($action) {
  case 'view':
    $form_state = array();
    $prefs = drupal_retrieve_form('boincwork_privacyprefs_form', $form_state);
    drupal_prepare_form('boincwork_privacyprefs_form', $prefs, $form_state);
    $output .= '<table>';

    $sections = array(
      'privacy' => $prefs['privacy']
    );

    foreach ($sections as $section) {
      $output .= '<tr class="section-heading">';
      $output .= "<td>{$section['#title']}</td></tr>";
      foreach ($section as $name => $setting) {
        if ($name{0} == '#') continue;
        $value = isset($setting['#default_value']) ? $setting['#default_value'] : '';
        if ($value AND isset($setting['#options'])) $value = $setting['#options'][$value];
        elseif ($value == '') $value = '---';
        if (!isset($setting['#title'])) $setting['#title'] = '';
        if (!isset($setting['#description'])) $setting['#description'] = '';
        if (!isset($setting['#field_suffix'])) $setting['#field_suffix'] = '';
        $output .= '<tr>';
        $output .= "<td>{$setting['#title']}<br/>{$setting['#description']}</td>";
        $output .= "<td>{$value} {$setting['#field_suffix']}</td>";
        $output .= '</tr>';
      }
    }

    // Edit preferences link
    $output .= '<tr>';
    $output .= '<td></td>';
    $output .= '<td>' . l(bts('Edit privacy preferences', array('@project' => PROJECT), NULL, 'boinc:account-preferences-privacy'), "account/prefs/privacy/edit") . '</td>';
    $output .= '</tr>';

    $output .= '</table>';

    break;

  case 'edit':
  default:
    require_boinc('util');
    // Return the HTML generated from the $form data structure.
    $output .= drupal_get_form('boincwork_privacyprefs_form');
    break;

  }

  return $output;
}

/**
  * Certificates menu callback
  * Called when user goes to account certificate pages
  */
function boincwork_certificates($type = null, $border = null) {
  global $user;
  $drupuser = user_load($user->uid);
  // Load BOINC account and pre-authenticate with BOINC code
  require_boinc(array('util', 'cert'));
  $boincuser = BoincUser::lookup_id($drupuser->boincuser_id);
  //global $g_logged_in_user;
  //$g_logged_in_user = $boincuser;
  //print_r($boincuser); exit;
  switch ($type) {
  case 'all':
    //include_boinc('user/cert_all.php');
    require_boinc(array('util','cert','user'));

    $join = date('j F Y', $boincuser->create_time);
    $today = date('j F Y', time(0));

    if ($border=="no") {
        $border = 0;
    } else {
        $border=8;
    }

    $title_font = "\"Optima,ZapfChancery\"";
    $font = "\"Optima,Lucida Bright,Times New Roman\"";

    $boincuser = get_other_projects($boincuser);
    $total_credit = 0;
    foreach ($boincuser->projects as $p) {
        $total_credit += $p->total_credit;
    }

    $credit = credit_string($total_credit, false);

    function show_proj($p) {
        $join = date('j F Y', $p->create_time);
        echo "<tr> <td>$p->name</td><td> $p->total_credit</td><td>$join</td></tr>
        ";
    }

    echo "
        <table width=900 height=650 border=$border cellpadding=20><tr><td>
        <center>
        <table width=700 border=0><tr><td style=\"background-position:center; background-repeat:no-repeat\" background=http://boinc.berkeley.edu/logo/boinc_fade_600.png>
        <center>
        <font style=\"font-size: 52\" face=$title_font>Certificate of Computation

        <font face=$font style=\"font-size:28\">
        <br><br>
        This certifies that
        <p>
        <font face=$font style=\"font-size:32\">
        $boincuser->name

        <font face=$font style=\"font-size:18\">
        <p>
        has contributed $credit
        to the following scientific research projects:

        <center>
        <table width=80%>
        <tr><th align=left>Project</th><th align=left>Cobblestones</th><th align=left>Joined</th></tr>
    ";
    foreach ($boincuser->projects as $p) {
        if ($p->total_credit<100) continue;
        show_proj($p);
    }
    echo "
        </table>
        </center>
    ";

    echo "
        </td>
    ";
    echo "
    </td><tr></table>
    ";
    break;

  case 'account':
  default:
    //include_boinc('user/cert1.php');
    require_boinc(array('util','cert'));

    $join = date('j F Y', $boincuser->create_time);
    $today = date('j F Y', time(0));

    if ($border=="no") {
        $border = 0;
    } else {
        $border=8;
    }

    $credit = credit_string($boincuser->total_credit, false);

    $title_font = "\"Optima,ZapfChancery\"";
    $font = "\"Optima,Lucida Bright,Times New Roman\"";

    echo "
        <table width=900 height=650 border=$border cellpadding=20><tr><td>
        <center>
        <table width=700 border=0><tr><td>
        <center>
        <font style=\"font-size: 52\" face=$title_font>Certificate of Computation


        <font face=$font style=\"font-size:28\">
        <br><br><br>
        This certifies that
        <p>
        <font face=$font style=\"font-size:32\">
        $boincuser->name

        <font face=$font style=\"font-size:18\">
        <p>
        has participated in ".PROJECT." since $join,
        and has contributed $credit
        to ".PROJECT.".

        <br><br><br>
        </td><tr></table>
        <table width=100%><tr>
        <td width=40><br></td>
        <td align=left>
        <font face=$font style=\"font-size:16\">
    ";
    if (defined("CERT_SIGNATURE")) {
        echo "
            <img src=".CERT_SIGNATURE.">
            <br>
        ";
    }
    if (defined("CERT_DIRECTOR_NAME")) {
        echo CERT_DIRECTOR_NAME." <br>Director, ".PROJECT."
            <br>
        ";
    }
    echo "
        <br>
        $today
        </td>
    ";
    if (defined("CERT_PROJECT_LOGO")) {
        echo "
            <td align=center valign=center> <img src=".CERT_PROJECT_LOGO."> </td>
        ";
    }
    if (defined("CERT_INSTITUTION_LOGO")) {
        echo "
            <td align=center width=30% valign=center><img src=".CERT_INSTITUTION_LOGO."></td>
        ";
    }
    echo "
    </td><tr></table>
    ";
  }
}

/**
 * Delete host menu callback
 * Called when user selects to delete a host
 */
function boincwork_host_delete($host_id) {
  // Verify that host has no tasks
  if (boincwork_host_user_is_owner($host_id)) {
    if (!boincwork_host_get_task_count($host_id)) {
      // Delete the host record
      db_set_active('boinc_rw');
      $host_deleted = db_query(
        "DELETE FROM {host} WHERE id = '%d'",
        $host_id
      );
      db_set_active('default');
      if ($host_deleted) {
        drupal_set_message(t('Host @id has been removed from your account.',
          array('@id' => $host_id)));
        drupal_goto('account/computers');
      }
      else {
        drupal_set_message(t('Host @id could not be deleted. Not sure why...',
          array('@id' => $host_id)), 'error'
        );
      }
    }
    else {
      drupal_set_message(t('Host @id cannot be deleted because it still has
        tasks associated with it. These tasks should be processed within the
        next few days, after which the host can be deleted.',
        array('@id' => $host_id)), 'warning'
      );
    }
  }
  else {
    drupal_set_message(t('You are not the owner of host @id, so you cannot
      delete it.',
      array('@id' => $host_id)), 'error'
    );
  }
  drupal_goto("host/{$host_id}");
}

/**
 * Host log menu callback
 * Called when user accesses the log for a host
 */
function boincwork_host_log($host_id = null) {
  $root_log_dir = variable_get('boinc_host_sched_logs_dir', '');
  $log = '';
  if ($root_log_dir AND $host_id) {
    $subdir = substr($host_id, 0, -3) OR $subdir = 0;
    $log = implode('/', array($root_log_dir, $subdir, $host_id));
  }
  if ($log AND file_exists($log)) {
    header('Content-type: text/plain');
    include($log);
  }
}

function boincwork_host_set_venue($host_id = NULL, $venue = NULL) {
  global $user;
  $account = user_load($user->uid);
  db_set_active('boinc_ro');
  // Verify that this is my host
  $host_owner = db_result(db_query(
    "SELECT userid FROM {host} WHERE id = '%d'",
    $host_id
  ));
  db_set_active('default');
  if ($host_owner AND $host_owner == $account->boincuser_id) {
    db_set_active('boinc_rw');
    $updated = db_query(
      "UPDATE {host} SET venue = '%s' WHERE id = '%d'",
      $venue, $host_id
    );
    db_set_active('default');
    if ($updated) {
      drupal_set_message(
        bts('The location for this host has been updated.', array(), NULL, 'boinc:account-host-details')
        . bts('This will take effect next time the host contacts the project.', array(), NULL, 'boinc:account-host-details')
      );
    }
    else {
      drupal_set_message(
        bts('Unable to save changes to this host for some reason!', array(), NULL, 'boinc:account-host-details'),
        'error'
      );
    }
  }
  else {
    drupal_set_message(
      bts('You are not allowed to make changes to this host.', array(), NULL, 'boinc:account-host-details'),
      'warning'
    );
  }
  drupal_goto("host/{$host_id}");
}

/**
 * Mobile stats menu callback
 * Called when user accesses cell phone stats
 */
function boincwork_mobile_stats($userid = null) {
  $_GET['id'] = $userid;
  include_boinc('user/userw.php');
}

/**
 * Server status menu callback
 * Called to build the server status page
 */
function boincwork_server_status() {
  include_boinc('user/server_status.php');
}

/**
 * Page callback for the job input file RPC (job_file.php).
 * RPC for managing job input files
 */
function boincwork_job_file() {
  include_boinc('user/job_file.php');
}

/**
 * Page callback for the output file request RPC (get_output.php).
 * Get output file from remote job submission
 */
function boincwork_get_output() {
  include_boinc('user/get_output.php');
}

/**
 * Page callback for the project config RPC (get_project_config.php).
 * Get the project configuration XML; used by client software
 */
function boincwork_get_project_config() {
  ob_start();
  include_boinc('user/get_project_config.php');
  $xml = ob_get_clean();
  $xml = load_configuration($xml);

  // obtain Drupal variables
  $termsofuse = variable_get('boinc_weboptions_termsofuse', '');
  $overrideboinctou = variable_get('boinc_weboptions_overrideboinctou', FALSE);

  // If terms of use string exists and override is true, set terms-of-use
  // to Drupal varaible.
  if ( (!empty($termsofuse) && ($overrideboinctou)) ) {
    if (!empty($xml['project_config']['terms_of_use'])) {
      // Remove any existing terms of use
      unset($xml['project_config']['terms_of_use']);
    }

    // Add terms of use from Drupal
    $xml['project_config']['terms_of_use']['@value'] = $termsofuse;
  }

  print save_configuration($xml);
}


/**
 * Page callback for the remote job submission RPC (submit_rpc_handler.php).
 */
function boincwork_submit_rpc_handler() {
  include_boinc('user/submit_rpc_handler.php');
}

/**
 * Page callback for user WAP (userw.php).
 */
function boincwork_user_wap() {
  // Remove q from the GET request or BOINC will panic
  unset($_GET['q']);
  include_boinc('user/userw.php');
}


/**
 * Page callback for user account task table
 */
function boincwork_account_task_table($tselect = NULL, $app_id = NULL) {
  $title = bts('Tasks for your account', array(), NULL, 'boinc:account-task-table');
  drupal_set_title($title);

  global $user;
  $account = user_load($user->uid);
  return boincwork_tasktable(0, $account->boincuser_id, $tselect, $app_id);
}

/**
 * Page callback for host task table
 */
function boincwork_host_task_table($host_id = NULL, $tselect = NULL, $app_id = NULL) {
  require_boinc( array('util', 'result') );

  $title = bts('Tasks for computer @host_id', array('@host_id' => $host_id), NULL, 'boinc:host-task-table');
  drupal_set_title($title);

  if (is_null($host_id)) {
    drupal_set_message(bts('ERROR: Invalid host ID', array(), NULL, 'boinc:host-task-table'), 'error');
    return '';
  }

  return boincwork_tasktable(2, $host_id, $tselect, $app_id);
}

/**
 * Page callback for workunit task table
 */
function boincwork_workunit_task_table($workunit_id = NULL, $tselect = NULL, $app_id = NULL) {
  $title = bts('Tasks for workunit @workunit_id', array('@workunit_id' => $workunit_id), NULL, 'boinc:workunit-task-table');
  drupal_set_title($title);

  if (is_null($workunit_id)) {
    drupal_set_message(bts('ERROR: Invalid workunit ID', array(), NULL, 'boinc:workunit-task-table'), 'error');
    return '';
  }

  return boincwork_tasktable(1, $workunit_id, $tselect, $app_id);
}

/**
 * Generic page callback
 * Take a node ID and render that node as a page
 */
function boincwork_view_page($nid) {
  $node = node_load($nid);
  return node_page_view($node);
}


/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *
 * Local supporting functions
 *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */

/**
 * Determine which venue should be selected
 */
function boincwork_select_venue(&$venue) {
  if (!$venue) {
    $active_venue = isset($_SESSION['prefs venue']) ? $_SESSION['prefs venue'] : NULL;
    if ($active_venue) {
      $venue = $active_venue;
      //unset($_SESSION['prefs venue']);
    }
  }
  else {
    // Set the active venue to keep it selected between computing and project
    // preference pages
    $_SESSION['prefs venue'] = $venue;
  }
}
