You are here

hosting_client.module in Hosting 6.2

File

client/hosting_client.module
View source
<?php

/**
 * @file
 *   Allow clients to be created and granted permissions over sites, tasks etc.
 */
include 'hosting_client.access.inc';
include drupal_get_path('module', 'node') . '/node.pages.inc';

/**
 * Implements hook_init().
 */
function hosting_client_init() {
  drupal_add_css(drupal_get_path('module', 'hosting_client') . '/hosting_client.css');
}

/**
 * The maximum length of a UNIX group
 *
 * Used to determine the sane maximum length of the internal name of a
 * client.
 *
 * @see hosting_client_sanitize()
 * @see http://community.aegirproject.org/node/557
 */
define('MAX_GROUP_LENGTH', 16);

/**
 * Implementation of hook_node_info().
 */
function hosting_client_node_info() {
  $types["client"] = array(
    "type" => 'client',
    "name" => 'Client',
    "module" => 'hosting_client',
    "has_title" => TRUE,
    "title_label" => 'Client name',
    "description" => hosting_node_help("client"),
    "has_body" => 0,
    "body_label" => '',
    "min_word_count" => 0,
  );
  return $types;
}

/**
 * Implementation of hook_theme().
 */
function hosting_client_theme($existing, $type, $theme, $path) {
  return array(
    'hosting_client_user_form' => array(
      'file' => 'hosting_client.access.inc',
      'arguments' => array(
        'form' => NULL,
      ),
    ),
    'hosting_client_form' => array(
      'file' => 'hosting_client.module',
      'arguments' => array(
        'form' => NULL,
      ),
    ),
    'hosting_client_platform_access_form' => array(
      'file' => 'hosting_client.module',
      'arguments' => array(
        'form' => NULL,
      ),
    ),
  );
}

/**
 * Implementation of hook_perm().
 */
function hosting_client_perm() {
  return array(
    'create client',
    'view client',
    'edit own client',
    'delete own client',
    'administer clients',
    'edit client users',
    'edit client uname',
  );
}

/**
 * Implementation of hook_access().
 */
function hosting_client_access($op, $node, $account) {
  if (!hosting_feature('client')) {

    // multiple client support has been disabled for this site.
    return FALSE;
  }
  if (user_access('administer clients', $account)) {
    return TRUE;
  }
  else {
    switch ($op) {
      case 'create':
        return user_access('create client', $account);
        break;
      case 'view':
        return user_access('view client', $account) && db_fetch_array(db_query("SELECT user FROM {hosting_client_user} WHERE user=%d and client=%d", $account->uid, $node->nid));
      case 'update':
        if (user_access('edit own client', $account) && $account->uid == $node->uid) {
          return TRUE;
        }
        break;
      case 'delete':
        if (user_access('delete own client', $account) && $account->uid == $node->uid) {
          return TRUE;
        }
        break;
      default:
        break;
    }
  }
}

/**
 * Get a client by name or nid.
 *
 * This is really a shortcut to node_load()
 *
 * @todo just load the freaking node instead of doing that select...
 * @see node_load()
 * @param $client
 *   Either the nid or the client title
 */
function hosting_get_client($client) {
  if (is_numeric($client)) {
    $result = db_result(db_query("SELECT n.nid FROM {hosting_client} h INNER JOIN {node} n ON n.nid = h.nid WHERE n.nid = %d", $client));
  }
  else {
    $result = db_result(db_query("SELECT c.nid FROM {hosting_client} c JOIN {node} n ON c.nid = n.nid WHERE n.title = '%s' AND n.type = 'client'", $client));
  }
  if ($result) {
    return node_load($result);
  }
  return false;
}

/**
 * Get a client by internal name.
 *
 * @see hosting_get_client()
 * @param $uname
 *   The internal name of this client.
 */
function hosting_get_client_by_uname($uname) {
  $result = db_result(db_query("SELECT c.nid FROM {hosting_client} c JOIN {node} n ON c.nid = n.nid WHERE c.uname = '%s' AND n.type = 'client'", $uname));
  if ($result) {
    return node_load($result);
  }
  else {
    return false;
  }
}

/**
 * Implementation of hook_form().
 */
function hosting_client_form(&$node) {
  $type = node_get_types('type', 'client');
  $form['title'] = array(
    '#type' => 'textfield',
    '#title' => $type->title_label,
    '#required' => TRUE,
    '#size' => 40,
    '#default_value' => $node->title,
    '#maxlength' => 100,
    '#description' => t('The name of this client, generally the organization name or the contact name for individuals.'),
  );
  $form['uname'] = array(
    '#type' => 'textfield',
    '#title' => t('Internal name'),
    '#size' => MAX_GROUP_LENGTH,
    '#maxlength' => MAX_GROUP_LENGTH,
    '#default_value' => $node->uname,
    '#access' => user_access('edit client uname'),
    '#description' => t('A machine-usable name that can be used internally, for example to map to a UNIX group in the backend. It is unique accross the system. If no value is provided, it is deduced from the client name, by stripping spaces and metacharacters and adding a prefix (%prefix).', array(
      '%prefix' => variable_get('hosting_client_prefix', 'no prefix define'),
    )),
  );
  if (!$node->nid && variable_get('hosting_client_register_user', FALSE)) {
    $form['email'] = array(
      '#type' => 'textfield',
      '#title' => t('Email address'),
      '#description' => t('Email address of the contact created with this client. Optional - if none is provided, no user will be created with this client.'),
      '#required' => FALSE,
      '#size' => 40,
      '#default_value' => $node->email,
      '#maxlength' => 100,
    );
    $form['email_confirm'] = array(
      '#type' => 'textfield',
      '#title' => t('Confirm Email address'),
      '#required' => FALSE,
      '#size' => 40,
      '#maxlength' => 100,
    );
  }
  if ($node->nid) {
    $users = hosting_client_users($node);
    foreach ($users as $uid => $uname) {
      $form['user_edit']['name'][$uid] = array(
        '#type' => 'markup',
        '#value' => l($uname, 'user/' . $uid),
      );
      $user_list[$uid] = '';
    }
    if (user_access('edit client users')) {
      $form['user_edit']['users'] = array(
        '#type' => 'checkboxes',
        '#options' => $user_list,
      );
    }
    $form['user_edit']['header'] = array(
      '#type' => 'value',
      '#value' => array(
        array(
          'data' => t('Allowed users'),
        ),
        array(
          'data' => t('Remove'),
        ),
      ),
    );
    if (user_access('edit client users')) {
      $form['user_edit']['new_user'] = array(
        '#type' => 'textfield',
        '#title' => t('Associate a user to this Client'),
        '#weight' => 2,
        '#autocomplete_path' => 'user/autocomplete',
        '#description' => t('This field allows you to associate an existing system user to this Client.
                             It does not create a new system user, but allows an existing user
                             to manage sites belonging to the Client.'),
      );
    }
    $form['user_edit']['#theme'] = 'hosting_client_form';
  }
  else {
    global $user;
    $form['new_user'] = array(
      '#type' => 'value',
      '#value' => $user->name,
    );
  }
  return $form;
}
function theme_hosting_client_form($form) {
  foreach (element_children($form['name']) as $user) {
    $row = array();
    $row['data'][] = drupal_render($form['name'][$user]);
    if (user_access('edit client users')) {
      $row['data'][] = drupal_render($form['users'][$user]);
    }
    $rows[] = $row;
  }
  $output = theme('table', $form['header']['#value'], $rows);
  $output .= drupal_render($form);
  return $output;
}

/**
 * Implementation of hook_validate() .
 */
function hosting_client_validate(&$node) {

  // We don't allow duplicate client names
  $title = $node->form_id == 'hosting_signup_form' ? 'client_name' : 'title';
  $node_nid = db_result(db_query("SELECT nid FROM {node} WHERE type = 'client' AND title = '%s'", $node->title));
  if ($node_nid && $node->nid != $node_nid) {
    form_set_error($title, t("Client name already in use, try %suggestion.", array(
      '%suggestion' => hosting_client_validate_suggest($node->title),
    )));
  }

  // We don't allow duplicate internal client names
  if ($node->uname) {
    $node->uname = hosting_client_sanitize($node->uname);
  }
  else {
    $node->uname = hosting_client_sanitize($node->title);
  }
  $nid = db_result(db_query("SELECT nid FROM {hosting_client} WHERE uname = '%s'", $node->uname));
  if ($nid && $node->nid != $nid) {
    form_set_error('uname', t("Client internal name already in use, try %suggestion.", array(
      '%suggestion' => hosting_client_validate_suggest($node->uname, TRUE),
    )));
  }
  if (!$node->nid && $node->email) {
    $user = user_load(array(
      'mail' => $node->email,
    ));
    if ($user) {
      form_set_error('email', t("Email address already exists."));
    }
    if ($node->email != $node->email_confirm) {
      form_set_error('email_confirm', t("Email addresses do not match"));
    }
    if (!valid_email_address($node->email)) {
      form_set_error('email', t("Email address invalid."));
    }
  }
}

/**
 * Helper for hosting_client_validate to suggest a new client name.
 *
 * @see hosting_client_validate()
 * @param $name
 *   The client name being validated.
 * @param $internal
 *   Whether this is an internal client name.
 */
function hosting_client_validate_suggest($name, $internal = FALSE) {
  $suggestion = FALSE;
  $table = $internal ? 'hosting_client' : 'node';
  $field = $internal ? 'uname' : 'title';
  $name = $internal ? hosting_client_sanitize($name) : $name;
  for ($i = 0; $i < 20; $i++) {
    $nid = db_result(db_query("SELECT nid FROM {%s} WHERE %s = '%s'", $table, $field, $name . $i));
    if (!$nid) {
      return $name . $i;
    }
  }
}

/**
 * Implementation of hook_insert().
 */
function hosting_client_insert($node) {
  if ($node->uname) {
    $node->uname = hosting_client_sanitize($node->uname);
  }
  else {
    $node->uname = hosting_client_sanitize($node->title);
  }
  db_query("INSERT INTO {hosting_client} (vid, nid, uname) VALUES (%d, %d, '%s' )", $node->vid, $node->nid, $node->uname);
  if (variable_get('hosting_client_register_user', FALSE) && !user_load(array(
    'mail' => $node->email,
  ))) {
    $user = hosting_client_register_user($node);
    $node->uid = $user->uid;
    db_query("UPDATE {node} SET uid = %d WHERE nid = %d", $user->uid, $node->nid);
    db_query("UPDATE {node_revisions} SET uid = %d WHERE vid = %d", $user->uid, $node->vid);
  }
  if (isset($node->new_user)) {
    $user = user_load(array(
      'name' => $node->new_user,
    ));
    if ($user) {
      db_query("INSERT INTO {hosting_client_user} (client, user, contact_type) VALUES (%d, %d, '%s')", $node->nid, $user->uid, '');
    }
  }
}

/**
 * Shortcut to get the rid of the 'aegir client' role.
 *
 * This hardcodes the 'aegir client' role name, so if it is changed,
 * this will fail.
 *
 * @todo the rid or role name should be a variable
 * @deprecated remove this function once the above setting is implemented
 */
function _hosting_client_get_role() {
  return db_result(db_query("SELECT rid FROM {role} WHERE name='aegir client'"));
}

/**
 * Register a new user account for the client.
 *
 * This is a helper function for client forms that will create a user
 * alongside a new client if the hosting_client_register_user setting
 * is true.
 *
 * @see hosting_client_insert()
 */
function hosting_client_register_user($node) {
  $pass = user_password();
  $user = new stdClass();
  $edit['name'] = $node->uname;
  $edit['hosting_client'] = $node->nid;
  $edit['mail'] = $node->email;
  $edit['pass'] = $pass;
  $edit['status'] = 1;
  $edit['roles'][_hosting_client_get_role()] = TRUE;
  $user = user_save($user, $edit);
  if ($user->uid && variable_get('hosting_client_send_welcome', FALSE)) {
    if ($node->client_name) {
      $to = sprintf("%s <%s>", $node->client_name, $node->email);
    }
    else {
      $to = $node->email;
    }
    $params = array(
      '!username' => $user->name,
      '!site' => variable_get('site_name', 'Drupal'),
      '!password' => $pass,
      '!uri' => $GLOBALS['base_url'],
      '!uri_brief' => substr($base_url, strlen('http://')),
      '!date' => format_date(time()),
      '!login_uri' => url('user', array(
        'absolute' => TRUE,
      )),
      '!edit_uri' => url('user/' . $user->uid . '/edit', array(
        'absolute' => TRUE,
      )),
      '!login_url' => user_pass_reset_url($user),
    );

    // No e-mail verification is required, create new user account, and login user immediately.
    $language = user_preferred_language($user);
    drupal_mail('hosting_client', 'hosting-client-register-welcome', $to, $language, $params);
  }
  return $user;
}

/**
 * Implementation of hook_mail().
 */
function hosting_client_mail($key, &$message, $params) {
  switch ($key) {
    case 'hosting-client-register-welcome':
      $message['subject'] = _hosting_client_mail_text('welcome_subject', $params);
      $message['body'] = _hosting_client_mail_text('welcome_body', $params);
      break;
  }
}

/**
 * Implementation of hook_update().
 *
 * As an existing node is being updated in the database, we need to do our own
 * database updates.
 */
function hosting_client_update($node) {

  // if this is a new node or we're adding a new revision,
  if ($node->revision) {
    hosting_client_insert($node);
  }
  else {
    if ($node->uname) {
      $node->uname = hosting_client_sanitize($node->uname);
    }
    else {
      $node->uname = hosting_client_sanitize($node->title);
    }
    db_query("UPDATE {hosting_client} SET uname='%s' WHERE nid=%d", $node->uname, $node->nid);
  }
  if ($node->users) {
    foreach ($node->users as $user) {
      db_query('DELETE FROM {hosting_client_user} WHERE user = %d AND client = %d', $user, $node->nid);
    }
  }
  if ($node->new_user) {
    $user = user_load(array(
      'name' => $node->new_user,
    ));
    db_query('INSERT INTO {hosting_client_user} (client, user, contact_type) VALUES (%d, %d, "%s")', $node->nid, $user->uid, '');
  }
}

/**
 * Implementation of hook_nodeapi_TYPE_OP().
 *
 * @see hosting_nodeapi()
 */
function hosting_nodeapi_client_delete_revision(&$node) {
  db_query('DELETE FROM {hosting_client} WHERE vid = %d', $node->vid);
}

/**
 * Implementation of hook_delete().
 */
function hosting_client_delete($node) {
  db_query('DELETE FROM {hosting_client} WHERE nid = %d', $node->nid);
}

/**
 * Implementation of hook_load().
 *
 * @param $node
 *    Node object
 */
function hosting_client_load($node) {
  $additions = db_fetch_object(db_query('SELECT uname FROM {hosting_client} WHERE vid = %d', $node->vid));
  return $additions;
}

/**
 * Implementation of hook_load().
 *
 * @param $arg
 *    Node's numeric nid
 */
function hosting_client_node_load($arg) {
  if (!is_numeric($arg)) {
    return FALSE;
  }
  if ($node = node_load($arg)) {
    if ($node->type == 'client') {
      return $node;
    }
  }
  return FALSE;
}

/**
 * Return a list of users for a given client.
 *
 * @param $node
 *   Client node (as nid or node object).
 * @return
 *   Array of user names indexed by uid.
 *
 */
function hosting_client_users($node) {
  if (is_object($node)) {
    $node = $node->nid;
  }
  elseif (!is_numeric($node)) {
    return array();
  }
  $users = array();
  $q = db_query("SELECT u.uid, u.name FROM {hosting_client_user} h INNER JOIN {users} u ON u.uid = h.user WHERE h.client = %d", $node);
  while ($result = db_fetch_array($q)) {
    $users[$result['uid']] = $result['name'];
  }
  return $users;
}

/**
 * Implementation of hook_view().
 */
function hosting_client_view($node, $teaser = FALSE, $page = FALSE) {
  $type = node_get_types('type', $node);
  $node->content['info']['#prefix'] = '<div id="hosting-client-info">';
  $node->content['info']['title'] = array(
    '#type' => 'item',
    '#title' => $type->title_label,
    '#value' => filter_xss($node->title),
  );
  $node->content['info']['uname'] = array(
    '#type' => 'item',
    '#title' => t('Internal name'),
    '#weight' => 1,
    '#value' => filter_xss($node->uname),
  );
  $node->content['info']['#suffix'] = '</div>';
  if ($page) {
    $users = hosting_client_users($node);
    foreach ($users as $uid => $uname) {
      if (user_access('access user profiles') || $uid == $GLOBALS['user']->uid) {
        $rows[] = array(
          l($uname, 'user/' . $uid),
        );
      }
      else {
        $rows[] = array(
          $uname,
        );
      }
    }
    $header = array(
      t('Allowed users'),
    );
    $node->content['users_view'] = array(
      '#type' => 'item',
      '#value' => theme('table', $header, $rows),
      '#class' => 'client',
      '#prefix' => '<div id="hosting-site-list">',
      '#suffix' => '</div>',
      '#weight' => 11,
    );
  }
  return $node;
}

/**
 * Implements hook_hosting_site_site_list_filters().
 */
function hosting_client_hosting_site_site_list_filters() {
  return array(
    'client',
  );
}

/**
 * Helper function to generate new client node during import.
 *
 * @param $name
 *   Client name.
 * @return
 *   The nid of the generated client.
 */
function hosting_import_client($name) {
  $client = hosting_get_client_by_uname($name);
  if (!$client) {
    $client = new stdClass();
    $client->type = 'client';
    $client->uid = 1;
    $client->title = trim($name);
    $client->status = 1;
    node_save($client);
  }
  return $client;
}

/**
 * Implementation of hook_menu().
 */
function hosting_client_menu() {
  $items['node/%node/site/add'] = array(
    'title' => 'Add site',
    'description' => 'Add a site to the current client',
    'page callback' => 'hosting_client_site_form',
    'page arguments' => array(
      'site_node_form',
      1,
    ),
    'access callback' => 'hosting_client_menu_access',
    'access arguments' => array(
      'create site',
      1,
    ),
    'type' => MENU_LOCAL_TASK,
    'weight' => 5,
  );
  $items['hosting_client/autocomplete'] = array(
    'title' => 'hosting client get client autocomplete',
    'page callback' => 'hosting_client_autocomplete',
    'access arguments' => array(
      'access content',
    ),
    'type' => MENU_CALLBACK,
  );
  $items['admin/hosting/client'] = array(
    'title' => 'Clients',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'hosting_client_configure',
    ),
    'access callback' => '_hosting_client_configure_access',
    'type' => MENU_LOCAL_TASK,
  );
  $items['hosting/clients/list'] = array(
    'title' => 'List',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -10,
  );
  $items['hosting/clients/add'] = array(
    'title' => 'Add client',
    'type' => MENU_LOCAL_TASK,
    'page callback' => 'drupal_goto',
    'page arguments' => array(
      'node/add/client',
    ),
    'access callback' => 'node_access',
    'access arguments' => array(
      'create',
      'client',
    ),
  );
  $items['node/%node/clients'] = array(
    'title' => 'Clients',
    'type' => MENU_LOCAL_TASK,
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'hosting_client_platform_access_form',
      1,
    ),
    'access callback' => '_hosting_client_platform_access',
    'access arguments' => array(
      1,
    ),
  );
  return $items;
}

/**
 * Access callback for the client platform access tab.
 */
function _hosting_client_platform_access($node) {
  return $node->type == 'platform' && $node->platform_status != HOSTING_PLATFORM_DELETED && node_access('update', $node) && user_access('administer clients');
}

/**
 * Page callback for the client platform access form.
 */
function hosting_client_platform_access_form($form, $node) {
  $form = array();
  $clients = isset($node->clients) ? $node->clients : array();
  if (count($clients)) {
    foreach ($clients as $client) {
      $client_node = hosting_get_client($client);
      $form['name'][$client] = array(
        '#type' => 'markup',
        '#value' => l($client_node->title, 'node/' . $client),
      );

      // Remove the label for the checkbox
      $clients[$client] = '';
    }
  }
  else {

    // No access control on this platform.
    $form['name']['all'] = array(
      '#type' => 'markup',
      '#value' => 'All clients have access to this platform.',
    );
  }
  $form['clients'] = array(
    '#type' => 'checkboxes',
    '#options' => $clients,
  );
  $form['header'] = array(
    '#type' => 'value',
    '#value' => array(
      array(
        'data' => t('Allowed clients'),
      ),
      array(
        'data' => t('Remove'),
      ),
    ),
  );
  $form['new_client'] = array(
    '#type' => 'textfield',
    '#title' => t('Grant a client access to this platform'),
    '#weight' => 2,
    '#autocomplete_path' => 'hosting_client/autocomplete/client',
    '#description' => t('This field allows you to grant a client access to this platform.
                         Remove all clients from this list to grant all clients access to this platform'),
  );
  $form['#theme'] = 'hosting_client_platform_access_form';
  $form['apply'] = array(
    '#type' => 'submit',
    '#value' => t('Apply'),
    '#access' => user_access('administer clients'),
    '#submit' => array(
      'hosting_client_platform_access_form_submit',
    ),
    '#weight' => 10,
  );
  return $form;
}

/**
 * Callback to theme the client platform access form.
 */
function theme_hosting_client_platform_access_form($form) {
  foreach (element_children($form['name']) as $client) {
    $row = array();
    $row['data'][] = drupal_render($form['name'][$client]);
    if (user_access('administer clients')) {
      $row['data'][] = drupal_render($form['clients'][$client]);
    }
    $rows[] = $row;
  }
  $output = theme('table', $form['header']['#value'], $rows);
  $output .= drupal_render($form);
  return $output;
}

/**
 * Submit handler for the client platform access form.
 */
function hosting_client_platform_access_form_submit($form, $form_state) {
  $clients = $form_state['values']['clients'];
  $new_client = isset($form_state['values']['new_client']) ? $form_state['values']['new_client'] : FALSE;
  $existing = $form['#parameters']['2']->clients;
  $platform = $form['#parameters']['2']->nid;

  // Remove the selected client(s).
  foreach ($clients as $cid => $remove) {
    if ($remove) {
      db_query("DELETE FROM {hosting_platform_client_access} WHERE pid = %d AND cid = %d", $platform, $cid);
    }
  }

  // Add the new client.
  if ($new_client) {
    $client = hosting_get_client($new_client);
    if ($client) {
      db_query("INSERT INTO {hosting_platform_client_access} (pid, cid) VALUES (%d, %d)", $platform, $client->nid);
    }
    else {
      $add_client = '';
      if (user_access('create client') || user_access('administer clients')) {
        $add_client = ' or ' . l('add a new client', 'node/add/client');
      }
      form_set_error('new_client', t('The client name (%client) was not recognized. Please try again', array(
        '%client' => $new_client,
      )) . $add_client . '.');
    }
  }
}

/**
 * Get the default value of the client field for a site node.
 *
 * @param $node
 *   The site to the the clients for.
 */
function _hosting_client_site_default($node) {

  # find the right client
  global $user;
  $current_client_id = 0;
  if ($user->uid) {
    $client_ids = hosting_get_client_from_user($user->uid);
    $clients = array();
    foreach ($client_ids as $client_id => $client_permissions) {
      $client_id = $client_id ? $client_id : HOSTING_DEFAULT_CLIENT;
      $client = node_load($client_id);
      $clients[$client->title] = $client->title;
      if ($node->client == $client_id || !$current_client_id) {
        $current_client_id = $client_id;
      }
    }
    if (!$current_client_id && !user_access('administer clients')) {
      form_set_error('client', t('Your user is not associated with any clients so you are not allowed to create new sites'));
    }
  }
  if (!$current_client_id) {
    $current_client_id = HOSTING_DEFAULT_CLIENT;
  }

  // allow admins to override
  if ($node->client && user_access('administer clients')) {
    $current_client_id = $node->client;
  }
  $client = node_load($current_client_id);
  if (!$client) {

    # we give up, couldn't find a client, we're probably in a preview so

    # just use the node client
    $client = new stdClass();
    $client->title = $node->client;
  }
  return $client->title;
}

/**
 * Wrapper around the regular site_node_form that passes a dummy site with a proper client
 */
function hosting_client_site_form($form, $node) {
  $site = new stdClass();
  $site->type = 'site';
  $site->client = $node->nid;
  return drupal_get_form('site_node_form', $site);
}

/**
 * Menu access callback for the site creation tab in the client node.
 *
 * @see hosting_client_menu()
 */
function hosting_client_menu_access($perm, $node) {
  if ($node->type == 'client') {
    return user_access($perm);
  }
  else {
    return false;
  }
}

/**
 * Menu access callback for the client settings.
 *
 * @see hosting_client_menu()
 */
function _hosting_client_configure_access() {
  return user_access('administer clients') && hosting_feature('client');
}

/**
 * Expand the client registration email message based on the variables
 *
 * This will check for a custom message set in the
 * hosting_client_mail_welcome_subject and
 * hosting_client_mail_welcome_body variables.
 */
function _hosting_client_mail_text($messageid, $variables = array()) {

  // Check if an admin setting overrides the default string.
  if ($admin_setting = variable_get('hosting_client_mail_' . $messageid, FALSE)) {
    return strtr($admin_setting, $variables);
  }
  switch ($messageid) {
    case 'welcome_subject':
      return t('Account details for !username at !site', $variables);
    case 'welcome_body':
      return t("!username,\n\nThank you for registering at !site. You may now log in to !login_uri using the following username and password:\n\nusername: !username\npassword: !password\n\nYou may also log in by clicking on this link or copying and pasting it in your browser:\n\n!login_url\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you will be redirected to !edit_uri so you can change your password.\n\n\n--  !site team", $variables);
  }
}

/**
 * Menu callback for the module's settings.
 *
 * @see hosting_client_menu()
 */
function hosting_client_configure() {
  $form['hosting_client_prefix'] = array(
    '#type' => 'textfield',
    '#title' => t('Client internal name prefix'),
    '#description' => t('Client nodes have an internal name that can be mapped to a UNIX group. This is the prefix assigned to that internal name to make sure it is in a separate namespace. Note that UNIX groups are generally limited to 16 characters so this prefix should be kept short. It can also be empty, in which case no prefix will be added.'),
    '#default_value' => variable_get('hosting_client_prefix', ''),
    '#size' => 5,
    '#maxlength' => 16,
  );
  $form['hosting_client_register_user'] = array(
    '#type' => 'checkbox',
    '#title' => t('Automatically create user accounts for new clients.'),
    '#description' => t('If this setting is on, any new client nodes will automatically have a system user account generated for them, and associated with the new client node. Users going through the signup form module have a user created regardless of this setting.'),
    '#default_value' => variable_get('hosting_client_register_user', FALSE),
  );

  // User e-mail settings.
  $form['email'] = array(
    '#type' => 'fieldset',
    '#title' => t('User e-mail settings'),
  );
  $form['email']['hosting_client_send_welcome'] = array(
    '#type' => 'checkbox',
    '#title' => t('Send welcome mail to new clients.'),
    '#description' => t('If this setting is on, new clients will receive a welcome email containing their login details.'),
    '#default_value' => variable_get('hosting_client_send_welcome', FALSE),
  );
  $form['email']['hosting_client_mail_welcome_subject'] = array(
    '#type' => 'textfield',
    '#title' => t('Subject of welcome e-mail'),
    '#default_value' => _hosting_client_mail_text('welcome_subject'),
    '#maxlength' => 180,
    '#description' => t('Customize the subject of your welcome e-mail, which is sent to new members upon registering.') . ' ' . t('Available variables are:') . ' !username, !site, !password, !uri, !uri_brief, !date, !login_uri, !edit_uri, !login_url.',
  );
  $form['email']['hosting_client_mail_welcome_body'] = array(
    '#type' => 'textarea',
    '#title' => t('Body of welcome e-mail'),
    '#default_value' => _hosting_client_mail_text('welcome_body'),
    '#rows' => 15,
    '#description' => t('Customize the body of the welcome e-mail, which is sent to new members upon registering.') . ' ' . t('Available variables are:') . ' !username, !site, !password, !uri, !uri_brief, !login_uri, !edit_uri, !login_url.',
  );
  return system_settings_form($form);
}

/**
 * Get 25 clients in a paged query.
 */
function _hosting_get_clients() {
  $return = array();
  $result = pager_query(db_rewrite_sql('SELECT n.nid, n.title from {node} n WHERE n.type = "client"'), 25);
  while ($client = db_fetch_object($result)) {
    $return[$client->nid] = $client->title;
  }
  return $return;
}

/**
 * Make a themed list of clients.
 *
 * Will take whatever _hosting_get_clients() will give as clients.
 *
 * @see _hosting_get_clients()
 */
function hosting_client_list() {
  $summary = array();
  $clients = _hosting_get_clients();
  return theme('item_list', array_map('_hosting_node_link', array_keys($clients)));
}

/**
 * Implementation of hook_block().
 */
function hosting_client_block($op = 'list', $delta = 0, $edit = array()) {
  $blocks = array();
  switch ($op) {
    case 'list':
      $blocks['hosting_clients'] = array(
        'info' => t('Hosting clients'),
        'enabled' => 1,
        'region' => 'left',
        'weight' => 10,
      );
      break;
    case 'view':
      switch ($delta) {
        case 'hosting_clients':
          $blocks['title'] = t('Clients');
          $blocks['content'] = user_access('view client') ? hosting_client_list() : '';
          if (user_access('create client')) {
            $blocks['content'] .= l(t('Add new client'), 'node/add/client', array(
              'attributes' => array(
                'class' => 'add-new-client',
              ),
            ));
          }
          break;
      }
  }
  return $blocks;
}

/**
 * Retrieve autocomplete suggestions.
 */
function hosting_client_autocomplete($type, $keyword) {
  $matches = array();
  if ($type == 'client') {
    $query = db_query(db_rewrite_sql("SELECT * FROM {node} n WHERE type = '%s' AND title LIKE '%s%%'"), $type, addcslashes($keyword, '\\%_'));
    while ($result = db_fetch_object($query)) {
      $matches[$result->title] = $result->title;
    }
  }
  drupal_json($matches);
  exit;
}

/**
 * Implementation of hook_nodeapi_TYPE_OP().
 *
 * This loads the platform/client access control mappings in the platform node.
 *
 * @todo move this to the platform module (!!)
 * @see hosting_nodeapi()
 */
function hosting_nodeapi_platform_load($node) {
  $result = db_query("SELECT cid FROM {hosting_platform_client_access} WHERE pid = %d", $node->nid);
  $additions = array();
  while ($record = db_fetch_object($result)) {
    $additions['clients'][$record->cid] = $record->cid;
  }
  return $additions;
}

/**
 * Implementation of hook_nodeapi().
 */
function hosting_client_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
  switch ($op) {
    case 'delete':
      if ($node->type == 'platform') {
        db_query("DELETE FROM {hosting_platform_client_access} WHERE pid = %d", $node->nid);
      }
  }
}

/**
 * Small helper function to get platforms that haven't been deleted or locked,
 * and are accessible by this user.
 *
 * We can get called a few times during a page request, so we implement static
 * caching for speed, and because the platforms that are available are unlikely
 * to change during a single page request.
 *
 * @param $uid
 *   The user ID to retrieve the allowed platforms for. If none is specified the
 *   currently logged in user will be used.
 * @param $reset
 *   Whether to reset the internal static cache or not.
 *
 * @todo this is not the right way. we need to implement node-level
 * access permissions here, the same way we do for sites. see
 * http://drupal.org/node/725952
 */
function _hosting_get_allowed_platforms($uid = NULL, $reset = FALSE) {
  static $platforms = array();
  if ($reset) {
    $platforms = array();
  }
  if (is_null($uid)) {
    global $user;
    $uid = $user->uid;
  }
  if (!isset($platforms[$uid])) {
    $platforms[$uid] = array();
    $result = db_query("SELECT n.nid, n.title FROM {node} n LEFT JOIN {hosting_platform} h ON n.nid = h.nid\n                      LEFT JOIN {hosting_platform_client_access} p ON n.nid = p.pid\n                      LEFT JOIN {hosting_client_user} c ON c.client = p.cid\n                      WHERE n.type='platform' AND n.status=1 AND h.status > '%d'\n                      AND (c.user= %d OR p.pid IS NULL)", HOSTING_PLATFORM_LOCKED, $uid);
    while ($server = db_fetch_object($result)) {
      $platforms[$uid][$server->nid] = $server->title;
    }
  }
  return $platforms[$uid];
}

/**
 * Return a machine-usable name for a client.
 *
 * This aims to be usable for unix group/user names and shells.
 *
 * It adds a prefix configured in the frontend settings (defaulting to
 * 'cl-'), and strips the total length of the string (including the
 * prefix) to MAX_GROUP_LENGTH.
 *
 * This can also be used to validate user-provided client unames, as
 * it strips and readds the prefix and performs the same validation
 * and corrections on the field.
 *
 * This is inspired from the context sanitization stuff.
 *
 * @see MAX_GROUP_LENGTH
 */
function hosting_client_sanitize($title) {
  $prefix = variable_get('hosting_client_prefix', '');

  // remove anything but "word characters", dots and dashes
  // remove the prefix in case we are validating an existing uname
  // TODO: optimize in one call
  $title = preg_replace("/[!\\W\\.\\-]/", "", preg_replace("/^{$prefix}/", "", $title));
  return substr(strtolower($prefix . $title), 0, MAX_GROUP_LENGTH);
}

/**
 * Implementation of hook_views_api().
 */
function hosting_client_views_api() {
  return array(
    'api' => 2,
    'path' => drupal_get_path('module', 'hosting_client'),
  );
}

Functions

Namesort descending Description
hosting_client_access Implementation of hook_access().
hosting_client_autocomplete Retrieve autocomplete suggestions.
hosting_client_block Implementation of hook_block().
hosting_client_configure Menu callback for the module's settings.
hosting_client_delete Implementation of hook_delete().
hosting_client_form Implementation of hook_form().
hosting_client_hosting_site_site_list_filters Implements hook_hosting_site_site_list_filters().
hosting_client_init Implements hook_init().
hosting_client_insert Implementation of hook_insert().
hosting_client_list Make a themed list of clients.
hosting_client_load Implementation of hook_load().
hosting_client_mail Implementation of hook_mail().
hosting_client_menu Implementation of hook_menu().
hosting_client_menu_access Menu access callback for the site creation tab in the client node.
hosting_client_nodeapi Implementation of hook_nodeapi().
hosting_client_node_info Implementation of hook_node_info().
hosting_client_node_load Implementation of hook_load().
hosting_client_perm Implementation of hook_perm().
hosting_client_platform_access_form Page callback for the client platform access form.
hosting_client_platform_access_form_submit Submit handler for the client platform access form.
hosting_client_register_user Register a new user account for the client.
hosting_client_sanitize Return a machine-usable name for a client.
hosting_client_site_form Wrapper around the regular site_node_form that passes a dummy site with a proper client
hosting_client_theme Implementation of hook_theme().
hosting_client_update Implementation of hook_update().
hosting_client_users Return a list of users for a given client.
hosting_client_validate Implementation of hook_validate() .
hosting_client_validate_suggest Helper for hosting_client_validate to suggest a new client name.
hosting_client_view Implementation of hook_view().
hosting_client_views_api Implementation of hook_views_api().
hosting_get_client Get a client by name or nid.
hosting_get_client_by_uname Get a client by internal name.
hosting_import_client Helper function to generate new client node during import.
hosting_nodeapi_client_delete_revision Implementation of hook_nodeapi_TYPE_OP().
hosting_nodeapi_platform_load Implementation of hook_nodeapi_TYPE_OP().
theme_hosting_client_form
theme_hosting_client_platform_access_form Callback to theme the client platform access form.
_hosting_client_configure_access Menu access callback for the client settings.
_hosting_client_get_role Deprecated Shortcut to get the rid of the 'aegir client' role.
_hosting_client_mail_text Expand the client registration email message based on the variables
_hosting_client_platform_access Access callback for the client platform access tab.
_hosting_client_site_default Get the default value of the client field for a site node.
_hosting_get_allowed_platforms Small helper function to get platforms that haven't been deleted or locked, and are accessible by this user.
_hosting_get_clients Get 25 clients in a paged query.

Constants

Namesort descending Description
MAX_GROUP_LENGTH The maximum length of a UNIX group