<?php

/**
 * @file
 * This module adds Site Disclaimer to the registration page.
 *
 */

/**
 * @todo: cancel/logout button on the "accept new terms of use" page
 * @todo: remove breadcrumbs on the "accept new terms of use" page
 *
 */

define('SITE_DISCLAIMER_SETTINGS_PATH', 'admin/config/people/site_disclaimer');
define('SITE_DISCLAIMER_ACCEPT_PATH', '_legal/accept_new');

define('SITE_DISCLAIMER_DEFAULT_TITLE', t('Terms of Use'));
define('SITE_DISCLAIMER_DEFAULT_CHECKBOX_LABEL', t('I agree with these terms'));


/**
 * Implements hook_help().
 */
function site_disclaimer_help($path, $arg) {
  switch ($path) {
    case 'admin/help#site_disclaimer':
      return '<p>' . t('Site Disclaimer form (which includes a Site Disclaimer statement and an [x] checkbox) is displayed on the registration page, and the visitor is required to accept it by checking the [x] checkbox in order to register. If Site Disclaimer statement is revised later, Site Disclaimer form will be displayed after registered user logs in and the user will be required to accept the new version of the terms by checking the [x] checkbox in order to proceed using the website.') . '</p>';
      break;
    case SITE_DISCLAIMER_SETTINGS_PATH:
      return '<p>' . t('Site Disclaimer form (which includes a Site Disclaimer statement and an [x] checkbox) is displayed on the registration page, and the visitor is required to accept it by checking the [x] checkbox in order to register. If Site Disclaimer statement is revised later, Site Disclaimer form will be displayed after registered user logs in and the user will be required to accept the new version of the terms by checking the [x] checkbox in order to proceed using the website.') . '</p>';
      break;
  }
}

/**
 * Implements hook_menu().
 */
function site_disclaimer_menu() {
  $items = array();

  $items[SITE_DISCLAIMER_SETTINGS_PATH] = array(
    'description' => 'Add Site Disclaimer to the registration page.',
    'title' => 'Site Disclaimer',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('site_disclaimer_admin_settings'),
    'access arguments' => array('administer site configuration'),
    'file'             => 'site_disclaimer.admin.inc',
  );

  $items['site_disclaimer/autocomplete'] = array(
    'title' => 'Autocomplete node title',
    'page callback' => 'site_disclaimer_autocomplete',
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK,
    'file' => 'site_disclaimer.admin.inc',
  );

  $items[SITE_DISCLAIMER_ACCEPT_PATH] = array(
    'page callback' => 'drupal_get_form',
    'page arguments' => array('site_disclaimer_confirm'),
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
    'file' => 'site_disclaimer.pages.inc',
  );

  return $items;
}

/**
 * Get selected translation of the variable.
 */
function _site_disclaimer_variable_get_translated($name, $default = '', $langcode = NULL) {
  if (!empty($langcode) && module_exists('i18n_variable')) {
    return  i18n_variable_get($name, $langcode, $default);
  }
  else {
    return variable_get($name, $default);
  }
}

/**
 * Get translation of a node for the current language.
 */
function _site_disclaimer_nid_translated($nid, $langcode = NULL) {
  if ($nid != '' && module_exists('translation')) {
    if (empty($langcode)) {
      global $language;
      $langcode = $language->language;
    }
    // translation_node_get_translations() returns empty set for $nid, but works ok ith $node->tnid. Go figure.
    $node = node_load($nid);
    $translations = translation_node_get_translations($node->tnid);
    if (!empty($translations[$langcode])) {
      $nid = $translations[$langcode]->nid;
    }
  }
  return $nid;
}

/**
 * Get an associative array of all node translations (keyed by language code)
 */
function _site_disclaimer_nid_all_translations($nid) {
  global $language;
  $nids = empty($nid) ? array() : array($language->language => $nid);
  if (!empty($nid) && module_exists('translation')) {
    $translations = translation_node_get_translations($nid);
    foreach ($translations as $langcode => $translation) {
      $nids[$langcode] = $translation->nid;
    }
  }
  return $nids;
}

/**
 * Go through the $checkbox_label string, replacing each @"Terms Link" with an actual node link
 *
 * @param $checkbox_label
 *  The label from the admin form saved in the DB.
 * @param $linked_nodes
 *  (Optional) return an array of all linked node nids.
 * @param $linked_errors
 *  (Optional) return an array of all linked node errors.
 * @return
 *  Substituted text output.
 */
function _site_disclaimer_checkbox_label_substitute_links($checkbox_label, &$linked_nodes = NULL, &$linked_errors = NULL) {
  $linked_nodes = array();
  $linked_errors = array();
  // Go through the $output string, replacing @"Terms Link" with an actual node link
  $output = str_replace('&quot;', '"', $checkbox_label);
  $i = 0;
  while ( $i < drupal_strlen($output) ) {
    if (drupal_substr($output, $i, 2) == '@"') {
      $start = $i + 2;
      $end = strpos($output, '"', $start);
      while (drupal_substr($output, $end - 1, 1) == "\\") {
        $end = strpos($output, '"', $end + 1);
      }
      $title = drupal_substr($output, $start, ($end - $start));
      $nodes = node_load_multiple(array(), array('title' => str_replace('\\"', '"', $title) ) );
      if (empty($nodes)) {
        $linked_errors[$title] = t('No post was found with "!title" title.', array('!title' => $title));
      }
      else {
        $node = array_shift($nodes);
        $linked_nodes[] = $node->nid;
        $link = l($node->title, 'node/' . $node->nid);
        $output = str_replace('@"' . $title . '"', $link, $output);
        $i += drupal_strlen($link) - 1;
      }
    }
    $i++;
  }

  return $output;
}

/**
 * Implements hook_form_form_id_alter().
 */
function site_disclaimer_form_user_register_form_alter(&$form, $form_state, $form_id = NULL, $langcode = NULL, $preview = FALSE) {
  // Administrative users can skip this. So admin/user/user/create won't show the Site Disclaimer.
  if (user_access('administer users') && !$preview) {
    return;
  }

  $height = variable_get('site_disclaimer_node_height', '');
  if ($height) {
    $css = '#site-disclaimer { height:' . check_plain($height) . '; overflow:auto; }';
    // Revert the styling so print shows the full text.
    $css2 = '#site-disclaimer { height:auto; overflow:visible; }';
    $form['#attached']['css'] = array(
      $css => array('type' => 'inline'),
      $css2 => array('type' => 'inline', 'media' => 'print'),
    );
//    drupal_add_css('' . $css . '', array('type' => 'inline'));
//    drupal_add_html_head('<style type="text/css" media="print">' . $css2 . '</style>', $key = NULL /* TODO Set this variable. */);
  }

  // Getting the node that contains the Site Disclaimer.
  $nid = _site_disclaimer_nid_translated(variable_get('site_disclaimer_node_id', ''), $langcode);
  $node = NULL;
  if ($nid != '') {
    $node = node_load($nid);
    if (!$node->nid) {
      watchdog('Site Disclaimer', 'The Site Disclaimer node !nid could not be loaded. Please check the settings and the node.', array('!nid' => $nid), WATCHDOG_ALERT, l(t('Administer Site Disclaimer'), SITE_DISCLAIMER_SETTINGS_PATH));
    }
  }

  // Will we show the full text of the Terms or only provide a link to the Terms.
  // Reading the checkbox label to find out.
  $checkbox_label = filter_xss_admin(_site_disclaimer_variable_get_translated('site_disclaimer_checkbox_label', SITE_DISCLAIMER_DEFAULT_CHECKBOX_LABEL, $langcode));
  $use_link = (strpos($checkbox_label, '@link') !== FALSE);
  $use_named_links = (strpos($checkbox_label, '@&quot;') !== FALSE);
  $show_node = $node && !$use_link && !$use_named_links;

  // If there is no node to show and no links in the $checkbox_label, we don't show the terms form.
  if (!$use_link && !$use_named_links && !$node) {
    return;
  }

  $checkbox_label = theme('site_disclaimer_checkbox_label', array('checkbox_label' => $checkbox_label, 'node' => $node));
  $fieldset_en = variable_get('site_disclaimer_fieldset', 1);
  if ($fieldset_en) {
    // Adding the fieldset.
    $form['site_disclaimer'] = array(
      '#type' => 'fieldset',
      '#title' => check_plain(variable_get('site_disclaimer_title', SITE_DISCLAIMER_DEFAULT_TITLE)),
      '#weight' => 10,
    );
    $fieldset = &$form['site_disclaimer'];
  }
  else {
    $fieldset = &$form;
  }
  // Adding the Site Disclaimer to the fieldset.
  if ($show_node) {
//D6    $terms = node_prepare($node);
    $items = field_get_items('node', $node, 'body');
    if ($items) {
      $terms = field_view_value('node', $node, 'body', $items[0]);
      $fieldset['site_disclaimer_text'] = array(
        '#markup' => theme('site_disclaimer', array('terms' => $terms['#markup'], 'node' => $node)),
        '#weight' => 9,
      );
    }
    else {
      watchdog('Site Disclaimer', 'The body field of the Site Disclaimer node !nid is empty. Please check the the node content.', array('!nid' => $nid), WATCHDOG_ALERT, l(t('Administer Site Disclaimer'), SITE_DISCLAIMER_SETTINGS_PATH));
    }
  }

  // Adding the checkbox to the fieldset.
  $fieldset['I_agree'] = array(
    '#type' => 'checkbox',
    '#title' => $checkbox_label,
    '#required' => TRUE,
    // The following adds "required" validation to the checkbox (patches core missing functionality as of D6.16)
    '#element_validate' => array('_site_disclaimer_validate_checkbox'),
    '#return_value' => variable_get('site_disclaimer_version', 1),
    '#weight' => 10,
  );

  return $form;
}

/**
 * Validation callback; verify that checkbox is checked.
 */
function _site_disclaimer_validate_checkbox($form, &$form_state) {
  $value = $form_state['values']['I_agree'];
  if ($value != variable_get('site_disclaimer_version', 1)) {
    form_set_error('I_agree', t('You must agree with the !terms to get an account.',
      array('!terms' => check_plain(variable_get('site_disclaimer_title', SITE_DISCLAIMER_DEFAULT_TITLE)))));
  }
}

/**
 * Implements hook_user_presave().
 */
function site_disclaimer_user_presave(&$edit, $account, $category) {
  if (isset($edit['I_agree'])) {
    $edit['data']['site_disclaimer'] = $edit['I_agree'];
    unset($edit['I_agree']);
  }
}

function _site_disclaimer_i18n() {
  $variables = array(
// Rely on node translations.
//    'site_disclaimer_node_title',
//    'site_disclaimer_node_id',
    'site_disclaimer_title',
    'site_disclaimer_checkbox_label',
    'site_disclaimer_version_details',
  );
  $i18n_variables = variable_get('i18n_variables', array());
  if (in_array($variables[0], $i18n_variables)) {
    return;
  }
  $i18n_variables = array_merge($i18n_variables, $variables);
  variable_set('i18n_variables', $i18n_variables);
}

/**
 * Implements hook_init().
 */
function site_disclaimer_init() {
  global $user;

  // http://drupal.org/node/313272#comment-2786004
  // This is a performance hit. We need it here because $conf in settings.php
  // overrides database variable value in variable_init().
  // Faster performance will be achieved if the variables in
  // _site_disclaimer_i18n() are added to $conf in settings.php.
//  _site_disclaimer_i18n();

  if ($user->uid > 1 && !user_access('administer site configuration')) {
    $site_disclaimer_version = variable_get('site_disclaimer_version', 1);
    $site_disclaimer_node_id = variable_get('site_disclaimer_node_id', '');
    if ($site_disclaimer_node_id > 0 && (!isset($user->data['site_disclaimer']) || $user->data['site_disclaimer'] < $site_disclaimer_version)) {
      $allow_nodes = variable_get('site_disclaimer_allow_nodes', array());
      // Allowed paths, array path components mean logical or (any of)
      $allow_paths = array(
        explode('/', SITE_DISCLAIMER_ACCEPT_PATH),
        array('user', 'logout'),
      );
      // Any links in the Site Disclaimer node or in the checkbox should also be allowed.
      if (!empty($allow_nodes)) {
        $allow_paths[] = array('node', $allow_nodes);
        // Special case for print.module - allow the user to also print the allowed nodes.
        $allow_paths[] = array('print', 'node', $allow_nodes);
        $allow_paths[] = array('printpdf', 'node', $allow_nodes);
      }
// @todo: implement hook_site_disclaimer_allowed_paths_alter() here, so paths like print, printpdf can belong to print module

      // Check if the path is in the allowed list
      foreach ($allow_paths as $allow_path) {
        // This while loop ends in "return" if the path matches.
        // If the path does not match, break 2 will loop to the next allowed path
        while (1) {
          $i = 0;
          // Array path components mean logical or (any of)
          foreach ($allow_path as $part) {
            if (is_array($part) && !in_array(arg($i), $part)) {
              break 2;
            }
            if (!is_array($part) && arg($i) != $part) {
              break 2;
            }
            $i++;
          }
          // The arg() path matches the one in the $allow_path
          return;
        }
      }
      drupal_goto(SITE_DISCLAIMER_ACCEPT_PATH, array('query' => drupal_get_destination()));
    }
  }
}


/**
 * Implements hook_theme().
 */
function site_disclaimer_theme() {
  return array(
    'site_disclaimer' => array(
      'variables' => array('terms' => NULL, 'node' => NULL),
    ),
    'site_disclaimer_checkbox_label' => array(
      'variables' => array('checkbox_label' => '', 'node' => NULL),
    ),
  );
}

/**
 * Theme the Site Disclaimer section.
 *
 * @param $terms
 *  The Site Disclaimer, already formatted.
 * @param $node
 *  The $node object, in case we need it.
 * @return
 *  HTML output.
 * @ingroup themeable
 */
function theme_site_disclaimer($variables) {
  $terms = $variables['terms'];
  $node = $variables['node'];
  // id to match our css (#site-disclaimer) for setting Site Disclaimer form height
  $output  = '<div id="site-disclaimer" class="content clear-block">';
  $output .= $terms;
  $output .= '</div>';
  return $output;
}

/**
 * Theme the Site Disclaimer checkbox label.
 *
 * @param $checkbox_label
 *  The label from the admin form saved in the DB.
 * @param $node
 *  The Site Disclaimer $node.
 * @return
 *  HTML output.
 * @ingroup themeable
 */
function theme_site_disclaimer_checkbox_label($variables) {
  $checkbox_label = $variables['checkbox_label'];
  $node = $variables['node'];
  if (!drupal_strlen($checkbox_label)) {
    return '';
  }

  $output = $checkbox_label;
  if (!empty($node)) {
    //We are linking to the terms instead, replace the link.
    //$output = str_replace('@link', l($node->title, 'node/' . $node->nid), $output);
    $output = str_replace('@link', l($node->title, 'node/' . $node->nid, array('attributes' => array('target' => '_blank'))), $output);
  }
  $output = _site_disclaimer_checkbox_label_substitute_links($output);
  return $output;
}

/*
 * Worker code for hook_nodeapi().
 */
function _site_disclaimer_nodeapi_work($node, $op) {
  $nid = variable_get('site_disclaimer_node_id', '');
  $nids = ($nid != '') ? _site_disclaimer_nid_all_translations($nid) : array();
  $nids += variable_get('site_disclaimer_allow_nodes', array());

  // Figure out translation relationships of the node
  if (!empty($node->tnid)) {
    $node_tnid = $node->tnid;
  }
  elseif (!empty($node->translation_source)) {
    $node_tnid = $node->translation_source->tnid
      ? $node->translation_source->tnid
      : $node->translation_source->nid;
  }
//?  if (!$node_tnid) {
//    $node_tnid = $_GET['translation'];
//  }

  if (isset($node->nid) && in_array($node->nid, $nids)
    || !empty($node_tnid) && in_array($node_tnid, $nids)
  ) {
    module_load_include('inc', 'site_disclaimer', 'site_disclaimer.admin');

    _site_disclaimer_prep_allowed_paths($nid, ($op == 'delete') ? $node->nid : NULL);
  }
}

/**
 * Implements hook_node_insert().
 */
function site_disclaimer_node_insert($node) {
  _site_disclaimer_nodeapi_work($node, 'insert');
}

/**
 * Implements hook_node_update().
 */
function site_disclaimer_node_update($node) {
  _site_disclaimer_nodeapi_work($node, 'update');
}

/**
 * Implements hook_node_delete().
 */
function site_disclaimer_node_delete($node) {
  _site_disclaimer_nodeapi_work($node, 'delete');
}

/**
 * Implements hook_views_api().
 */
function site_disclaimer_views_api() {
  return array(
    'api' => 2.0,
    'path' => drupal_get_path('module', 'site_disclaimer') . '/views',
  );
}
