<?php

/**
 * @file
 * Main module file for the SuiteDesk module.
 * Basic SuiteDesk module provides a dashboard and some API functions.
 * 1: Hooks (help, perm,  init, menu, theme)
 * 2: Dashboard
 * 3: Admin Settings
 * 4: Date / time manipulation
 * 5: Taxation functions
 * 6: SuiteDesk Icons
 * 7: SQL Functions
 * 8: Node form theming
 */

/**
 * Status value used for database field {node}.status
 */
define('STATUS_TRASH', -1);

/**
 * Notify node type prefix.
 */
define('NOTIFY_NODE_TYPE', 'notify_node_type_');

// HOOKS
/**
 * Implements hook_help().
 */
function storm_help($path, $arg) {
  $output = '';

  switch ($path) {
    case "dashboard":
      if (user_is_anonymous()) {
        $output = '<p>' . t('Welcome to !suitedesk, my own web based app for planning, knowledge sharing, clients support, collaboration and personal productivity, with both powerful and simplicity in mind.', array('!suitedesk' => '<strong>SuiteDesk</strong>')) . '</p>';
        $output .= '<p>' . t('As a customer, you will have direct access to !suitedesk on any device to keep track your project(s), view online documentation, report project issues or access billing information.', array('!suitedesk' => '<strong>SuiteDesk</strong>')) . '</p><br />';
        /*
        $output .= '<div class="suitedesk-help suitedesk-projects">';
        $output .= '<img class="img-responsive" src="' . drupal_get_path('module', 'storm') . '/images/suitedesk-projects.png" alt="' . 'SuiteDesk Projects' . '" />';
        $output .= '<h2>' . t('Projects & Tasks') . '</h2>';
        $output .= '<p>' . t('Keep track of everything, with visual indicators and Gantt charts to monitorize tasks and subtasks in order to stay up to date with milestones, track of time, workflows and all that requires your attention.') . '</p>';
        $output .= '</div>';

        $output .= '<div class="suitedesk-help suitedesk-doks">';
        $output .= '<img class="img-responsive" src="' . drupal_get_path('module', 'storm') . '/images/suitedesk-doks.png" alt="' . 'SuiteDesk Doks' . '" />';
        $output .= '<h2>' . t('Doks') . '</h2>';
        $output .= '<p>' . t('Documents are "web documents", availables wherever you are. Use the book project to keep project requeriments, attached files, install and user guides, or meeting minutes at your fingertips.') . '</p>';
        $output .= '</div>';

        $output .= '<div class="suitedesk-help suitedesk-tickets">';
        $output .= '<img class="img-responsive" src="' . drupal_get_path('module', 'storm') . '/images/suitedesk-tickets.png" alt="' . 'SuiteDesk Tickets' . '" />';
        $output .= '<h2>' . t('Issue Tracking') . '</h2>';
        $output .= '<p>' . t('You can report and interact on every issue that arise in your project and easily track it and get all needed information until get fixed and tested.') . '</p>';
        $output .= '</div>';

        $output .= '<div class="suitedesk-help suitedesk-rwd">';
        $output .= '<img class="img-responsive" src="' . drupal_get_path('module', 'storm') . '/images/suitedesk-rwd.png" alt="' . 'SuiteDesk Responsive Design' . '" />';
        $output .= '<h2>' . t('Responsive Design') . '</h2>';
        $output .= '<p>' . t('!suitedesk is optimized to look great on mobile devices thanks to its !rwd. All pages adapt automatically to the screen size on a mobile phone, tablet or desktop computer.', array('!suitedesk' => '<strong>SuiteDesk</strong>', '!rwd' => '<em>' . t('responsive design') . '</em>')) . '</p>';
        $output .= '</div>';

        $output .= '<div class="suitedesk-contact"></div>';
        */
      }
      break;
    case "admin/help#storm":
      $output = '<p>' .  t("Provides a complete project management environment") . '</p>';
      break;
    case "admin/help#stormattribute":
      $output = '<p>' .  t("Provides attributes support for SuiteDesk") . '</p>';
      $output .= '<p>' . t("Price Modes - Price modes are used calculate the price of the SuiteDesk node when added to an invoice. A price mode can be added to any SuiteDesk node type and any type can be added to an invoice. When a node is added to an invoice it looks for a price mode in the following order:") . '</p>';
      $output .= '<p>' . t("Ticket, Task, Project, Organization.") . '</p>';
      $output .= '<p>' . t("It will take the price mode for it's current node or the first valid node type above in it's tree. e.g. For a task node type, if that node doesn't have a price mode it will look at the project and then the organization and take the first one it finds. This means you can define a price mode for the organization and it will be used for all nodes under that organization unless it's given a different one.") . '</p>';
      $output .= '<p>' . t("The following price modes keys are defined:") . '<br />';
      $output .= '<ul>';
      $output .= '<li>' . t("not applicable - This is ignored by the system. This means that no price mode is defined.") . '</li>';
      $output .= '<li>' . t("fixed - Price of 0 is used (so the invoice would need to be manually updated.") . '</li>';
      $output .= '<li>' . t("hourly - Price taken from node and multiplied by billing duration.") . '</li>';
      $output .= '<li>' . t("daily - Price given is for daily rate. This price is divided by 8 hours and then multiplied by the billing duration.") . '</li>';
      $output .= '<li>' . t("fixed_price - Will use the price given as the fixed price for that invoice item.") . '</li>';
      $output .= '</ul>';
      break;
  }

  return $output;
}

/**
 * Implements hook_perm().
 */
function storm_perm() {
  return array(
    'Storm: access dashboard',
    'Storm: access administration pages',
    'Storm: administer notify',
    'Storm: access trash',
    'Storm: purge trash',
/*  'administer chamilo', */
  );
}

/**
 * Implements hook_theme_registry_alter().
 */
function storm_theme_registry_alter(&$theme_registry) {
  if (isset($theme_registry['page'])) {
    $theme_registry['page']['preprocess functions'][] = 'storm_preprocess_page';
  }
}

/**
 * Process variables for page.tpl.php
 *
 * @param $variables
 *   The existing theme data structure.
 */
function storm_preprocess_page(&$variables) {
  // Get an array of all the JavaScript files loaded by Drupal on this page.
  // Only if Advanced CSS/JS Aggregator module is not enabled.
  if (!defined(ADVAGG_ENABLED) || !ADVAGG_ENABLED) {
    $scripts = drupal_add_js();
    storm_preprocess_js($scripts);
    $variables['scripts'] = drupal_get_js('header', $scripts);
  }
}

/**
 * Implements hook_advagg_js_pre_alter().
 */
function storm_advagg_js_pre_alter(&$javascript, $preprocess_js, $public_downloads, $scope) {
  storm_preprocess_js($javascript);
}

function storm_preprocess_js(&$scripts) {
  global $theme;

  if (empty($scripts) || $theme != 'zuitedesk') {
    return;
  }

  // The path to the jQuery files that need to be replaced.
  $jquery_replace_path = drupal_get_path('module', 'storm') . '/js/';

  if (isset($scripts['core']['misc/jquery.js'])) {
    $scripts['core'] = array_merge(
      array($jquery_replace_path . 'jquery-1.9.1.min.js' => $scripts['core']['misc/jquery.js']),
      array($jquery_replace_path . 'jquery-switch.js' => $scripts['core']['misc/jquery.js']),
      array($jquery_replace_path . 'jquery-1.3.2.min.js' => $scripts['core']['misc/jquery.js']),
      array($jquery_replace_path . 'jquery-ie.js' => $scripts['core']['misc/jquery.js']),
      $scripts['core']
    );
    unset($scripts['core']['misc/jquery.js']);
  }

  $jquery_replacements = array(
    'module' => array(
      'misc/farbtastic/farbtastic.js' => 'farbtastic.js',
      'misc/teaser.js' => 'teaser.js',
      'misc/jquery.form.js' => 'jquery.form.2.95.js',
      'misc/ahah.js' => 'ahah.js',
      // Certain versions of Views re-add tabledrag.js as $type 'module'.
      'misc/tabledrag.js' => 'tabledrag.js',
    ),
    'core' => array(
      'misc/tabledrag.js' => 'tabledrag.js',
    ),
  );
  // Loop through each of the required replacements.
  foreach ($jquery_replacements as $type => $replacements) {
    foreach ($replacements as $find => $replace) {
      // If the file to replace is loaded on this page...
      if (isset($scripts[$type][$find])) {
        // Create new entry for the replacement file and unset the original one.
        $replace = $jquery_replace_path . $replace;
        $scripts[$type][$replace] = $scripts[$type][$find];
        unset($scripts[$type][$find]);
      }
    }
  }
}

/**
 * Implements hook_user().
 *
 * After user LOGOUT:
 *
 *   - Logout from SuiteCRM if there is an active session.
 *   - Try logout from Chamilo.
 *
 */
function storm_user($op, &$edit, &$account, $category = NULL) {
  global $base_url;

  switch($op) {
    case 'view':
      $account->content['summary']['member_for']['#title'] = t('In SuiteDesk for:');
      break;

    case 'login':

      /* Update EDVCA user password:
      $chamilo_user = $user->chamilo_settings['chamilo_user'];
      if (!empty($chamilo_user)) {
        db_set_active('educa');
        db_query("UPDATE user SET password = '%s' WHERE username = '%s'", $password, $chamilo_user);
        db_set_active('default');
      } */

      /* Redirect user after login (see Login destination module).
         Don't redirect on registration's password reset.
         Also check for force_password_change.
      if (!(arg(0) == 'user' && arg(1) == 'reset') && !($account->force_password_change && $account->uid == $user->uid) && !isset($_REQUEST['destination'])) {
        $redirect = '/';
        if ($_SESSION['deskmenu'][7] == '1') {
          $redirect = '/dashboard';
        } elseif ($_SESSION['deskmenu'][0] == '1') {
          $redirect = '/posts';
        }
        $_REQUEST['destination'] = $redirect;
      } */

      break;

    case 'logout':
      // Try SuiteCRM logout:
      if (!empty($_SESSION['SuiteCRM_Session_ID'])) {
        file_get_contents($base_url . '/crm/index.php?module=Users&action=Logout&MSID=' . $_SESSION['SuiteCRM_Session_ID']);
        watchdog('user', t('Logout SuiteCRM: session closed for user %user.', array('%user' => $account->name)));
        unset($_SESSION['SuiteCRM_Session_ID']);
      }
      /* Try Chamilo logout:
      if (!empty($chamilo_user)) {
        drupal_goto($base_url . '/educa/index.php?logout=logout');
      } */
      break;

/*  case 'load':
      if ($account->uid) {
        $result = db_query('SELECT duser FROM {chamilo_user} WHERE user_id = %d', $account->uid);
        while ($user_data = db_fetch_object($result)) {
          $account->chamilo_settings = array(
            'chamilo_user' => $user_data->duser
          );
        }
      }
      break; */

/*  case 'form':
      if ($category == 'account' && user_access('administer chamilo')) {
        $form['chamilo_settings'] = array(
          '#type' => 'fieldset',
          '#title' => t('Chamilo settings'),
          '#collapsible' => TRUE,
          '#weight' => 1,
          '#tree' => TRUE,
        );
        $form['chamilo_settings']['chamilo_user'] = array(
          '#type' => 'textfield',
          '#title' => t('Chamilo username'),
          '#default_value' => $account->chamilo_settings['chamilo_user'],
          '#description' => t('Chamilo username.'),
        );
        return $form;
      }
      break; */

    case 'insert':
      // After user creation (assign to user default notify and watcher values):
      if (isset($edit['notify_decision']) && $edit['notify_decision'] == 1) {
        db_query('INSERT INTO {notify} (uid, status, node, comment) VALUES (%d, %d, %d, %d)', $account->uid, 1, 1, 0);
        $edit['notify_decision'] = NULL;
      }
      db_query('INSERT INTO {watcher_user_settings} values(%d, 1, 1, 1, 1, 1, 0, 0)', $account->uid);

      break;

/*  case 'update':
      if ($category == 'account' && user_access('administer chamilo')) {
        // Delete old user data:
        db_query("DELETE FROM {chamilo_user} WHERE user_id = %d", $account->uid);

        if (!empty($edit['chamilo_settings']['chamilo_user']) && $edit['chamilo_settings']['chamilo_user']) {
          // Save new user data:
          db_query("INSERT INTO {chamilo_user} (user_id, duser) VALUES (%d, '%s')", $account->uid, $edit['chamilo_settings']['chamilo_user']);
        }
      }
      break; */

    case 'delete':
      // In user deletion (delete user notify and watcher values):
      db_query('DELETE FROM {notify} WHERE uid = %d', $account->uid);
      db_query('DELETE FROM {watcher_user_settings} WHERE uid = %d', $account->uid);

      // Redirect after delete user:
      if ($account->uid != 1) {
        $_REQUEST['destination'] = 'admin/user/user/local';
      }

      break;

    case 'register':
      // Returns notify form fields to be added to User Registration form:
      if ($_SESSION['deskmenu'][1]) {
        // Add a fieldset containing a checkbox for users to accept getting
        // updates on the registration form:
        $fields['notify_agree'] = array(
          '#type' => 'fieldset',
          '#title' => t('SuiteDesk notifications')
        );
        // Add the checkbox to the fieldset:
        $fields['notify_agree']['notify_decision'] = array(
          '#type' => 'checkbox',
          '#title' => t('Receive email notifications of new content posted to SuiteDesk.'),
          '#return_value' => 1,
          '#default_value' => 1,
        );
        return $fields;
      }
  }
}

/**
 * Implements hook_init().
 *
 * First time for authenticated users (not depends of user login):
 *
 *   - Initialize SuiteDesk user data.
 *   - Init $_SESSION['deskmenu'] variable for build main menus:
 *
 *       XXXXXXXXXXXXXXXXXX = $_SESSION['deskmenu']
 *       ||||||||||||||||||
 *       |||||||||||||||||+- [17] - Access to 'Watcher'
 *       ||||||||||||||||+-- [16] - Access to 'Trash'
 *       |||||||||||||||+--- [15] - Access to 'Ideas'
 *       ||||||||||||||+---- [14] - Access to 'Notes'
 *       |||||||||||||+----- [13] - Access to 'Invoices'
 *       ||||||||||||+------ [12] - Access to 'Expenses'
 *       |||||||||||+------- [11] - Access to 'Timetrackings'
 *       ||||||||||+-------- [10] - Access to 'Tickets'
 *       |||||||||+---------  [9] - Access to 'Teams'
 *       ||||||||+----------  [8] - Access to 'Persons'
 *       |||||||+-----------  [7] - Access to 'Organizations'
 *       ||||||+------------  [6] - Access to 'Documents'
 *       |||||+-------------  [5] - Access to 'Events'
 *       ||||+--------------  [4] - Access to 'Tasks Kanban'
 *       |||+---------------  [3] - Access to 'Tasks'
 *       ||+----------------  [2] - Access to 'Projects'
 *       |+-----------------  [1] - Access to create users
 *       +------------------  [0] - First time login
 *
 *   - Try login on SuiteCRM and init variables:
 *
 *       $_SESSION['SuiteCRM_User_ID'] = User ID in SuiteCRM
 *       $_SESSION['SuiteCRM_Session_ID'] = Session ID for user in SuiteCRM
 */
function storm_init() {
  global $user, $base_url, $conf;

  // It is intended to move these calls to pages which specifically need them rather than on hook_init.
  drupal_add_js(drupal_get_path('module', 'storm') .'/storm.js', 'module', 'header', FALSE);
  drupal_add_css(drupal_get_path('module', 'storm') .'/storm.css', 'module');

  // Only first time for authenticated users:
  if (user_is_logged_in() && empty($_SESSION['deskmenu'])) {

    // Initialize SuiteDesk user data:
    $user->stormorganization_nid = -1;
    $user->stormperson_nid = -1;

    // Init $_SESSION[deskmenu] to build main menus:
    $deskmenu = '000000000000000000';
    if ($user->uid == 1) {
      $deskmenu = '111111111111111111';
    } else {
      $deskmenu[1]  = user_access('create users') ? '1' : '0';
      $deskmenu[2]  = user_access('Storm project: access') ? '1' : '0';
      $deskmenu[3]  = user_access('Storm task: access') ? '1' : '0';
      $deskmenu[4]  = user_access('Storm task: edit if assigned to task') ? '1' : '0';
      $deskmenu[5]  = user_access('Storm event: access') ? '1' : '0';
      $deskmenu[6]  = user_access('Storm dok: access') ? '1' : '0';
      $deskmenu[7]  = user_access('Storm organization: access') ? '1' : '0';
      $deskmenu[8]  = user_access('Storm person: access') ? '1' : '0';
      $deskmenu[9]  = user_access('Storm team: access') ? '1' : '0';
      $deskmenu[10] = user_access('Storm ticket: access') ? '1' : '0';
      $deskmenu[11] = user_access('Storm timetracking: access') ? '1' : '0';
      $deskmenu[12] = user_access('Storm expense: access') ? '1' : '0';
      $deskmenu[13] = user_access('Storm invoice: access') ? '1' : '0';
      $deskmenu[14] = user_access('Storm note: access') ? '1' : '0';
      $deskmenu[15] = user_access('Storm idea: access') ? '1' : '0';
      $deskmenu[16] = user_access('Storm: access trash') ? '1' : '0';
      $deskmenu[17] = user_access('use watcher') ? '1' : '0';
    }
    // Is it the first time user login?:
    $deskmenu[0] = $user->created == $user->access ? '1' : '0';
    $_SESSION['deskmenu'] = $deskmenu;

    // Verify SuiteCRM access. Get the SuiteCRM User ID:
    if ($conf['storm_suitecrm']) {
      $sugaruid = $user->uid == 1 ? 1 : db_result(db_query("SELECT field_stormperson_sugaruid_value FROM {stormperson} JOIN {content_type_stormperson} USING (vid) WHERE user_uid = %d", $user->uid));
      if (!empty($sugaruid)) {
        $password = md5(substr(md5(microtime()), rand(0, 26), 16));
        db_set_active('sugarcrm');
        db_query("UPDATE users SET user_hash = '%s' WHERE id = '%s'", $password, $sugaruid);
        $affected_rows = db_affected_rows();
        db_set_active('default');

        if ($affected_rows == 0) {
          watchdog('user', t('Login SuiteCRM: User ID %userid not found.', array('%userid' => $sugaruid)), NULL, WATCHDOG_ERROR);
        } else {

          // Make cURL request:

          # ob_start();
          $curl_request = curl_init();

          curl_setopt($curl_request, CURLOPT_URL, $base_url . '/crm/service/v2/rest.php');
          curl_setopt($curl_request, CURLOPT_POST, 1);
          curl_setopt($curl_request, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
          curl_setopt($curl_request, CURLOPT_HEADER, 1);
          curl_setopt($curl_request, CURLOPT_SSL_VERIFYPEER, 0);
          curl_setopt($curl_request, CURLOPT_RETURNTRANSFER, 1);
          curl_setopt($curl_request, CURLOPT_FOLLOWLOCATION, 0);

          $parameters = array(
            "user_auth" => array(
              "user_name" => $user->name,
              "password"  => $password,
              "version"   => "1"
            ),
            "application_name" => "RestTest",
            "name_value_list"  => array()
          );

          $post = array(
            "method" => "login",
            "input_type" => "JSON",
            "response_type" => "JSON",
            "rest_data" => json_encode($parameters)
          );

          curl_setopt($curl_request, CURLOPT_POSTFIELDS, $post);
          $result = curl_exec($curl_request);
          curl_close($curl_request);

          $result = explode("\r\n\r\n", $result, 2);
          $login_result = json_decode($result[1]);
          # ob_end_flush();

          if (empty($login_result->id)) {
            watchdog('user', t('Login SuiteCRM: impossible get Session ID for user %user.', array('%user' => $user->name)), NULL, WATCHDOG_ERROR);
          } else {
            watchdog('user', t('Login SuiteCRM: session opened for user %user.', array('%user' => $user->name)));
            $_SESSION['SuiteCRM_User_ID'] = $sugaruid;
            $_SESSION['SuiteCRM_Session_ID'] = $login_result->id;
          }
        }
      }
    }
  }
/*
  global $user, $base_path;

  // Implementation of Master SSO server for Chamilo access:
  if ($user->uid && isset($_GET['sso_referer'])) {
    $protocols = array('http://', 'https://');
    $sso_protocol = $protocols[variable_get('chamilo_sso_protocol', 0)];
    $master_auth_uri = filter_xss($_SERVER['HTTP_HOST']) . $base_path . '?q=user';

    // Master Cookie:
    $sso = array(
      'username' => $user->chamilo_settings['chamilo_user'],
      'secret' => sha1($user->pass),
      'master_domain' => filter_xss($_SERVER['HTTP_HOST']),
      'master_auth_uri' => $master_auth_uri,
      'lifetime' => time() + 3600,
      'target' => filter_xss($_GET['sso_target']),
    );
    $cookie = base64_encode(serialize($sso));

    // Redirect to Chamilo server:
    $params = 'sso_referer=' . urlencode($sso_protocol . $master_auth_uri) . '&sso_cookie=' . urlencode($cookie);
    header('Location: ' . $sso_protocol . variable_get('chamilo_server', '') . '?' . $params);
    ?>
    <html>
    <head><title><?php print t('Redirection');?></title></head>
    <body><?php print t('Please wait...');?></body>
    </html>
    <?php
    exit;
  } */
}

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

  // Redirect to SuiteCRM:
  $items['suitecrm'] = array(
    'title' => 'SuiteCRM',
    'page callback' => 'storm_suitecrm_callback',
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );

  $dashboard_types = module_invoke_all('storm_dashboard_types');
  foreach ($dashboard_types as $type => $type_info) {
    if (isset($type_info['url'])) {
      $title = isset($type_info['title']) ? $type_info['title'] : 'SuiteDesk Dashboard';
      $items[$type_info['url']] = array(
        'title' => $title,
        'description' => $title,
        'page callback' => 'storm_dashboard',
        'page arguments' => array($type),
        'access arguments' => array('Storm: access dashboard'),
        'parent' => '',
        'type' => MENU_NORMAL_ITEM,
      );
      if (isset($type_info['menu_options'])) {
        $items[$type_info['url']] = array_merge($items[$type_info['url']], $type_info['menu_options']);
      }
    }
  }

  // View trash.
  $items['trash'] = array(
    'title' => 'Trash',
    'description' => 'SuiteDesk node trash',
    'page callback' => 'storm_trash_list',
    'access arguments' => array('Storm: access trash'),
    'type' => MENU_NORMAL_ITEM,
  );
  $items['trash/empty'] = array(
    'page callback' => 'drupal_get_form',
    'page arguments' => array('storm_empty_trash_confirm'),
    'access arguments' => array('Storm: purge trash'),
    'type' => MENU_CALLBACK,
  );
  // Restore node from trash.
  $items['trash/%node/restore'] = array(
    'title' => t('Restore'),
    'page callback' => 'drupal_get_form',
    'page arguments' => array('storm_trash_restore_node_confirm', 1),
    'access callback' => 'node_access',
    'access arguments' => array('delete', 1),
    'type' => MENU_CALLBACK,
  );
  // Delete node from trash.
  $items['trash/%node/delete'] = array(
    'title' => t('Delete'),
    'page callback' => 'drupal_get_form',
    'page arguments' => array('storm_trash_delete_node_confirm', 1),
    'access callback' => 'node_access',
    'access arguments' => array('delete', 1),
    'type' => MENU_CALLBACK,
  );

  $items['admin/settings/suitedesk'] = array(
    'title' => 'SuiteDesk',
    'description' => 'SuiteDesk administration page',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('storm_admin_settings'),
    'access arguments' => array('Storm: access administration pages'),
    'type' => MENU_NORMAL_ITEM,
  );

  $items['admin/settings/suitedesk/suitedesk'] = array(
    'title' => 'SuiteDesk',
    'description' => 'SuiteDesk administration page',
    'access arguments' => array('Storm: access administration pages'),
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -100,
  );

  $items['admin/settings/suitedesk/notify'] = array(
    'title' => 'Notification settings',
    'description' => 'Adjust settings for new SuiteDesk content notifications sent by e-mail.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('storm_notify_admin_settings'),
    'access callback' => 'user_access',
    'access arguments' => array('Storm: administer notify'),
    'type' => MENU_NORMAL_ITEM,
  );

  $items['admin/user/user/notify'] = array(
    'title' => 'SuiteDesk notifications',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('storm_notify_admin_users'),
    'access callback' => 'user_access',
    'access arguments' => array('create users'),
    'type' => MENU_LOCAL_TASK,
  );
/*
  $items['admin/settings/chamilo'] = array(
    'title' => 'Chamilo',
    'description' => 'Configure integration settings with Chamilo.',
    'page callback' => '_storm_chamilo_settings',
    'access callback' => 'user_access',
    'access arguments' => array('administer chamilo'),
  );
*/
  $items['attributes'] = array(
    'title' => 'Attributes',
    'description' => 'SuiteDesk attributes',
    'page callback' => 'storm_attribute_list',
    'access arguments' => array('Storm: access administration pages'),
    'file' => 'storm.admin.inc',
    'type' => MENU_NORMAL_ITEM,
    'weight' => 11,
  );

  $items['attributes/add'] = array(
    'title' => 'Add a new attribute',
    'description' => 'SuiteDesk attributes',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('storm_attribute_add'),
    'access arguments' => array('Storm: access administration pages'),
    'file' => 'storm.admin.inc',
    'type' => MENU_CALLBACK,
  );

  $items['attributes/edit/%'] = array(
    'title' => 'Edit an attribute',
    'description' => 'SuiteDesk attributes',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('storm_attribute_edit', 3),
    'access arguments' => array('Storm: access administration pages'),
    'file' => 'storm.admin.inc',
    'type' => MENU_CALLBACK,
  );

  $items['attributes/delete/%'] = array(
    'title' => 'Delete an attribute',
    'description' => 'SuiteDesk attributes',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('storm_attribute_delete', 3),
    'access arguments' => array('Storm: access administration pages'),
    'file' => 'storm.admin.inc',
    'type' => MENU_CALLBACK,
  );

  $items['storm/attributes/domain/autocomplete'] = array(
    'title' => 'List of attributes',
    'description' => 'SuiteDesk attributes',
    'page callback' => '_storm_attribute_domain_autocomplete',
    'page arguments' => array(4),
    'access arguments' => array('Storm: access administration pages'),
    'file' => 'storm.admin.inc',
    'type' => MENU_CALLBACK,
  );

  $items['user/%user/notify'] = array(
    'title' => 'Notifications',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('storm_notify_user_settings_form', 1),
    'access callback' => TRUE,
    'type' => MENU_LOCAL_TASK,
  );

  return $items;
}

function storm_suitecrm_callback() {
  global $base_url;

  if ($_SERVER['SCRIPT_NAME'] != '/cron.php') {
    $urlcrm = str_replace('http://', 'https://', $base_url) . '/crm/index.php';
    $params = !empty($_SESSION['SuiteCRM_Session_ID']) ? 'MSID=' . $_SESSION['SuiteCRM_Session_ID'] . '&action=ajaxui#ajaxUILoc=index.php?module=Home&action=index' : NULL;
    drupal_goto($urlcrm, $params);
  }
}

/**
 * Implements hook_menu_alter().
 */
function storm_menu_alter(&$items) {
  $items['node/%node/edit']['page callback'] = '_storm_trash_node_page_edit';
  $items['admin/user/user']['access callback'] = '_storm_user_list_access';
}

function _storm_user_list_access() {
  global $user;

  return !($user->uid > 1 && module_exists('administerusersbyrole')) ? FALSE : user_access('administer users');
}

function storm_notify_admin_settings() {
  $period = array(
    60      => format_interval(60),
    900     => format_interval(900),
    1800    => format_interval(1800),
    3600    => format_interval(3600),
    10800   => format_interval(10800),
    21600   => format_interval(21600),
    32400   => format_interval(32400),
    43200   => format_interval(43200),
    86400   => format_interval(86400),
    172800  => format_interval(172800),
    432000  => format_interval(432000),
    604800  => format_interval(604800),
    1209600 => format_interval(1209600),
    3024000 => format_interval(3024000),
    -1      => t('Never'),
  );

  $form = array();

  $form['notify_settings'] = array(
    '#type' => 'fieldset',
    '#title' => t('E-mail notification settings'),
    '#collapsible' => TRUE,
  );

  $form['notify_settings']['notify_send'] = array(
    '#type' => 'select',
    '#title' => 'Send notifications every',
    '#default_value' => variable_get('notify_send', array(86400)),
    '#options' => $period,
    '#description' => 'How often should new content notifications be sent? Requires cron to be running at least this often.',
  );

  $form['notify_settings']['notify_send_hour'] = array(
    '#type' => 'select',
    '#title' => 'Hour to Send Notifications',
    '#description' => 'Specify the hour (24-hour clock) in which notifications should be sent, if the frequency is one day or greater.',
    '#default_value' => variable_get('notify_send_hour', 9),
    '#options' => array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23),
  );

  $form['notify_settings']['notify_attempts'] = array(
    '#type' => 'select',
    '#title' => 'Number of failed sends after which notifications are disabled',
    '#default_value' => variable_get('notify_attempts', array(5)),
    '#options' => array(t('Disabled'), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20),
  );

  $form['notify_settings']['notify_reg_default'] = array(
    '#type' => 'checkbox',
    '#title' => 'Notification checkbox default on new user registration form',
    '#return_value' => 1,
    '#default_value' => variable_get('notify_reg_default', 1),
  );

  $form['notify_settings']['notify_include_updates'] = array(
    '#type' => 'checkbox',
    '#title' => 'Include updated posts in notifications',
    '#return_value' => 1,
    '#default_value' => variable_get('notify_include_updates', 1),
  );

  $set = 'ntype';
  $form[$set] = array(
    '#type' => 'fieldset',
    '#title' => 'Notification by node type',
    '#collapsible' => TRUE,
    '#collapsed' => FALSE,
    '#description' => 'Having nothing checked defaults to sending notifications about all node types.'
  );

  foreach (node_get_types('types', array()) as $type => $object) {
    $form[$set][NOTIFY_NODE_TYPE . $type] = array(
      '#type' => 'checkbox',
      '#title' => $object->name,
      '#return_value' => 1,
      '#default_value' => variable_get(NOTIFY_NODE_TYPE . $type, 0),
    );
  }

  return system_settings_form($form);
}

function storm_notify_admin_users() {
  $form = array();
  $form['#tree'] = TRUE;
  $form['info'] = array('#value' => '<p>' . t('The following table shows all users that have SuiteDesk notifications enabled.') . '</p>');

  $form['users'] = array();

  $result = db_query('SELECT u.uid, u.name, u.mail, n.* FROM {users} u LEFT JOIN {notify} n ON u.uid = n.uid WHERE n.status = 1 AND u.status = 1 ORDER BY u.name');
  while ($notify = db_fetch_object($result)) {
    $form['users'][$notify->uid] = array();
    $form['users'][$notify->uid]['name'] = array('#type' => 'markup', '#value' => theme('username', $notify));
    $form['users'][$notify->uid]['mail'] = array('#type' => 'markup', '#value' => $notify->mail);
    $form['users'][$notify->uid]['node'] = array('#type' => 'checkbox', '#default_value' => $notify->node);
    $form['users'][$notify->uid]['comment'] = array('#type' => 'checkbox', '#default_value' => $notify->comment);
    $form['users'][$notify->uid]['attempts'] = array('#type' => 'textfield', '#size' => 2,  '#default_value' => $notify->attempts ? intval($notify->attempts) : 0);
  }

  $form['submit'] = array('#type' => 'submit', '#value' => t('Save settings'));

  return $form;
}

function storm_notify_user_settings_form(&$form_state, $arg) {
  global $user;

  if ($user->uid != $arg->uid && !$_SESSION['deskmenu'][1]) {
    drupal_access_denied();
    return;
  }

  $account = user_load(array('uid' => $arg->uid));
  if (!is_object($account)) {
    drupal_not_found();
    return;
  }

  $result = db_query('SELECT u.uid, u.name, u.mail, n.status, n.node, n.comment FROM {users} u LEFT JOIN {notify} n ON u.uid = n.uid WHERE u.uid = %d AND u.status = 1', $account->uid);
  $notify = db_fetch_object($result);
  $form = array();
  if (!$notify->mail) {
    drupal_set_message(t('Your e-mail address must be specified on your <a href="@url">my account</a> page.', array('@url' => url('user/'. $account->uid .'/edit'))), 'error');
  }

  $form['notify_page_master'] = array('#type' => 'fieldset', '#title' => t('SuiteDesk notifications'));
  $form['notify_page_master']['status'] = array('#type' => 'radios',
    '#title' => t('Notify status'),
    '#default_value' => $notify->status,
    '#options' =>  array(t('Disabled'), t('Enabled')),
    '#description' => t('Do you wish to receive periodic e-mails when new content is posted?'),
  );
  $form['notify_page_master']['node'] = array('#type' => 'radios',
    '#title' => t('Notify new content'),
    '#default_value' => $notify->node,
    '#options' => array(t('Disabled'), t('Enabled')),
    '#description' => t('Include new posts in the notification mail.'),
  );
  $form['notify_page_master']['comment'] = array('#type' => 'radios',
    '#title' => t('Notify new comments'),
    '#default_value' => $notify->comment,
    '#options' => array(t('Disabled'), t('Enabled')),
    '#description' => t('Include new comments in the notification mail.'),
  );
  $form['uid'] = array('#type' => 'value', '#value' => $account->uid);
  $form['submit'] = array('#type' => 'submit', '#value' => t('Save settings'));

  return $form;
}

function storm_notify_user_settings_form_submit($form, &$form_state) {
  unset($form);
  db_query('DELETE FROM {notify} WHERE uid = %d', $form_state['values']['uid']);
  db_query('INSERT INTO {notify} (uid, status, node, comment) VALUES (%d, %d, %d, %d)', $form_state['values']['uid'], $form_state['values']['status'], $form_state['values']['node'], $form_state['values']['comment']);
  drupal_set_message(t('Notify settings saved.'));
}

function storm_notify_admin_users_submit($form, &$form_state) {
  unset($form);
  if ($form_state['values']['users']) {
    foreach ($form_state['values']['users'] as $uid => $settings) {
      db_query('UPDATE {notify} SET node = %d, comment = %d, attempts = %d WHERE uid = %d', $settings['node'], $settings['comment'], $settings['attempts'], $uid);
    }
  }
  drupal_set_message(t('Notify settings saved.'));
}

/*
function _storm_chamilo_settings() {
  return drupal_get_form('_storm_chamilo_settings_form');
}

function _storm_chamilo_settings_form() {
  $form['chamilo_sso_protocol'] = array(
    '#type' => 'radios',
    '#title' => t('SSO Server Protocol'),
    '#options' => array('http://', 'https://'),
    '#default_value' => variable_get('chamilo_sso_protocol', 0),
  );
  $form['chamilo_server'] = array(
    '#type' => 'textfield',
    '#title' => t('Application URL and Path'),
    '#description' => t('NOTICE: Do NOT use trailing slash nor http://. i.e: yourchamilosite.com'),
    '#default_value' => variable_get('chamilo_server', ''),
  );
  return system_settings_form($form);
} */

function storm_theme() {
  return array(
    'storm_form_group' => array(
      'file'      => 'storm.theme.inc',
      'arguments' => array('header', 'form'),
    ),
    'datetime' => array(
      'file'      => 'storm.theme.inc',
      'arguments' => array('element'),
    ),
    'dateext' => array(
      'file'      => 'storm.theme.inc',
      'arguments' => array('element'),
    ),
    'storm_link' => array(
      'file'      => 'storm.theme.inc',
      'arguments' => array('source_module', 'destination_module', 'node_nid', 'weight'),
    ),
    'storm_list_report' => array(
      'file'      => 'storm.theme.inc',
      'arguments' => array('header', 'rows', 'title', 'footer', 'headtitle'),
    ),
    'storm_report' => array(
      'file'      => 'storm.theme.inc',
      'arguments' => array('header', 'content', 'title', 'footer'),
    ),
    'storm_view_item' => array(
      'file'      => 'storm.theme.inc',
      'arguments' => array('label', 'value'),
    ),
    'storm_dashboard' => array(
      'file'      => 'storm.theme.inc',
      'arguments' => array('links' => array()),
    ),
    'storm_dashboard_block' => array(
      'file'      => 'storm.theme.inc',
      'arguments' => array('links' => array()),
    ),
    'storm_dashboard_link' => array(
      'file'      => 'storm.theme.inc',
      'arguments' => array('link_blocks' => array()),
    ),
    'storm_dashboard_links_weight_table' => array(
      'file'      => 'storm.theme.inc',
      'arguments' => array('form' => array()),
    ),
    'storm_number_items' => array(
      'file'      => 'storm.theme.inc',
      'arguments' => array('number' => ''),
    ),
    'storm_attribute_list' => array(
      'file'      => 'storm.theme.inc',
      'arguments' => array('form'),
    ),
    'storm_notify_admin_users' => array(
      'file'      => 'storm.theme.inc',
      'arguments' => array('form' => NULL)
    ),
  );
}

/**
 * Register a node form field for alteration by modules implementing the
 * hook_form_field_alter() hook.
 *
 * The mother of the lamb :)
 *
 * See https://drupal.org/node/823858
 */
function storm_form_field_alter_register(&$fields, $name) {
  foreach (element_children($fields) as $key) {
    if (isset($fields[$key]) && ($fields[$key]['#name'] == $name || $fields[$key]['#field_name'] == $name || $fields[$key]['#group_name'] == $name)) {
      foreach (module_implements('form_field_alter') as $module) {
        call_user_func_array($module . '_form_field_alter', array($name, &$fields[$key]));
      }
    } else {
      storm_form_field_alter_register($fields[$key], $name, $callback);
    }
  }
}

/**
 * Function to create a dashboard (call to theme function)
 *
 * @param string $type dashboard type, used for storm_get_dashboard_links
 * @param null|string $theme Theme function to use, if omitted it uses storm_dashboard
 * @return string themed string
 */
function storm_dashboard($type = 'page') {
  $types = module_invoke_all('storm_dashboard_types');
  // only set Page Title if it has a url, so there is a menu entry for that
  // The block type has no url and shouldn't change the title!
  if (isset($types[$type]['url']) && isset($types[$type]['title'])) {
    drupal_set_title($types[$type]['title']);
  }
  drupal_add_css(drupal_get_path('module', 'storm') .'/storm-dashboard.css');

  $link_blocks = storm_dashboard_get_links(TRUE, $type);
  if (!empty($link_blocks)) {
    // DIVIDE LINKS INTO TWO BLOCKS
    $count = ceil(count($link_blocks) / 2);
    $link_blocks = array_chunk($link_blocks, $count);
  }
  $theme = isset($types[$type]['theme']) ? $types[$type]['theme'] : 'storm_dashboard';
  $content = theme($theme, $link_blocks);

  return $content;
}

/**
 * Return links array for the storm dashboard.
 *
 * @param bool $check_active when FALSE, returns all links, no matter if activated or not (needed for admin settings)
 * @param string $type
 * @return array dashboard links
 */
function storm_dashboard_get_links($check_active = TRUE, $type = 'page') {

  $links = module_invoke_all('storm_dashboard_links', $type);

  if (!empty($links)) {

    $default_dashboard_settings = variable_get('storm_'. $type .'dashboard_settings', array());

    $weight = 0;
    foreach ($links as $key => &$link_array) {

      // ACTIVE CHECK
      if ($check_active && isset($default_dashboard_settings[$link_array['path']]['active']) && $default_dashboard_settings[$link_array['path']]['active'] == FALSE) {
        unset($links[$key]);
        continue;
      }

      // MODULE EXIST CHECK
      if (isset($link_array['destination_module']) && !module_exists($link_array['destination_module'])) {
        unset($links[$key]);
        continue;
      }

      // ACCESS CHECK
      if (!empty($link_array['access_callback'])) {
        if (function_exists($link_array['access_callback'])) {
          if (!isset($link_array['access_arguments'])) {
            $link_array['access_arguments'] = "";
          }
          // USE THE ACCESS CHECK OF THE MENU API
          _menu_check_access($link_array, $link_array['map']);
        }
      }
      elseif (isset($link_array['access_arguments'])) {
        $link_array['access'] = user_access($link_array['access_arguments']);
      }
      else {
        $link_array['access'] = TRUE;
      }

      if (!$link_array['access']) {
        unset($links[$key]);
      }

      if (isset($default_dashboard_settings[$link_array['path']]['weight'])) {
        $link_array['weight'] = $default_dashboard_settings[$link_array['path']]['weight'];
      }
      elseif (!isset($link_array['weight'])) {
        $link_array['weight'] = $weight;
        $weight++;
      }

    }

    // HOOK FOR ALTERING LINKS
    drupal_alter('storm_dashboard_links', $links, $type);

    // SORT LINKS BY WEIGHT
    uasort($links, '_storm_dashboard_sort_links');

  }
  return $links;
}

/**
 * Helper function for storm_dashboard_get_links().
 * Order dashboard links by their weight.
 * This is a uasort callback function.
 *
 * @see uasort()
 * @param array $a dashboard link array
 * @param array $b dashboard link array
 * @return int
 */
function _storm_dashboard_sort_links($a, $b) {
  if (intval($a['weight']) == intval($b['weight'])) {
      return 0;
  }
  elseif (intval($a['weight']) < intval($b['weight'])) {
    return -1;
  }
  else {
    return 1;
  }
}

function storm_storm_dashboard_links($type) {
  $links = array();
  if ($type == 'page' || $type == 'block') {
    $links[] = array(
      'theme' => 'storm_dashboard_link',
      'title' => t('Desktop'),
      'icon' => 'stormoptions',
      'path' => '/',
      'access_arguments' => 'access content',
      'weight' => -10,
    );
/*  if (module_exists('storm_dashboard')) {
      $links[] = array(
        'theme' => 'storm_dashboard_link',
        'title' => t('Desktop'),
        'icon' => 'stormknowledgebase',
        'path' => 'stormdashboard',
        'access_arguments' => 'create stormdoc content',
        'weight' => -9,
      );
    }
    if ($_GET['q'] == 'admin/settings/storm' || !empty($_SESSION['SuiteCRM_Session_ID'])) {
      $links[] = array(
        'theme' => 'storm_dashboard_link',
        'title' => 'SuiteCRM',
        'icon' => 'stormknowledgebase',
        'path' => 'crm',
        'weight' => -8,
      );
    }
*/  $links[] = array(
      'theme' => 'storm_dashboard_link',
      'title' => t('Tasks Kanban'),
      'icon' => 'stormkanban',
      'path' => 'tasks/kanban',
      'access_arguments' => 'Storm task: access',
      'weight' => 5,
    );
    $links[] = array(
      'theme' => 'storm_dashboard_link',
      'title' => t('Attributes'),
      'icon' => 'stormattributes',
      'path' => 'attributes',
      'params' => array(),
      'access_arguments' => 'Storm: access administration pages',
      'map' => array(),
      'weight' => 14,
    );
    $links[] = array(
      'title' => t('Configuration'),
      'icon' => 'stormconfiguration',
      'path' => 'admin/settings/storm',
      'params' => array(),
      'access_arguments' => 'Storm: access administration pages',
      'node_type' => '',
      'add_type' => '',
      'map' => array(),
      'weight' => 15,
    );
/*  $links[] = array(
      'theme' => 'storm_dashboard_link',
      'title' => t('My calendar'),
      'icon' => 'stormevent',
      'path' => 'calendar',
      'params' => array(),
      'access_arguments' => 'create stormevent content',
      'node_type' => 'stormevent',
      'add_type' => 'stormevent',
      'map' => array(),
      'weight' => 21,
    );
*/  $links[] = array(
      'theme' => 'storm_dashboard_link',
      'title' => t('Glossary'),
      'icon' => 'stormglossary',
      'path' => 'lexicon',
      'access_arguments' => 'access lexicon',
      'weight' => 22,
    );
    $links[] = array(
      'theme' => 'storm_dashboard_link',
      'title' => t('FAQ'),
      'icon' => 'stormfaq',
      'path' => 'faq',
      'access_arguments' => 'view faq page',
      'weight' => 23,
    );
    $links[] = array(
      'theme' => 'storm_dashboard_link',
      'title' => t('Watcher'),
      'icon' => 'stormwatcher',
      'path' => 'user/watcher',
      'access_arguments' => 'use watcher',
      'weight' => 24,
    );
    $links[] = array(
      'theme' => 'storm_dashboard_link',
      'title' => t('Trash'),
      'icon' => 'stormtrash',
      'path' => 'trash',
      'access_arguments' => 'Storm: access trash',
      'weight' => 25,
    );
  }
  return $links;
}

/**
 * Implementation of hook_storm_dashboard_types().
 * Defines dashboard types page and block.
 * This can be used as an example for other types
 * @return array
 */
function storm_storm_dashboard_types() {
  return array(
    'page' => array(
      // URL: menu path which should be used, if omitted no menu item will be created
      'url'         => 'dashboard',
      // title: Required. Title of menu item, page title and title for the storm settings
      'title'       => t('SuiteDesk'),
      // description: Required. Description for the fieldset in the storm settings
      'description' => t('You can disable or reorder the links from the !dashboard here', array('!dashboard' => l(t('dashboard'), 'dashboard'))),
      // theme: theme which should be used to display this dashboard. If omitted uses standard theme storm_dashboard
      'theme'       => 'storm_dashboard',
      // menu_options: This array will be merged into the default menu item array. If the menu should have special access arguments, title, it can be set here.
      // page callback and page argument shouldn't been overwritten.
      'menu_options' => array(
        'title'       => t('SuiteDesk'),
        'description' => t('SuiteDesk dashboard'),
      ),
    ),
    'block' => array(
      'title'       => 'Dashboard block',
      'description' => t('You can disable or reorder the links from the dashboard !block here', array('!block' => l(t('block'), 'admin/build/block'))),
      'theme'       => 'storm_dashboard_block',
    ),
  );
}

/**
 * Implements hook_filter().
 */
function storm_filter($op, $delta = 0, $format = -1, $text = '', $cache_id = 0) {
  if ($op == 'list') {
    return array(0 => t('Empty paragraph killer'));
  } elseif ($delta == 0) {
    switch ($op) {
      case 'description':
        return t('Content editors/creators sometimes hit the carriage return
          twice at the end of a paragraph. This may not be in keeping with the
          look and feel of your site. When entering more than one carriage
          return, this filter will ensure that only the first will be honored.');
      case 'no cache':
        // This case can be removed for most filters,
        // but returning TRUE is useful for development.
        return TRUE;
      case 'prepare':
        return $text;
      case 'process':
        // Empty paragraph killer filter.
        $text = preg_replace('#<p[^>]*>(\s|&nbsp;?)*</p>#', '', $text);
        return $text;
    }
  }
}

/**
 * Implements hook_form_alter().
 */
function storm_form_alter(&$form, $form_state, $form_id) {
  if (stristr($form_id, 'node_form')) {
    $node = $form['#node'];
    $types = node_get_types();
    if (empty($node->nid)) {
      drupal_set_title(t('Create ' . $types[$node->type]->name));
    }
    if (!empty($form['title']['#title'])) {
      $form['title']['#title'] = t($form['title']['#title']);
    }
    if (!empty($form['body_field']['body']['#title'])) {
      $form['body_field']['body']['#title'] = t($form['body_field']['body']['#title']);
    }
    // Hide body field show summary:
    if (isset($form['body_field']['teaser_include'])) {
	    unset($form['body_field']['teaser_include']);
    }
  }

  if ($form_id == 'node_type_form' && isset($form['identity']['type'])) {
    // Allow SuiteDesk node edition in a block per content type:
    $form['stormblock'] = array(
      '#type' => 'fieldset',
      '#title' => t('SuiteDesk Block'),
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
    );
    $form['stormblock']['stormblock_expose'] = array(
      '#type' => 'checkbox',
      '#title' => t('Enable data entry from a block'),
      '#default_value' => variable_get('stormblock_expose_'. $form['#node_type']->type, 0),
      '#description' => t('Enable this option to make the entry form for this content type available as a block.'),
    );
    $form['stormblock']['stormblock_show_help'] = array(
      '#type' => 'checkbox',
      '#title' => t('Show submission guidelines'),
      '#default_value' => variable_get('stormblock_show_help_'. $form['#node_type']->type, 0),
      '#options' => $options,
      '#description' => t('Enable this option to show the submission guidelines in the block above the form.'),
      '#process' => array('stormblock_dependent_process'),
      '#dependency' => array('edit-stormblock-expose' => array('1')),
    );

    // Node type edit form, add checkbox to activate trash:
    $form['workflow']['trash_settings'] = array(
      '#type' => 'checkboxes',
      '#title' => t('Trash'),
      '#description' => t('If the trash is enabled for this node type, nodes will be moved to trash instead of being deleted. To permanently delete them you need to delete them from the !trash.', array('!trash' => l(t('trash'), 'trash'))),
      '#options' => array(
        'enabled' => t('Enable trash'),
      ),
      '#weight' => 5,
      '#default_value' => variable_get('trash_settings_' . $form['#node_type']->type, array()),
    );
  }

  if (isset($form['type']) && $form_id == $form['type']['#value'] . '_node_form') {
    // Node editing form. Add classes to node form:
    $class = isset($form['#attributes']['class']) ? $form['#attributes']['class'] . ' ' : NULL;
    $class .= $form_id;
    $form['#attributes']['class'] = $class;
    foreach ($form as $key => $elem) {
      if (is_array($elem) && (isset($elem['#type']) ? $elem['#type'] : NULL) == 'fieldset') {
        $class = (isset($elem['#attributes']['class'])) ? $elem['#attributes']['class'] . ' ' : NULL;
        $class .= $key;
        $form[$key]['#attributes']['class'] = $class;
      }
    }
    // Check if node is edited and it is an affected type for trash nodes:
    if ($form['nid']['#value'] && _storm_trash_enabled($form['type']['#value'])) {
      $form['buttons']['delete']['#value'] = t('Move to trash');
    }
    // Disable fields for tickets and tasks comments:
    if ($form['nid']['#value'] && in_array($form['type']['#value'], array('stormticketc', 'stormtaskc'))) {
      $form['#after_build'][] = 'storm_node_form_after_build';
    }
  }
}

function storm_node_form_after_build($form, &$form_state) {
  $v = 'value';
  $a = '#attributes';
  $g = 'group_' . $form['type']['#value'] . '_data';
  // https://www.silviogutierrez.com/blog/making-cck-fields-read-only-drupal-6:
  switch ($form['type']['#value']) {
    case 'stormticketc':
      # Title:
      $form[$g]['field_stormticketc_title'][0][$v][$a]['readonly'] = 'readonly';
      $form[$g]['field_stormticketc_title'][0][$v][$a]['onclick'] = 'blur();';
      $form[$g]['field_stormticketc_title'][0][$v][$a]['style'] = 'cursor: not-allowed;';
      # Category:
      $form[$g]['field_stormticketc_category'][$v][$a]['disabled'] = 'disabled';
      # Status:
      $form[$g]['field_stormticketc_status'][$v][$a]['disabled'] = 'disabled';
      # Priority:
      $form[$g]['field_stormticketc_priority'][$v][$a]['disabled'] = 'disabled';
      # Assigned:
      $form[$g]['field_stormticketc_assigned'][$v][$a]['readonly'] = 'readonly';
      $form[$g]['field_stormticketc_assigned'][$v][$a]['onclick'] = 'return false;';
      $form[$g]['field_stormticketc_assigned'][$v][$a]['style'] = 'cursor: not-allowed;';
      break;
    case 'stormtaskc':
      # Title:
      $form[$g]['field_stormtaskc_title'][0][$v][$a]['readonly'] = 'readonly';
      $form[$g]['field_stormtaskc_title'][0][$v][$a]['onclick'] = 'blur();';
      $form[$g]['field_stormtaskc_title'][0][$v][$a]['style'] = 'cursor: not-allowed;';
      # Category:
      $form[$g]['field_stormtaskc_category'][$v][$a]['disabled'] = 'disabled';
      # Status:
      $form[$g]['field_stormtaskc_status'][$v][$a]['disabled'] = 'disabled';
      # Priority:
      $form[$g]['field_stormtaskc_priority'][$v][$a]['disabled'] = 'disabled';
  }
  return $form;
}

/**
 * Implements hook_form_FORM_ID_alter().
 * Alter form id node_delete_confirm at node.pages.inc
 * URL: node/%/delete
 */
function storm_form_node_delete_confirm_alter(&$form, &$form_state) {
  $node = node_load($form['nid']['#value']);
  if ($node->status == STATUS_TRASH) {
    drupal_set_title(t('Trash: @title', array('@title' => $node->title)));
    if (user_access('Storm: access trash')) {
      $deleted = '<p>' . t('This content has been moved to the !trash.', array('!trash' => l(t('trash'), 'trash'))) . '</p>';
    } else {
      $deleted = '<p>' . t('This content has been moved to the trash.') . '</p>';
    }
    if (node_access('delete', $node)) {
      $deleted .= '<p>' . t('You can !restore the !lastest.', array('!restore' => l(t('restore'), 'trash/' . $node->nid . '/restore'), '!lastest' => l(t('lastest version'), 'node/' . $node->nid))) . '</p>';
      $deleted .= '<p>' . t('You can permanently !delete this content.', array('!delete' => l(t('delete'), 'trash/' . $node->nid . '/delete'))) . '</p>';
    }
    unset($form['description']);
    unset($form['actions']);
    $form['node_trash_deleted'] = array(
      '#value' => $deleted
    );
  } else {
    if (_storm_trash_enabled($node->type)) {
      drupal_set_title(t('Are you sure you want to move %title to the trash?', array('%title' => $node->title)));
      $form['log'] = array(
        '#type' => 'textfield',
        '#title' => t('Log message'),
        '#default_value' => t('Moved to trash'),
        '#weight' => -1,
      );
      $form['description']['#value'] = t('The node will be moved to the !trash where it can be restored or permanently deleted.', array('!trash' => l(t('trash'), 'trash')));
      $form['actions']['submit']['#value'] = t('Move to trash');
    }
    $form['actions']['submit']['#submit'][] = 'storm_form_node_delete_confirm_submit';
  }
}

function storm_form_node_delete_confirm_submit(&$form, &$form_state) {
  if ($form_state['values']['confirm']) {
    $delete = TRUE;
    $redirect = '<front>';
    $node = node_load($form_state['values']['nid']);
    switch ($node->type) {
      case 'stormorganization':
        $delete = _stormorganization_validate_predelete($node);
        $redirect = 'organizations';
        break;
       case 'stormperson':
         $delete = _stormperson_validate_predelete($node);
         $redirect = 'people';
         break;
       case 'stormteam':
         $delete = _stormteam_validate_predelete($node);
         $redirect = 'teams';
         break;
       case 'stormproject':
         $delete = _stormproject_validate_predelete($node);
         $redirect = 'projects';
         break;
       case 'stormtask':
         $delete = _stormtask_validate_predelete($node);
         $redirect = 'tasks';
         break;
       case 'stormticket':
         $delete = _stormticket_validate_predelete($node);
         $redirect = 'tickets';
         break;
       case 'stormevent':
         $date = new DateTime(substr($node->field_stormevent_date[0]['value'], 0, 10));
         $redirect = 'calendar/' . substr($node->field_stormevent_date[0]['value'], 0, 4) . '-W' . $date->format("W");
         break;
    }
    if ($delete) {
      if (_storm_trash_enabled($node->type)) {
        $redirect = 'trash';
        // Save node as a new revision with status set to STATUS_TRASH:
        $node->status = STATUS_TRASH;
        $node->revision = 1;
        $node->log = $form_state['values']['log'];
        // Call nodeapi hook:
        node_invoke_nodeapi($node, 'move to trash');
        // Save node:
        node_save($node);
      } else {
        // Call original submit handler:
        node_delete_confirm_submit($form, $form_state);
      }
      $form_state['redirect'] = $redirect;
    }
  }
}

/**
 * Implements hook_nodeapi().
 */
function storm_nodeapi(&$node, $op, $teaser, $page) {
  global $user;

  switch ($op) {
    case 'alter':
      if ($node->status == STATUS_TRASH) {
        drupal_set_title(t('Trash: @title', array('@title' => $node->title)));
        if (user_access('Storm: access trash')) {
          drupal_set_message(t('This content has been moved to the !trash.', array('!trash' => l(t('trash'), 'trash'))), 'warning');
        } else {
          drupal_set_message(t('This content has been moved to the trash.'), 'warning');
        }
        if (node_access('delete', $node)) {
          drupal_set_message(t('You can !restore the lastest version.', array('!restore' => l(t('restore'), 'trash/' . $node->nid . '/restore'))), 'warning');
          drupal_set_message(t('You can permanently !delete this content.', array('!delete' => l(t('delete'), 'trash/' . $node->nid . '/delete'))), 'warning');
        }
      }
      break;

    case 'presave':
      // Revisions for projects, doks and notes:
      if (!in_array($node->type, array('stormproject', 'stormdok', 'stormnote', 'stormevent'))) {
        return;
      }

      // Revisions. If it is a new or cloned node:
      if (empty($node->nid)) {
        $node->version = '1.0.000';
        return;
      }

      // Load previous data:
      $node_unchanged = node_load($node->nid);

      // Otherwise check node revision:
      $array_version = explode('.', $node_unchanged->version);
      $nver = empty($array_version[0]) ? 1 : (int)$array_version[0];
      $nrev = empty($array_version[1]) ? 0 : (int)$array_version[1];
      $ndet = empty($array_version[2]) ? 0 : (int)$array_version[2];

      if ($node->revision == 1) {
        // Forcing new revision as a minor modification:
        $nrev++;
        $ndet = 0;
      } elseif ($node->review == 'no review' && $user->uid != $node_unchanged->revision_uid) {
        // New revision if node has been edited by a different user:
        drupal_set_message(t('Created new revision to keep modifications of previous user.'), 'warning');
        $node->log = t('Keeping modifications of previous user.');
        $node->revision = 1;
        $nrev++;
        $ndet = 0;
      } elseif ($node->review == 'minor review') {
        // New revision by minor modification:
        $node->log = $node->reviewlog;
        $node->revision = 1;
        $nrev++;
        $ndet = 0;
      } elseif ($node->review == 'major review') {
        // New revision by major modification:
        $node->log = $node->reviewlog;
        $node->revision = 1;
        $nver++;
        $nrev = 0;
        $ndet = 0;
      } else {
        $ndet++;
      }

      // Check number revisions limits:
      if ($ndet > 999) {
        $nrev++;
        $ndet = 0;
      }
      if ($nrev > 99) {
        $nver++;
        $nrev = 0;
        $ndet = 0;
      }

      // Set version:
      $node->version = $nver . '.' . $nrev . '.' . substr('000', 1, 3 - strlen($ndet)) . $ndet;
      break;
  }
}

/**
 * Are there active storm elements for referential integrity?
 */
function _storm_validate_predelete($node_type, $query) {
  $query = "SELECT s.nid FROM {$node_type} s JOIN {node} n USING (vid) WHERE ($query) AND n.status != " . STATUS_TRASH . ' LIMIT 1';
  return module_exists($node_type) && db_result(db_query($query));
}

/**
 * Is the trash activated for the node type?
 */
function _storm_trash_enabled($node_type) {
  return user_access('Storm: access trash') && in_array('enabled', variable_get("trash_settings_$node_type", array()));
}

/**
 * Overridden page callback for 'node/%node/edit' path.
 */
function _storm_trash_node_page_edit($node) {
  if (isset($node->comment_target_nid) && module_exists('nodecomment')) {
    $target_node = node_load($node->comment_target_nid);
    return $target_node->status == STATUS_TRASH ? node_page_view($node, NULL) : node_page_edit($node);
  }
  return $node->status == STATUS_TRASH ? node_page_view($node, NULL) : node_page_edit($node);
}

/**
 * Restore node confirmation form.
 */
function storm_trash_restore_node_confirm(&$form_state, $node) {
  $form['nid'] = array('#type' => 'value', '#value' => $node->nid);
  $form['log'] = array(
    '#type' => 'textfield',
    '#title' => t('Log message'),
    '#default_value' => t('Restored from trash'),
    '#weight' => -1,
  );
  return confirm_form($form,
    t('Are you sure you want to restore %title?', array('%title' => $node->title)),
    isset($_GET['destination']) ? $_GET['destination'] : 'node/' . $node->nid, '',
    t('Restore'), t('Cancel')
  );
}

/**
 * Restore node confirmation form submit handler.
 */
function storm_trash_restore_node_confirm_submit($form, &$form_state) {
  if ($form_state['values']['confirm']) {
    $node = node_load($form_state['values']['nid']);
    // Save node as a published again:
    $node->status = 1;
    $node->revision = 1;
    $node->log = $form_state['values']['log'];

    // Call nodeapi hook:
    node_invoke_nodeapi($node, 'restore from trash');

    // Save node:
    node_save($node);

    // Go to restored node:
    $form_state['redirect'] = 'node/' . $node->nid;
  } else {
    // Go to trash list:
    $form_state['redirect'] = 'trash';
  }
}

/**
 * Permanently delete node confirmation form.
 */
function storm_trash_delete_node_confirm(&$form_state, $node) {
 $form['nid'] = array('#type' => 'value', '#value' => $node->nid);

 return confirm_form($form,
   t('Are you sure you want to permanently delete %title?', array('%title' => $node->title)),
   isset($_GET['destination']) ? $_GET['destination'] : 'node/' . $node->nid,
   t('This action cannot be undone.'),
   t('Delete'), t('Cancel'));
}

/**
 * Permanently delete node confirmation form submit handler.
 */
function storm_trash_delete_node_confirm_submit($form, &$form_state) {
  if ($form_state['values']['confirm']) {
    // Delete node comments (if apply):
    if (module_exists('nodecomment')) {
      _nodecomment_delete_comments($form_state['values']['nid']);
    }
    // Delete the node:
    node_delete($form_state['values']['nid']);
  }
  // Go to trash list:
  $form_state['redirect'] = 'trash';
}

/**
 * Menu callback for 'trash' page.
 */
function storm_trash_list() {
  $breadcrumb = array();
  $breadcrumb[] = l(t('SuiteDesk'), 'dashboard');
  drupal_set_breadcrumb($breadcrumb);

  $output = '';
  $header = array(
    array('data' => t('Title'), 'field' => 'n.title'),
    array('data' => t('Type'), 'field' => 'n.type'),
    array('data' => t('Deleted on'), 'field' => 'n.changed', 'sort' => 'desc'),
    array('data' => t('Log')),
    array('data' => NULL),
  );

  $count_sql = db_rewrite_sql("SELECT count(n.nid) FROM {node} n WHERE status = " . STATUS_TRASH);
  $node_sql = db_rewrite_sql("SELECT n.nid,n.type,n.title,n.changed,u.uid,u.name,r.log FROM {node} n JOIN {users} u ON n.uid = u.uid JOIN {node_revisions} r ON n.vid = r.vid WHERE n.status = " . STATUS_TRASH . tablesort_sql($header));

  $itemsperpage = isset($_SESSION['stormtrash_list_filter']['itemsperpage']) ? $_SESSION['stormtrash_list_filter']['itemsperpage'] : variable_get('storm_default_items_per_page', 10);

  $result = pager_query($node_sql, $itemsperpage, 0, $count_sql);

  $nodes = array();
  while($node = db_fetch_object($result)) {
    $nodes[] = $node;
  }
  if (count($nodes)) {
    $node_types = node_get_types('names');
    $rows = array();
    foreach($nodes as $node) {
      $rows[] = array(
        l($node->title, "node/$node->nid"),
        t($node_types[$node->type]),
        t('!date by !username', array(
          '!date' => format_date($node->changed, 'small'),
          '!username' => theme('username', $node))),
        $node->log,
        array(
          'data' => storm_icon_restore_node($node, $_GET) . storm_icon_delete_perm_node($node, $_GET),
          'class' => 'storm_list_operations',
        ),
      );
    }
    $output .= theme('table', $header, $rows, array('id' => 'stormtrash'));
    $output .= theme('pager', NULL, $itemsperpage, 0);
    if (user_access('Storm: purge trash')) {
      $output .= '<p>' . l(t('Empty trash'), "trash/empty") . '</p>';
    }
  } else {
    $output .= '<p>' . t('The trash is empty.') . '</p>';
  }
  return $output;
}

/**
 * Empty trash confirmation form.
 */
function storm_empty_trash_confirm(&$form_state) {
 return confirm_form($form,
   t('Are you sure you want to empty the trash?', array('%title' => $node->title)),
   isset($_GET['destination']) ? $_GET['destination'] : 'trash',
   t('This action cannot be undone. Only contents with delete permission will be deleted.'),
   t('Empty trash'), t('Cancel'));
}

/**
 * Empty trash confirmation form submit handler.
 */
function storm_empty_trash_confirm_submit($form, &$form_state) {
  if ($form_state['values']['confirm']) {
    $result = db_query("SELECT n.nid FROM {node} n WHERE n.status = %d", STATUS_TRASH);
    while ($node = db_fetch_object($result)) {
      node_delete($node->nid);
    }
  }
  $form_state['redirect'] = 'trash';
}

/**
 * Implementation of hook_block().
 */
function storm_block($op = 'list', $delta = 0) {
  switch ($op) {
    // Declare the list of blocks to create:
    case 'list' :
      $blocks = array();
      $blocks['storm_dashboard_block']['info'] = t('SuiteDesk dashboard block');
      if (module_exists('recently_read')) {
        $blocks['storm_recent_nodes']['info'] = t('Recent SuiteDesk nodes');
      }

      foreach (node_get_types('names') as $type => $name) {
        if (variable_get('stormblock_expose_'. $type, 0)) {
          $blocks[$type] = array(
            'info' => t('@name form block', array('@name' => $name)),
            'cache' => BLOCK_NO_CACHE,
          );
        }
      }

      return $blocks;

    // Assign the content for the listed block:
    case 'view':
      switch ($delta) {
        case 'storm_dashboard_block':
          $block = array(
            'subject' => t('Shortcuts'),
            'content' => storm_dashboard('block'),
          );
          return $block;

        case 'storm_recent_nodes':
          $block = array(
            'subject' => t('Recent hits on nodes'),
            'content' => storm_block_recent_nodes(),
          );
          return $block;

        default:
          return storm_get_block($delta);
      }
      return NULL;
  }
}

/**
 * Generate a block containing a node entry form.
 */
function storm_get_block($type) {
  if (node_access('create', $type) && variable_get('stormblock_expose_'. $type, 0)) {
    // Include page handler for node_add()
    module_load_include('inc', 'node', 'node.pages');
    // Note title before rendering of form.
    $title = drupal_get_title();
    $form = node_add($type);
    // Restore title, which will have been overridden.
    drupal_set_title($title);
    $node_type = node_get_types('type', $type);
    $help = '';
    if (variable_get('stormblock_show_help_'. $type, 0)) {
      $help = (!empty($node_type->help) ? '<p>'. filter_xss_admin($node_type->help) .'</p>' : '');
    }
    return array(
      'subject' => t('@type form', array('@type' => $node_type->name)),
      'content' => $help . $form,
    );
  }
}

/**
 * Process callback to add dependency to form items.
 */ /*
function storm_dependent_process($element, $edit, &$form_state, &$form) {
  if (isset($element['#dependency'])) {
    if (!isset($element['#dependency_count'])) {
      $element['#dependency_count'] = 1;
    }
    if (!isset($element['#dependency_type'])) {
      $element['#dependency_type'] = 'hide';
    }

    $js = array(
      'values' => $element['#dependency'],
      'num' => $element['#dependency_count'],
      'type' => $element['#dependency_type'],
    );

    if (!empty($form_state['ajax'])) {
      $form_state['js settings']['stormblock']['dependent'][$element['#id']] = $js;
    }
    else {
      $path = drupal_get_path('module', 'storm');
      drupal_add_js($path .'/storm.js', 'module', 'header');
      $options['stormblock']['dependent'][$element['#id']] = $js;
      drupal_add_js($options, 'setting');
    }
  }

  return $element;
} */


function storm_block_recent_nodes() {
  global $user;

  $content = '';
  $count_items_1 = 0;
  $count_items_2 = 0;

  if (module_exists('recently_read')) {
    // Disable page caching if this block is displayed:
    recently_read_disable_page_cache();

    $items = recently_read_get_read_items(array(
      'stormorganization',
      'stormperson',
      'stormproject',
      'stormtask',
      'stormticket',
      'stormtimetracking',
      'stormexpense',
      'storminvoice',
      'stormevent'), $user->uid, 14);
    $count_items_1 = count($items);
    if ($count_items_1) {
      $content .= theme('recently_read_item_list', $items);
    }

    $items = recently_read_get_read_items(array('stormdok', 'stormnote'), $user->uid, 14);
    $count_items_2 = count($items);
    if ($count_items_2) {
      $content .= theme('recently_read_item_list', $items);
    }
  }
  if ($count_items_1 + $count_items_2 == 0) {
    $content = '<p style="margin: 3px 12px;">' . t('Nothing has been read yet.') . '</p>';
  }
  return $content;
}

/**
 * @function
 * Defines the administration settings form for the SuiteDesk module
 */
function storm_admin_settings() {
  $form = array();
  $w = -10;

  $form['icons'] = array(
    '#type' => 'fieldset',
    '#title' => t('Icons'),
    '#collapsed' => TRUE,
    '#collapsible' => TRUE,
    '#weight' => $w++,
  );

  $form['icons']['storm_icons_display'] = array(
    '#type' => 'checkbox',
    '#title' => t('Display SuiteDesk icons'),
    '#default_value' => variable_get('storm_icons_display', TRUE),
    '#description' => t('The icons that ship with SuiteDesk may not fit well with some themes. If this box is unchecked, icons will be hidden.'),
    '#weight' => $w++,
  );

  $form['icons']['storm_icons_path'] = array(
    '#type' => 'textfield',
    '#title' => t('Icons directory'),
    '#default_value' => variable_get('storm_icons_path', drupal_get_path('module', 'storm') .'/icons'),
    '#description' => t("The directory that contains SuiteDesk's icons."),
    '#weight' => $w++,
    '#element_validate' => array('storm_admin_settings_icons_path_validate'),
  );

  $form['lists'] = array(
    '#type' => 'fieldset',
    '#title' => t('Lists'),
    '#collapsed' => TRUE,
    '#collapsible' => TRUE,
    '#weight' => $w++,
  );

  $form['lists']['storm_default_items_per_page'] = array(
    '#type' => 'textfield',
    '#title' => t('Default Items per Page'),
    '#default_value' => variable_get('storm_default_items_per_page', 10),
    '#description' => t('Default items per page when viewing lists'),
    '#size' => 5,
    '#weight' => $w++
  );

  $form['reports'] = array(
    '#type' => 'fieldset',
    '#title' => t('Reports'),
    '#collapsed' => TRUE,
    '#collapsible' => TRUE,
    '#weight' => $w++,
  );

  $form['reports']['storm_report_header'] = array(
    '#type' => 'textarea',
    '#title' => t('Report header'),
    '#default_value' => variable_get('storm_report_header', ''),
    '#description' => t('The header that will appear on SuiteDesk reports'),
    '#weight' => $w++,
  );

  $form['yearsrange'] = array(
    '#type' => 'fieldset',
    '#title' => t('Years range in dates'),
    '#collapsed' => TRUE,
    '#collapsible' => TRUE,
    '#weight' => $w++,
  );

  $form['yearsrange']['group0'] = array(
    '#type' => 'markup',
    '#theme' => 'storm_form_group',
    '#weight' => $w++,
  );

  $form['yearsrange']['group0']['storm_yearsrangebegin'] = array(
    '#type' => 'select',
    '#title' => t('Begin'),
    '#options' => drupal_map_assoc(range(1970, 2037)),
    '#default_value' => variable_get('storm_yearsrangebegin', 2001),
  );

  $form['yearsrange']['group0']['storm_yearsrangeend'] = array(
    '#type' => 'select',
    '#title' => t('End'),
    '#options' => drupal_map_assoc(range(1970, 2037)),
    '#default_value' => variable_get('storm_yearsrangeend', 2015),
  );

  $form['taxation'] = array(
    '#type' => 'fieldset',
    '#title' => t('Taxation defaults'),
    '#collapsed' => TRUE,
    '#collapsible' => TRUE,
    '#weight' => $w++,
  );

  $form['taxation']['storm_tax1_app'] = array(
    '#type' => 'select',
    '#title' => t('Tax 1: Application'),
    '#default_value' => variable_get('storm_tax1_app', 1),
    '#description' => t('The method of application to use for Tax 1'),
    '#options' => array(
      1 => t('Apply to item amount'),
      0 => t('Do not apply tax'),
    ),
    '#weight' => $w++,
  );

  $form['taxation']['group0'] = array(
    '#type' => 'markup',
    '#theme' => 'storm_form_group',
    '#weight' => $w++,
  );

  $form['taxation']['group0']['storm_tax1_name'] = array(
    '#type' => 'textfield',
    '#title' => t('Tax 1: Name'),
    '#default_value' => variable_get('storm_tax1_name', 'VAT'),
    '#description' => t('The name to use for Tax 1'),
    '#weight' => $w++,
  );

  $form['taxation']['group0']['storm_tax1_percent'] = array(
    '#type' => 'textfield',
    '#title' => t('Tax 1: Default percentage'),
    '#default_value' => variable_get('storm_tax1_percent', 20),
    '#description' => t('Default percentage for Tax 1'),
    '#size' => 20,
    '#weight' => $w++
  );

  $form['taxation']['storm_tax2_app'] = array(
    '#type' => 'select',
    '#title' => t('Tax 2: Application'),
    '#default_value' => variable_get('storm_tax2_app', 0),
    '#description' => t('The method of application to use for Tax 2'),
    '#options' => array(
      2 => t('Apply to total of item amount plus previous tax'),
      1 => t('Apply to item amount'),
      0 => t('Do not apply tax'),
    ),
    '#weight' => $w++,
  );

  $form['taxation']['group1'] = array(
    '#type' => 'markup',
    '#theme' => 'storm_form_group',
    '#weight' => $w++,
  );

  $form['taxation']['group1']['storm_tax2_name'] = array(
    '#type' => 'textfield',
    '#title' => t('Tax 2: Name'),
    '#default_value' => variable_get('storm_tax2_name', 'Tax 2 Name'),
    '#description' => t('The name to use for Tax 2'),
    '#weight' => $w++,
  );

  $form['taxation']['group1']['storm_tax2_percent'] = array(
    '#type' => 'textfield',
    '#title' => t('Tax 2: Default percentage'),
    '#default_value' => variable_get('storm_tax2_percent', 20),
    '#description' => t('Default percentage for Tax 2'),
    '#size' => 20,
    '#weight' => $w++,
  );

  $form['taxation']['storm_tax_display'] = array(
   '#type' => 'checkbox',
   '#title' => t('Display tax edit fields'),
   '#default_value' => variable_get('storm_tax_display', TRUE),
   '#description' => t('If disabled, all tax fields will use the default values and you will not be able to override them for any nodes until this setting is enabled again.'),
   '#weight' => $w++,
  );

  $form['taxation']['storm_tax2_display'] = array(
   '#type' => 'checkbox',
   '#title' => t('Display tax 2 edit fields'),
   '#default_value' => variable_get('storm_tax2_display', TRUE),
   '#description' => t('If disabled, tax 2 fields will use the default values and you will not be able to override them for any nodes until this setting is enabled again. This setting will be ignored unless the general "Display tax edit fields" setting is enabled above.'),
    '#weight' => $w++,
  );

  // DASHBOARD SETTINGS
  $form['storm_dashboard_settings'] = array(
    '#type' => 'fieldset',
    '#title' => t('Dashboard settings'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
  );

  $types = module_invoke_all('storm_dashboard_types');
  foreach ($types as $type => $type_data) {
    $all_links_options = array();
    $all_links = storm_dashboard_get_links(FALSE, $type);
    foreach ($all_links as $link) {
      $all_links_options[$link['path']] = l($link['title'], $link['path']);
    }

    $default_dashboard_settings = variable_get('storm_'. $type .'dashboard_settings', array());

    $form['storm_dashboard_settings'][$type] = array(
      '#type' => 'fieldset',
      '#title' => t($type_data['title']),
      '#description' => $type_data['description'],
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
    );
    $form['storm_dashboard_settings'][$type]['dashboard_links'] = array(
      '#theme' => 'storm_dashboard_links_weight_table',
      '#infix' => $type,
    );

    $weight = 0;
    foreach ($all_links_options as $path => $title) {
      $form['storm_dashboard_settings'][$type]['dashboard_links'][$path][$type .'_storm_dashboard_link_active_'. $path] = array(
        '#type' => 'checkbox',
        '#default_value' => isset($default_dashboard_settings[$path]['active']) ? $default_dashboard_settings[$path]['active'] : TRUE,
      );
      $form['storm_dashboard_settings'][$type]['dashboard_links'][$path][$type .'_storm_dashboard_link_weight_'. $path] = array(
        '#type' => 'weight',
        '#default_value' => isset($default_dashboard_settings[$path]['weight']) ? $default_dashboard_settings[$path]['weight'] : $weight,
        '#delta' => 30,
      );
      $form['storm_dashboard_settings'][$type]['dashboard_links'][$path]['#value'] = $title;
      $weight++;
    }
  }

  if (empty($form['#submit'])) {
    $form['#submit'] = array();
  }
  $form['#submit'] = array('storm_admin_settings_form_submit');

  return system_settings_form($form);
}

/**
 * Validate Icon Path form element.
 *
 * @param $form
 * @param $form_values
 */
function storm_admin_settings_icons_path_validate($form, $form_values) {
  $icon_path_old = variable_get('storm_icons_path', drupal_get_path('module', 'storm') .'/icons');
  $icon_path = $form_values['values']['storm_icons_path'];
  // check if directory exists
  if (!is_dir($icon_path)) {
    form_set_error('storm_icons_path', t('Icon path %path does not exist', array('%path' => $icon_path)));
  }
  // if it exists and changed, delete cache, so there are no old values in the cache
  elseif ($icon_path != $icon_path_old) {
    cache_clear_all('storm:icons', 'cache', FALSE);
  }
}

function storm_admin_settings_form_submit($form, $form_state) {
  $op = isset($form_state['values']['op']) ? $form_state['values']['op'] : '';
  // RESET
  if ($op == t('Reset to defaults')) {
    $types = module_invoke_all('storm_dashboard_types');
    foreach ($types as $type => $type_data) {
      variable_del('storm_'. $type .'dashboard_settings');
    };
    return;
  }

  // GET OPTIONS
  $types = module_invoke_all('storm_dashboard_types');
  foreach ($types as $type => $type_data) {
    $all_links = storm_dashboard_get_links(FALSE, $type);
    $settings = array();
    foreach ($all_links as $link) {
      $path = $link['path'];
      $settings[$path]['active'] = $form_state['values'][$type .'_storm_dashboard_link_active_'. $path];
      $settings[$path]['weight'] = $form_state['values'][$type .'_storm_dashboard_link_weight_'. $path];
    }
    variable_set('storm_'. $type .'dashboard_settings', $settings);
  }

}

// DATE / TIME MANIPULATION
function storm_elements() {
  $type['datetime'] = array(
    '#input' => TRUE,
    '#process' => array('storm_datetime_expand'),
    '#element_validate' => array('storm_datetime_validate'),
    '#default_value' => array(
      'day' => format_date(time(), 'custom', 'j'),
      'month' => format_date(time(), 'custom', 'n'),
      'year' => format_date(time(), 'custom', 'Y'),
      'hour' => format_date(time(), 'custom', 'H'),
      'minute' => format_date(time(), 'custom', 'i'),
    ),
  );
  $type['dateext'] = array(
    '#input' => TRUE,
    '#process' => array('storm_dateext_expand'),
    '#element_validate' => array('storm_dateext_validate'),
    '#default_value' => time(),
    '#withnull' => FALSE,
    '#disable_date_popup' => FALSE,
  );
  return $type;
}

function storm_datetime_expand($element) {
  if (empty($element['#value'])) {
    $element['#value'] = array(
      'day' => format_date(time(), 'custom', 'j'),
      'month' => format_date(time(), 'custom', 'n'),
      'year' => format_date(time(), 'custom', 'Y'),
      'hour' => format_date(time(), 'custom', 'H'),
      'minute' => format_date(time(), 'custom', 'i'),
    );
  }

  $element['#tree'] = TRUE;

  // Determine the order of day, month, year in the site's chosen date format.
  $format = variable_get('date_format_short', 'm/d/Y - H:i');
  $sort = array();
  $sort['day'] = max(strpos($format, 'd'), strpos($format, 'j'));
  $sort['month'] = max(strpos($format, 'm'), strpos($format, 'M'));
  $sort['year'] = strpos($format, 'Y');
  $sort['hour'] = strpos($format, 'H');
  $sort['minute'] = strpos($format, 'i');
  asort($sort);
  $order = array_keys($sort);

  // Output multi-selector for date.
  foreach ($order as $type) {
    switch ($type) {
      case 'year':
        $options = drupal_map_assoc(range(variable_get('storm_yearsrangebegin', 2001), variable_get('storm_yearsrangeend', 2012)));
        break;
      case 'month':
        $options = drupal_map_assoc(range(1, 12), 'map_month');
        break;
      case 'day':
        $options = drupal_map_assoc(range(1, 31));
        break;
      case 'hour':
        $options = drupal_map_assoc(range(0, 23));
        break;
      case 'minute':
        $options = drupal_map_assoc(range(0, 59));
        break;
    }
    $parents = $element['#parents'];
    $parents[] = $type;
    $element[$type] = array(
      '#type' => 'select',
      '#value' => $element['#value'][$type],
      '#attributes' => $element['#attributes'],
      '#options' => $options,
    );
  }
  return $element;
}

function storm_datetime_validate($form) {
  if (!checkdate($form['#value']['month'], $form['#value']['day'], $form['#value']['year'])) {
    form_error($form, t('The specified date is invalid.'));
  }
}

function storm_dateext_expand($element, $edit, $form_state, $form) {
  if (empty($element['#value'])) {
    if (!$element['#withnull']) {
      $element['#value'] = array(
        'day' => format_date(time(), 'custom', 'j'),
        'month' => format_date(time(), 'custom', 'n'),
        'year' => format_date(time(), 'custom', 'Y'),
      );
    }
    else {
      $element['#value'] = array('day' => -1, 'month' => -1, 'year' => -1);
    }
  }

  $element['#tree'] = TRUE;

  // If date popup exists, we should use date popup,
  // but you can force to disable it by set disable_date_popup to true
  if (module_exists('date_popup') && (!isset($element['disable_date_popup']) || !$element['disable_date_popup'])) {

    // value is timestamp
    if (is_numeric($element['#value']) && $element['#value'] != 0) {
      $element['#value'] = array('popup' => format_date($element['#value'], 'custom', 'Y-m-d'));
    }
    // value is date array
    elseif (is_array($element['#value']) && !isset($element['#value']['popup']) && $element['#value']['day'] != -1) {
      $element['#value'] = array('popup' => sprintf("%04d-%02d-%02d", $element['#value']['year'], $element['#value']['month'], $element['#value']['day']));
    }
    elseif (is_numeric($element['#value']) || !isset($element['#value']['popup']) || (isset($element['#value']['day']) && $element['#value']['day'] == -1)) {
      $element['#value'] = array('popup' => '');
    }

    if (is_array($element['#value']['popup']) && isset($element['#value']['popup']['date'])) {
      $value = $element['#value']['popup']['date'];
    }
    else {
      $value = $element['#value']['popup'];
    }

    $current_year = date('Y');
    $begin = variable_get('storm_yearsrangebegin', 2001) - $current_year;
    if ($begin > 0) {
      $begin = '+'. $begin;
    }
    $end = variable_get('storm_yearsrangeend', 2015) - $current_year;
    if ($end > 0) {
      $end = '+'. $end;
    }

    $parents = $element['#parents'];
    $parents[] = 'popup';
    $element['popup'] = array(
      '#type' => 'date_popup',
      '#date_timezone' => date_default_timezone_name(),
      '#date_format' => "Y-m-d",
      '#date_year_range' => $begin .':'. $end,
      '#default_value' => $value,
      '#attributes' => $element['#attributes'],
      '#withnull' => $element['#withnull'],
    );
  }
  // fallback - use select boxes to choose the date
  else {

    // its a timestamp, convert it to a date format
    if (is_numeric($element['#value']) && $element['#value'] != 0) {
      $element['#value'] = _storm_gmtimestamp_to_date($element['#value']);
    }

    // Determine the order of day, month, year in the site's chosen date format.
    $format = variable_get('date_format_short', 'm/d/Y - H:i');
    $sort = array();
    $sort['day'] = max(strpos($format, 'd'), strpos($format, 'j'));
    $sort['month'] = max(strpos($format, 'm'), strpos($format, 'M'));
    $sort['year'] = strpos($format, 'Y');
    asort($sort);
    $order = array_keys($sort);

    // Output multi-selector for date.
    foreach ($order as $type) {
      switch ($type) {
        case 'year':
          $options = drupal_map_assoc(range(variable_get('storm_yearsrangebegin', 2001), variable_get('storm_yearsrangeend', 2012)));
          break;
        case 'month':
          $options = drupal_map_assoc(range(1, 12), 'map_month');
          break;
        case 'day':
          $options = drupal_map_assoc(range(1, 31));
          break;
      }
      if ($element['#withnull']) {
        $options = array('-1' => '-') + $options;
      }

      if (empty($element['#attributes'])) {
        $element['#attributes'] = array();
      }

      $parents = $element['#parents'];
      $parents[] = $type;
      $element[$type] = array(
        '#type' => 'select',
        '#value' => isset($element['#value'][$type]) ? $element['#value'][$type] : NULL,
        '#options' => $options,
        '#attributes' => array_merge(array('onchange' => "storm_datext_tonull(this, '". $element['#id'] ."')"), $element['#attributes']),
      );
    }
  }
  return $element;
}

function storm_dateext_validate($element, &$form_state) {
  // value is a string, convert it back to array
  if (is_array($element['#value']) && isset($element['#value']['popup'])) {
    if (!$element['#withnull'] && empty($element['#value']['popup']['date'])) {
      form_set_error($element, t('Field %field is required.', array('%field' => !empty($element['#title']) ? $element['#title'] : '')));
    }
#   if (preg_match('/(\d{4})-(\d{2})-(\d{2})/', $element['#value']['popup']['date'], $matches)) {
    if (isset($element['#value']['popup']['date']) && preg_match('/(\d{4})-(\d{2})-(\d{2})/', $element['#value']['popup']['date'], $matches)) {
      $element['#value'] = array();
      $element['#value']['day'] = (int) $matches[3];
      $element['#value']['month'] = (int) $matches[2];
      $element['#value']['year'] = (int) $matches[1];
    }
    else {
      if (!$element['#withnull'] && !empty($element['#value']['popup']['date'])) {
        form_set_error($element, t('Wrong Format for Field %field. Format should be YYYY-MM-DD.', array('%field' => !empty($element['#title']) ? $element['#title'] : '')));
      }
      $element['#value'] = array('day' => -1, 'month' => -1, 'year' => -1);
    }
    form_set_value($element, $element['#value'], $form_state);
  }

  if ($element['#value']['day'] == -1 && !$element['#withnull']) {
    form_set_error($element, t('Field %field is required.', array('%field' => !empty($element['#title']) ? $element['#title'] : '')));
  }
  if ($element['#value']['day'] != -1 && !checkdate($element['#value']['month'], $element['#value']['day'], $element['#value']['year'])) {
    form_error($element, t('The specified date is invalid.'));
  }
  return $element;
}

function storm_dependent_select_process($form, $edit, $form_state, $complete_form) {
  unset($form['#needs_validation']);
  return $form;
}

function _timestamp_to_gm($timestamp, $timezone=NULL) {
  if (!isset($timezone)) {
    global $user;
    if (variable_get('configurable_timezones', 1) && $user->uid && drupal_strlen($user->timezone)) {
      $timezone = $user->timezone;
    }
    else {
      $timezone = variable_get('date_default_timezone', 0);
    }
  }
  $timestamp -= $timezone;
  return $timestamp;
}

function _storm_date_to_gmtimestamp($date, $timezone=NULL) {
  if ($date['month'] == -1 || $date['year'] == -1 || $date['day'] == -1) {
    return NULL;
  }
  else {
    $gmttimestamp = gmmktime(0, 0, 0, intval($date['month']), intval($date['day']), intval($date['year']));
    return _timestamp_to_gm($gmttimestamp, $timezone);
  }
}

function _storm_datetime_to_gmtimestamp($datetime, $timezone=NULL) {
  $gmttimestamp = gmmktime(intval($datetime['hour']), intval($datetime['minute']), 0, intval($datetime['month']),
  intval($datetime['day']), intval($datetime['year']));
  return _timestamp_to_gm($gmttimestamp, $timezone);
}

function _storm_gmtimestamp_to_datetime($timestamp, $timezone=NULL) {
  $datetime = array(
    'day' => format_date($timestamp, 'custom', 'j', $timezone),
    'month' => format_date($timestamp, 'custom', 'n', $timezone),
    'year' => format_date($timestamp, 'custom', 'Y', $timezone),
    'hour' => (int)format_date($timestamp, 'custom', 'H', $timezone),
    'minute' => (int)format_date($timestamp, 'custom', 'i', $timezone),
  );
  return $datetime;
}

function _storm_gmtimestamp_to_date($timestamp, $timezone=NULL) {
  if ($timestamp) {
    $date = array(
      'day' => format_date($timestamp, 'custom', 'j', $timezone),
      'month' => format_date($timestamp, 'custom', 'n', $timezone),
      'year' => format_date($timestamp, 'custom', 'Y', $timezone),
    );
  }
  else {
    $date = array(
      'day' => -1,
      'month' => -1,
      'year' => -1,
    );
  }

  return $date;
}

function _storm_gmtimestamp_without_time($timestamp, $timezone=NULL) {
  $date = _storm_gmtimestamp_to_date($timestamp, $timezone);
  $gmttimestamp = gmmktime(0, 0, 0, $date['month'], $date['day'], $date['year']);
  return _timestamp_to_gm($gmttimestamp, $timezone);
}

function _storm_strtotime($timestr='') {
  $timestr = drupal_substr($timestr, 0, 5);
  $time = array();
  $time['hour'] = 0;
  $time['minute'] = 0;

  $ar = explode(':', $timestr);
  if (is_array($ar)) {
    if (array_key_exists(0, $ar)) $time['hour'] = $ar[0];
    if (array_key_exists(1, $ar)) $time['minute'] = $ar[1];
  }
  return $time;
}

function _timetostr($time=array()) {
  $timestr = str_pad($time['hour'], 2, "0", STR_PAD_LEFT) .':'. str_pad($time['minute'], 2, "0", STR_PAD_RIGHT);
  return $timestr;
}


/**
 * @function
 * Calculates taxation for SuiteDesk nodes
 */
function storm_taxation(&$node) {
  // If values are not set, then use default values
  if (!isset($node->tax1app)) {
    $node->tax1app = variable_get('storm_tax1_app', 'none');
  }
  if (!isset($node->tax1percent)) {
    $node->tax1percent = variable_get('storm_tax1_percent', '20');
  }
  if (!isset($node->tax2app)) {
    $node->tax2app = variable_get('storm_tax2_app', 'none');
  }
  if (!isset($node->tax2percent)) {
    $node->tax2percent = variable_get('storm_tax2_percent', '20');
  }

  switch ($node->tax1app) {
    case 0:
      $node->tax1 = 0;
      break;
    case 1:
      $node->tax1 = $node->amount * $node->tax1percent / 100;
      break;
    default:
      // ERROR
      drupal_set_message(t('Error during tax calculations (SuiteDesk module)'), 'error');
  }

  $node->subtotal = $node->amount + $node->tax1;

  switch ($node->tax2app) {
    case 0:
      $node->tax2 = 0;
      break;
    case 1:
      $node->tax2 = $node->amount * $node->tax2percent / 100;
      break;
    case 2:
      $node->tax2 = $node->subtotal * $node->tax2percent / 100;
      break;
    default:
      // ERROR
      drupal_set_message(t('Error during tax calculations (SuiteDesk module)'), 'error');
  }

  $node->total = $node->subtotal + $node->tax2;
}

// STORM ICON ADD / EDIT / LINK FUNCTIONS
function storm_icon_add_node($node, $params=array()) {
  return storm_icon_add('node/add/'. $node->type, $node, $params);
}

function storm_icon_edit_node($node, $params=array()) {
  return storm_icon_edit('node/'. $node->nid .'/edit', $node, $params);
}

function storm_icon_comment_node($node, $params=array()) {
  return storm_icon_comment('node/add/'. $node->type .'c/'. $node->nid, $node, $params);
}

function storm_icon_delete_node($node, $params=array()) {
  return storm_icon_delete('node/'. $node->nid .'/delete', $node, $params);
}

function storm_icon_restore_node($node, $params=array()) {
  $params['q'] = drupal_get_path_alias('node/' . $node->nid);
  return storm_icon_restore('trash/'. $node->nid .'/restore', $node, $params);
}

function storm_icon_delete_perm_node($node, $params=array()) {
  return storm_icon_delete_perm('trash/'. $node->nid .'/delete', $node, $params);
}

function storm_icon_gantt_project($project, $params=array()) {
  return storm_icon_gantt('node/'. $project->nid .'/tasks', $project, $params);
}

function storm_icon_gantt_task($task, $params=array()) {
  return storm_icon_gantt('node/'. $task->project_nid .'/tasks', $task, $params);
}

function storm_icon_add($path, $item, $params=array()) {
  global $user;
  if (!node_access('create', $item, $user)) return '';
  $attributes = array('class' => 'popups-form');
  return storm_icon_l('application_add', $path, t('Add'), '', $params, $attributes);
}

function storm_icon_edit($path, $item, $params=array()) {
  global $user;
  if (!node_access('update', $item, $user)) return '';
  $attributes = array('class' => 'popups-form');
  return storm_icon_l('application_edit', $path, t('Edit'), '', $params, $attributes);
}

function storm_icon_comment($path, $item, $params=array()) {
  global $user;
  if (!user_access('create '. $item->type .'c content')) return '';
  $attributes = array('class' => 'popups-form');
  return storm_icon_l('application_comment', $path, t('Add comment'), '', $params, $attributes);
}

function storm_icon_delete($path, $item, $params=array()) {
  global $user;
  if (!node_access('delete', $item, $user)) return '';
  $attributes = array('class' => 'popups-form');
  return storm_icon_l('application_delete', $path, t('Delete'), '', $params, $attributes);
}

function storm_icon_restore($path, $item, $params=array()) {
  global $user;
  if (!node_access('delete', $item, $user)) return '';
  $attributes = array('class' => 'popups-form');
  return storm_icon_l('application_restore', $path, t('Restore'), '', $params, $attributes);
}

function storm_icon_delete_perm($path, $item, $params=array()) {
  global $user;
  if (!node_access('delete', $item, $user)) return '';
  $attributes = array('class' => 'popups-form');
  return storm_icon_l('application_delete', $path, t('Delete permanently'), '', $params, $attributes);
}

function storm_icon_gantt($path, $item, $params=array()) {
  global $user;
  if (!user_access('Storm project: access')) return '';
  $attributes = array('class' => 'popups-form');
  return storm_icon_l('application_gantt', $path, t('Project tasks'), '', $params, $attributes);
}

function storm_icon_l($icon, $path, $title, $permission='', $params=array(), $attributes=array()) {
  if ($permission && !user_access($permission)) return '';
  $icon = storm_icon($icon, $title);
  $attributes ['title'] = $title;

  $query = '';

  if (array_key_exists('q', $params)) {
    $destination = $params['q'];
    unset($params['q']);
    $c = 0;
    if (array_key_exists('page', $params)) {
      $destination .= '?page='. $params['page'];
      unset($params['page']);
      $c++;
    }
    if (array_key_exists('sort', $params)) {
      if ($c) {
        $destination .= '&';
      }
      else {
        $destination .= '?';
      }
      $destination .= 'sort='. $params['sort'];
      unset($params['sort']);
      $c++;
    }
    if (array_key_exists('order', $params)) {
      if ($c) {
        $destination .= '&';
      }
      else {
        $destination .= '?';
      }
      $destination .= 'order='. $params['order'];
      unset($params['order']);
      $c++;
    }
    $query .= 'destination='. urlencode($destination);
    unset($params['destination']);
  }

  $params = _storm_icon_urlencode_helper($params);
  foreach ($params as $key => $value) {
    if ($query) $query .= '&';
    $query .= $key .'='. urlencode($value);
  }

  $o = l($icon, $path, array('attributes' => $attributes, 'query' => $query, 'html' => TRUE));
  return $o;
}

function _storm_icon_urlencode_helper($params, $org_key = "") {
  $new_params = array();
  foreach ($params as $key => $value) {
    if (!empty($org_key)) {
      $new_key = $org_key ."[". $key ."]";
    }
    else {
      $new_key = $key;
    }
    if (is_array($value)) {
      $new_params = array_merge(_storm_icon_urlencode_helper($value, $new_key), $new_params);
    }
    else {
      $new_params[$new_key] = $value;
    }
  }
  return $new_params;
}

function storm_icon($icon, $title) {
  global $base_path;

  // Running checkplain on these variables means that we can call storm_icon without further sanitising
  $icon = check_plain($icon);
  $title = check_plain($title);

  $icon = str_replace(' ', '_', $icon);

  if (variable_get('storm_icons_display', TRUE) == TRUE) {
    $available = cache_get('storm:icons');
    if (!is_object($available)) {
      // Cache miss
      $available = storm_icon_recache();
    }

    if (in_array($icon .'.png', $available->data)) {
      // Standard route - display normal image
      $img_src = $base_path . variable_get('storm_icons_path', drupal_get_path('module', 'storm') .'/icons') .'/'. $icon .'.png';
      $o = '<img src="'. $img_src .'" alt="'. $title .'" title="'. $title .'" />';
    }
    else {
      // Icon not found
      $o = storm_icon_default($icon, $title);
    }
  }
  else {
    // Icons set to not display
    $o = $title;
  }
  return $o;
}

function storm_icon_recache() {
  $available = array();

  // For PHP5, replace with scandir() function
  $dir = variable_get('storm_icons_path', drupal_get_path('module', 'storm') . '/icons');

  $files = file_scan_directory($dir, '.*');
  foreach ($files as $file) {
    $available[] = $file->basename;
  }

  cache_set('storm:icons', $available, 'cache', CACHE_TEMPORARY);
  $available = cache_get('storm:icons');
  return $available;
}

function storm_icon_default($icon, $title) {
  // For now, just return $title.
  // A future extension could be more intelligent using $icon.
  return $title;
}

function storm_rewrite_sql($sql, $where = array(), $join = array()) {
  $where = empty($where) ? '' : '(' . implode(') AND (', $where) . ')';
  $join = empty($join) ? '' : implode(' ', $join);

  if (!empty($where) || !empty($join)) {
    if (!empty($where)) {
      $new = "WHERE $where ";
    }
    $new = " $join $new";
    if (strpos($sql, 'WHERE')) {
      $sql = str_replace('WHERE', $new . 'AND (', $sql);
      $insert = ') ';
    }
    else {
      $insert = $new;
    }
    if (strpos($sql, 'GROUP')) {
      $replace = 'GROUP';
    }
    elseif (strpos($sql, 'HAVING')) {
      $replace = 'HAVING';
    }
    elseif (strpos($sql, 'ORDER')) {
      $replace = 'ORDER';
    }
    elseif (strpos($sql, 'LIMIT')) {
      $replace = 'LIMIT';
    }
    else {
      $sql .= $insert;
    }
    if (isset($replace)) {
      $sql = str_replace($replace, $insert . $replace, $sql);
    }
  }
# drupal_set_message(print_r($sql, TRUE));
  return $sql;
}

function storm_db_rewrite_sql($query, $primary_table, $primary_field, $args) {
  if (($primary_table == 'n' || $primary_table == 'node') && $primary_field == 'nid') {

    if (preg_match("/'storm_access'='storm_access'/", $query)) {
      return array();
    }

    global $user;
    $conditions = array();
    foreach (module_invoke_all('storm_rewrite_where_sql', $query, $primary_table, $user) as $condition) {
      if ($condition) {
        $conditions[] = $condition;
      }
    }

    $return = array();
    $where = '';
    if ($conditions) {
      switch ($GLOBALS['db_type']) {
        case 'mysql':
        case 'mysqli':
          $where  = '(';
          $where .= " CASE ${primary_table}.type ";
          foreach ($conditions as $condition) {
            $where .= $condition .' ';
          }
          $where .= ' ELSE 1 END ';
          $where .= ' )=1 ';
          $return['where'] = $where;
        break;
        case 'pgsql':
        break;
      }
    }
#   drupal_set_message(print_r($return, TRUE));
    return $return;
  }
}

/**
 * Get a list of people and teams for select boxes
 *
 * Params:
 *  $organization_nid
 *    Leave blank to get a list of all teams and persons, otherwise also provide
 *  $project_nid
 *    to get a limited list of teams and persons following the following logic:
 *    - If the project is assigned to a person, only that person is listed as an option
 *    - If the project is assigned to a team, all team members are listed as options
 *    - If the project is neither assigned to a person nor a team, all people that are
 *      assigned to the given origanization are listed as options
 *    - In addition, if the project is assigned to a manager, that person is also listed
 *    - Finally, look into all existing teams and list those teams that exclusively
 *      contain members that are already selected
 *    If $organization_nid is provided but $project_nid is omitted, then the logic is as
 *    above, just for all projects of the given organization.
 */
function storm_get_assignment_options($organization_nid = 0, $project_nid = 0) {
  $teams = t('Teams:');
  $people = t('People:');
  $options = array();
  if (!$organization_nid) {
    $options['all'] = t('- no filter -');
    $options['mine'] = t('- mine -');
  }
  $options['none'] = t('- unassigned -');
  if (module_exists('stormteam')) {
    $options[$teams] = array();
  }
  if (module_exists('stormperson')) {
    $options[$people] = array();
  }
  $add_org_people = TRUE;
  if ($organization_nid) {
    $add_org_people = FALSE;
    $manager = array();
    $projects = array();
    if ($project_nid) {
      $projects[] = $project_nid;
    }
    else {
      $sql  = "SELECT n.nid FROM {node} AS n
        INNER JOIN {stormproject} AS spr
          ON n.vid=spr.vid
        WHERE n.status=1
          AND n.type='stormproject'
          AND spr.organization_nid=%d";
      $sql = stormproject_access_sql($sql);
      $result = db_query(db_rewrite_sql($sql), $organization_nid);
      while ($project = db_fetch_object($result)) {
        $projects[] = $project->nid;
      }
      $add_org_people = TRUE;
    }
    foreach ($projects as $pid) {
      $project = node_load($pid);
      if ($project->manager_nid) {
        $manager[] = node_load($project->manager_nid);
      }
      if ($project->assigned_nid) {
        $node = node_load($project->assigned_nid);
        if ($node->type == 'stormperson') {
          if (module_exists('stormperson')) {
            $options[$people][$node->nid] = check_plain($node->title);
          }
        }
        else { // ($node->type == 'stormteam')
          if (module_exists('stormteam')) {
            $options[$teams][$node->nid] = check_plain($node->title);
            foreach ($node->members_array as $nid => $name) {
              // do not add deactivated persons
              if (!array_key_exists($nid, $node->members_deactivated_array)) {
                $options[$people][$nid] = check_plain($name);
              }
            }
          }
        }
      }
      else {
        $add_org_people = TRUE;
      }
    }
  }
  if ($add_org_people) {
    if (module_exists('stormperson')) {
      $where = (isset($organization_nid) && $organization_nid != 0) ? 'WHERE spe.organization_nid = %d' : '';
      $sql = "SELECT  spe.nid,
                      spe.fullname,
                      n.title
                  FROM {node} n
                  INNER JOIN {stormperson} spe
                    ON n.vid = spe.vid
                  $where
                  ORDER BY spe.fullname ASC";
      $sql = stormperson_access_sql($sql);
      $sql = db_rewrite_sql($sql);
      $result = db_query($sql, array($organization_nid));
      while ($person = db_fetch_object($result)) {
        $options[$people][$person->nid] = ($person->fullname) ? check_plain($person->fullname) : check_plain($person->title);
        if (empty($options[$people][$person->nid])) {
          $options[$people][$person->nid] = t('Person !nid', array('!nid' => $person->nid));
        }
      }
    }
  }
  else {
    if (isset($manager) && module_exists('stormperson')) {
      foreach ($manager as $manager_node) {
        if (!array_key_exists($manager_node->nid, $options[$people])) {
          $options[$people][$manager_node->nid] = check_plain($manager_node->title);
        }
      }
    }
  }
  if (module_exists('stormteam')) {
    $where = array();
    $args = array();
    $having = "";
    // do not add teams, which we already added
    if (!empty($options[$teams])) {
      $where[] = "ste.nid NOT IN (". db_placeholders($options[$teams]) .")";
      $args = array_merge($args, array_keys($options[$teams]));
    }
    // only add teams, which have at least the same members as persons we added so far
    if (!empty($organization_nid) && !empty($options[$people])) {
      // add all persons which should be in the team
      $where[] = "ste.mnid IN (". db_placeholders($options[$people]) .")";
      $args = array_merge($args, array_keys($options[$people]));
      // add a count, how many of the given persons should be in the other teams
      $having = " HAVING COUNT(ste.nid) = %d ";
      $args[] = count($options[$people]);
    }
    $sql = "SELECT n.nid, n.vid, n.title
                FROM {node} n
                INNER JOIN {stormteam} ste
                  ON n.vid = ste.vid
                WHERE n.type = 'stormteam'
                GROUP BY ste.nid". $having ."
                ORDER BY n.title ASC";
    $sql = stormteam_access_sql($sql, $where);
    $sql = db_rewrite_sql($sql);
    $result = db_query($sql, $args);
    while ($team = db_fetch_object($result)) {
      $options[$teams][$team->nid] = check_plain($team->title);
    }
  }
  if (isset($options[$people]) && array_key_exists(0, $options[$people])) {
    unset($options[$people][0]);
  }
  if (isset($options[$teams]) && array_key_exists(0, $options[$teams])) {
    unset($options[$teams][0]);
  }
  if (isset($options[$people]) && !sizeof($options[$people])) {
    unset($options[$people]);
  }
  if (isset($options[$teams]) && !sizeof($options[$teams])) {
    unset($options[$teams]);
  }

  // SORT OPTIONS
  if (!empty($options[$people]) && is_array($options[$people])) {
    asort($options[$people], SORT_LOCALE_STRING);
  }
  if (!empty($options[$teams]) && is_array($options[$teams])) {
    asort($options[$teams], SORT_LOCALE_STRING);
  }

  return $options;
}

/**
 * Helper function to count nodes of type destination module with parent nid of type source module.
 *
 * @param string $source_module A string of module where the request is comming from
 * @param string $destination_module A string with name of targeted module
 * @param int $nid node id of source module
 * @return ''|int
 *   empty string if can't be counted or the count of destination nodes in source node
 * @see theme_storm_link()
 */
function _storm_number_of_items($source_module, $destination_module, $nid) {
  global $user;
  $nmb_of_items = '';

  $valid_destination_modules = array();
  switch ($source_module) {
    case "stormorganization":
      $column_name = 'organization_nid';
      $valid_destination_modules = array('stormproject', 'stormtask', 'stormticket', 'stormtimetracking', 'stormevent', 'stormdok', 'stormnote', 'stormperson', 'stormexpense', 'storminvoice');
      break;

    case "stormproject":
      $column_name = 'project_nid';
      $valid_destination_modules = array('stormtask', 'stormticket', 'stormtimetracking', 'stormevent', 'stormdok', 'stormnote', 'stormexpense', 'storminvoice');
      break;

    case "stormtask":
      $column_name = 'task_nid';
      $valid_destination_modules = array('stormticket', 'stormtimetracking', 'stormevent', 'stormdok', 'stormnote', 'stormexpense');
      break;

    case "stormticket":
      $column_name = 'ticket_nid';
      $valid_destination_modules = array('stormtimetracking', 'stormexpense');
      break;
  }

  if (in_array($destination_module, $valid_destination_modules)) {
    switch ($destination_module) {
      case 'stormproject':
        $sql = "SELECT COUNT(*) FROM {node} AS n INNER JOIN {stormproject} AS spr ON n.vid=spr.vid WHERE n.status=1 AND
                n.type='stormproject' and spr.". $column_name ." = %d";
        $sql = stormproject_access_sql($sql);
        break;
      case 'stormtask':
        $sql = "SELECT COUNT(*) FROM {node} AS n INNER JOIN {stormtask} AS sta ON n.vid=sta.vid WHERE n.status=1 AND
                n.type='stormtask' and sta.". $column_name ." = %d";
        $sql = stormtask_access_sql($sql);
        break;
      case 'stormticket':
        $sql = "SELECT COUNT(*) FROM {node} AS n INNER JOIN {stormticket} AS sti ON n.vid=sti.vid WHERE n.status=1 AND
                n.type='stormticket' and sti.". $column_name ." = %d";
        $sql = stormticket_access_sql($sql);
        break;
      case 'stormtimetracking':
        $sql = "SELECT COUNT(*) FROM {node} AS n INNER JOIN {stormtimetracking} AS stt ON n.vid=stt.vid WHERE n.status=1 AND
                n.type='stormtimetracking' and stt.". $column_name ." = %d";
        $sql = stormtimetracking_access_sql($sql);
        break;
      case 'stormperson':
        $sql = "SELECT COUNT(*) FROM {node} AS n INNER JOIN {stormperson} AS spe ON n.vid=spe.vid WHERE n.status=1 AND
                n.type='stormperson' and spe.". $column_name ." = %d";
        $sql = stormperson_access_sql($sql);
        break;
      case 'stormevent':
        $sql = "SELECT COUNT(*) FROM {node} AS n INNER JOIN {stormevent} AS sev ON n.vid=sev.vid WHERE n.status=1 AND
                n.type='stormevent' and sev.". $column_name ." = %d";
        $sql = stormevent_access_sql($sql);
        break;
      case 'stormdok':
        $sql = "SELECT COUNT(*) FROM {node} AS n INNER JOIN {stormdok} AS sdo ON n.vid=sdo.vid WHERE n.status=1 AND
                n.type='stormdok' and sdo.". $column_name ." = %d";
        $sql = stormdok_access_sql($sql);
        break;
      case 'stormnote':
        $sql = "SELECT COUNT(*) FROM {node} AS n INNER JOIN {stormnote} AS sno ON n.vid=sno.vid WHERE n.status=1 AND
                n.type='stormnote' and sno.". $column_name ." = %d";
        $sql = stormnote_access_sql($sql);
        break;
      case 'stormexpense':
        $sql = "SELECT COUNT(*) FROM {node} AS n INNER JOIN {stormexpense} AS sex ON n.vid=sex.vid WHERE n.status=1 AND
                n.type='stormexpense' and sex.". $column_name ." = %d";
        $sql = stormexpense_access_sql($sql);
        break;
      case 'storminvoice':
        $sql = "SELECT COUNT(*) FROM {node} AS n INNER JOIN {storminvoice} AS sin ON n.vid=sin.vid WHERE n.status=1 AND
                n.type='storminvoice' and sin.". $column_name ." = %d";
        $sql = storminvoice_access_sql($sql);
        break;

    }
    $sql = db_rewrite_sql($sql);
    $nmb_of_items = db_result(db_query($sql, $nid));
  }
  return $nmb_of_items;
}

/**
 * Implements hook_views_pre_render to add the storm_icon_add
 *
 * @param $view
 */
function storm_views_pre_render(&$view) {
  if (isset($view->field['operation'])
    && $view->field['operation'] instanceof storm_handler_field_operation
    && isset($view->field['operation']->definition['type'])
    && $view->field['operation']->options['display_add_icon']
  ) {
    $i = new stdClass();
    $i->type = $view->field['operation']->definition['type'];
    if ($view->field['operation']->options['display_icons']) {
      $view->attachment_before .= '<span class="storm-icon-add-view">'. storm_icon_add_node($i, $_GET) .'</span>';
    }
    else {
      global $user;
      $type = $i->type;
      $af = $type .'_access';
      if ($af('create', $i, $user)) {
        $view->attachment_before .= '<span class="storm-icon-add-view">'. l(t('Add'), 'node/add/'. $type) .'</span>';
      }
    }
  }
}

function storm_attribute_access($op, $item=NULL, $account=NULL) {
  if (empty($account)) {
    global $user;
    $account = $user;
  }

  if ($op == 'create') {
    return user_access('Storm: access administration pages');
  }

  if ($op == 'delete') {
    return user_access('Storm: access administration pages');
  }

  if ($op == 'update') {
    return user_access('Storm: access administration pages');
  }

  return FALSE;
}

function storm_attributes_bydomain($domain) {
  static $attributes_cache = array();
  $attributes = array();

  if (array_key_exists($domain, $attributes_cache)) return $attributes_cache[$domain];

  $s = "SELECT * FROM {stormattribute} WHERE LOWER(domain) LIKE LOWER('%s') AND isactive=1 ORDER BY weight, avalue";
  $r = db_query($s, $domain);
  $attributes['values'] = array();
  while ($attribute = db_fetch_object($r)) {
    // The variable is deliberately passed through t() for translatability
    $attributes['values'][$attribute->akey] = t($attribute->avalue);
    if ($attribute->isdefault) {
      $attributes['default'] = $attribute->akey;
    }
  }
  if (is_array($attributes['values']) && !array_key_exists('default', $attributes)) {
    $v = array_flip($attributes['values']);
    $attributes['default'] = array_shift($v);
  }

  $attributes_cache[$domain] = $attributes;
  return $attributes;
}

function storm_attribute_value($domain, $key) {
  $attributes_array = storm_attributes_bydomain($domain);
  $attributes = $attributes_array['values'];
  if (array_key_exists($key, $attributes)) {
    return $attributes[$key];
  }
  return $key;
}

/**
 * Implements hook_cron().
 */
function storm_cron() {
  $send_last = variable_get('notify_send_last', 0);
  $send_interval = variable_get('notify_send', 86400);
  $send_hour = variable_get('notify_send_hour', 9);
  $send_start = time();

  if (($send_start - $send_last > $send_interval) &&
      (date('G', $send_start) >= $send_hour || $send_interval < 86400) &&
      ($send_interval != -1)) {  //special case of settings to send 'never'
    _storm_notify_send($send_start);
    variable_set('notify_send_last', $send_start);
  }
}

function _storm_notify_send($send_start = NULL) {
  $send_start = $send_start ? $send_start : time();
  $period = variable_get('notify_send_last', $send_start - variable_get('notify_send', 86400));

  $num_sent = 0;
  $num_failed = 0;

  // Store current user:
  _storm_switch_user();

  // Fetch all node type authorized by notify settings. Note that queries use
  // 'nn' alias to avoid conflicts when query is rewritten by access control
  // modules:
  $ntype = array();
  foreach (node_get_types() as $type => $name) {
    if (variable_get(NOTIFY_NODE_TYPE . $type, 0)) {
      $ntype[] = $type;
    }
    if (count($ntype) >= 1) {
      $nodetypes_query = "AND (nn.type = '" . implode("' OR nn.type = '", $ntype) . "') ";
    } else {
      $nodetypes_query = '';
    }
  }

  // Build query to fetch desired nodes:
  $q = 'SELECT nn.nid FROM {node} nn WHERE (nn.status = 1 OR nn.moderate = 1) ' . $nodetypes_query . ' AND ((nn.created > %d AND nn.created <= %d)';
  // Include updated nodes?
  $q .= variable_get('notify_include_updates', 1) ? ' OR (nn.changed > %d AND nn.changed <= %d))' : ')';
  $q .= ' ORDER BY nn.created ASC';

  // Run query and load the nodes (we check for individual user access later):
  _storm_switch_user(1);
  $nresult = db_query(db_rewrite_sql($q, 'nn'), $period, $send_start, $period, $send_start);
  $nodes = array();
  while ($node = db_fetch_object($nresult)) {
    $nodes[$node->nid] = node_load($node->nid);
  }

  // Fetch new comments:
  $comments = array();
  if (module_exists('comment')) {
    $cresult = db_query(db_rewrite_sql('SELECT c.nid, c.cid, c.subject, c.name, c.status FROM {comments} c INNER JOIN {node} nn ON c.nid = nn.nid WHERE c.timestamp > %d AND c.timestamp <= %d ' . $nodetypes_query . ' ORDER BY c.nid, c.timestamp', 'c'), $period, $send_start);
    while ($comment = db_fetch_object($cresult)) {
      $comments[$comment->nid][] = $comment;
    }
  }

  if (count($nodes) || count($comments)) {

    // Fetch users with notify enabled:
    $uresult = db_query('SELECT u.uid, u.name, u.mail, u.language, n.status, n.node, n.comment FROM {notify} n INNER JOIN {users} u ON n.uid = u.uid WHERE n.status = 1 AND u.status = 1 AND n.attempts <= %d', variable_get('notify_attempts', 5));

    while ($user = db_fetch_object($uresult)) {
      // Switch current user to this account to use node_access functions, etc.:
      _storm_switch_user($user->uid);

      $node_body = '';
      $comment_body = '';

      // Write new node content to e-mail if user has permissions:
      if ($user->node && user_access('access content') && count($nodes)) {
        $node_count = 0;
        foreach ($nodes as $node) {
          // Don't show node if we're not allowed to view it:
          if ($node->status == 1 && node_access('view', $node)) {
            $node_type = t(node_get_types('name', $node));
            $node_body .= '<li><a href="' . url('node/' . $node->nid, array('absolute' => TRUE)) . '" style="color: blue;"><strong>' . $node->title . '</strong></a><br />' .
                          '<em>' . t('!type, by !author.', array('!type' => $node_type, '!author' => ($node->name ? $node->name : variable_get('anonymous', 'Anonymous')))) . '</em></li>';
            $node_count++;
          }
        }
        if ($node_count > 0) {
          $node_body = '<p>' . t('Recent content - !count', array('!count' => format_plural($node_count, '1 new post', '@count new posts'))) . ':</p>' .
                       '<p><ol style="line-height: 1.4em; margin-left: 20px;">' . $node_body . '</ol></p>';
        }
      }

      // Write new comments to e-mail if user has permissions:
      if ($user->comment && user_access('access comments') && count($comments)) {
        $total_comment_count = 0;
        foreach ($comments as $nid => $comment) {
          // If we don't already have the node, fetch it:
          if (!isset($nodes[$nid])) {
            $nodes[$nid] = node_load($nid);
          }

          // Don't show comments if we're not allowed to view this node:
          if (node_access('view', $nodes[$nid])) {
            $comment_count = 0;
            $comment_nodes = '';
            foreach ($comment as $c) {
              //Determine whether to show comment status:
              if ($c->status == COMMENT_PUBLISHED && user_access('administer comments')) {
                $comment_nodes .= '<li>' . t('Comment !title by !author', array('!title' => $c->subject, '!author' => ($c->name ? $c->name : variable_get('anonymous', 'Anonymous')))) . '<br />' .
                                  '[ ' . url('node/' . $nid, array('fragment' => 'comment-' . $c->cid, 'absolute' => TRUE)) . ' ]</li>';
                $comment_count++;
                $total_comment_count++;
              }
            }
            if ($comment_count > 0) {
              $comment_body .= '<p>' . t('!count attached to !type posted by !author: !title', array('!count' => format_plural(count($comment), '1 new comment', '@count new comments'), '!title' => $nodes[$nid]->title, '!type' => node_get_types('name', $nodes[$nid]), '!author' => $nodes[$nid]->name ? $nodes[$nid]->name : variable_get('anonymous', 'Anonymous'))) . '</p>' .
                               '<p><ol style="line-height: 1.4em; margin-left: 20px;">' . $comment_nodes . '</ol></p>';
            }
          }
        }
        if ($total_comment_count > 0) {
          $comment_body = '<p>' . t('Recent comments - !count', array('!count' => format_plural($total_comment_count, '1 new comment', '@count new comments'))) . '</p>' . $comment_body;
        }
      }

      $body = $node_body . $comment_body;

      // If there was anything new, send mail:
      if ($body) {
        // Set up initial values for e-mail:
        $headers = array();
        if (!drupal_mail('storm', 'send', $user->mail, user_preferred_language($user), array('content' => $body), NULL, TRUE)) {
          $num_failed++;
          db_query('UPDATE {notify} SET attempts = attempts + 1 WHERE uid = %d', $user->uid);
          watchdog('notify', 'User %name (%mail) could not be notified. Mail error.', array('%name' => $user->name, '%mail' => $user->mail), WATCHDOG_ERROR);
        } else {
          $num_sent++;
          watchdog('notify', 'User %name (%mail) notified successfully.', array('%name' => $user->name, '%mail' => $user->mail), WATCHDOG_INFO);
        }
      }
    }
  }
  // Restore user:
  _storm_switch_user();

  return array($num_sent, $num_failed);
}

function storm_mail($key, &$message, $params) {
  global $user, $base_url;

  $sitename = '<strong>' . variable_get('site_name', 'Drupal') . '</strong>';
  $url_preferences = str_replace('http://', 'https://', url("user/$user->uid/notify", array('absolute' => TRUE)));

  $headers = array(
    'MIME-Version' => '1.0',
    'Content-Type' => 'text/html; charset=UTF-8; format=flowed',
    'Content-Transfer-Encoding' => '8Bit',
    'X-Mailer' => 'Drupal',
  );
  foreach ($headers as $key => $value) {
    $message['headers'][$key] = $value;
  }

  $message['subject'] = t('!sitename notification for !username', array('!sitename' => variable_get('site_name', 'Drupal'), '!username' => $user->name));

  $message['body'] = '<div style="width: 96%; max-width: 800px; margin: 0 auto;">' .
                     '<p style="color: #777; font-size: 1.8em; margin: .5em 0;">' . t('Hi !username!', array('!username' => '<strong>' . $user->name . '</strong>')) . '&nbsp;</p>' .
                     '<div style="background-color: #f0f0f0; border: 1px solid #ddd; border-radius: 5px; padding: 0 1em; font-size: 1.2em; color: #111;">' .
                     $params['content'] .
                     '<p>' .
                     t('This is an automatic e-mail from !sitename.', array('!sitename' => $sitename)) . '<br />' .
                     t('To stop receiving these e-mails, change your !preferences.', array('!preferences' => '<a href="' . $url_preferences . '" style="color: blue;">' . t('notification preferences') . '</a>')) .
                     '</p><p>' .
                     t('!suitedesk is a web based software for project management, planning, personal productivity, communication between project stakeholders, collaboration, customers support, documentation, cost control and change management.', array('!suitedesk' => '<a href="' . $base_url . '" style="color: blue;">SuiteDesk</a>', '!sitename' => $sitename)) .
                     '</p>' .
                     '<p>Manuel Cillero 【ツ】</p>' .
                     '<p style="font-size: smaller;"><a href="mailto:manuel@cillero.es" style="color: blue;">manuel@cillero.es</a><br />' .
                     '<a href="https://manuel.cillero.es" style="color: blue;">https://manuel.cillero.es</a><br />' .
                     '<a href="https://twitter.com/manuelcillero" style="color: blue;">https://twitter.com/manuelcillero</a><br />' .
                     '<a href="https://facebook.com/manuelcillero" style="color: blue;">https://facebook.com/manuelcillero</a></p>' .
                     '</div>' .
                     '<img style="margin: 1em 0; max-width: 100%; height: auto;" src="http://manuel.cillero.es/logo.png" alt="manuel.cillero.es" />' .
                     '</div>';
}

/**
 * Return TRUE if cron is running.
 */
function storm_cron_is_running() {
  return preg_match('/\/cron.php$/', $_SERVER['SCRIPT_NAME']);
}

/**
 * Switch from original user to mail submission user and back. Notes:
 *
 *  - Copied from mailhandler.
 *  - You first need to run _storm_switch_user() without argument to store the
 *    current user. Call _storm_switch_user() without argument to set the user
 *    back to the original user.
 */
function _storm_switch_user($uid = NULL) {
  global $user;
  static $orig_user = array();

  if (isset($uid)) {
    session_save_session(FALSE);
    $user = user_load(array('uid' => $uid));
  } elseif (count($orig_user)) {
    // Retrieve the initial user, can be called multiple times:
    $user = array_shift($orig_user);
    array_unshift($orig_user, $user);
    session_save_session(TRUE);
  } else {
    // Store the initial user:
    $orig_user[] = $user;
  }
}
