You are here

hosting_client.module in Hosting 7.3

File

client/hosting_client.module
View source
<?php

/**
 * @file
 * Allow clients to be created and granted permissions over sites, tasks etc.
 */
include dirname(__FILE__) . '/hosting_client.access.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('HOSTING_CLIENT_MAX_GROUP_LENGTH', 16);

/**
 * Implements hook_node_info().
 */
function hosting_client_node_info() {
  $types["client"] = array(
    "name" => t('Client'),
    'base' => '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;
}

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

/**
 * Implements hook_permission().
 */
function hosting_client_permission() {
  return array(
    'create client' => array(
      'title' => t('create client'),
    ),
    'view client' => array(
      'title' => t('view client'),
    ),
    'edit own client' => array(
      'title' => t('edit own client'),
    ),
    'delete own client' => array(
      'title' => t('delete own client'),
    ),
    'administer clients' => array(
      'title' => t('administer clients'),
    ),
    'edit client users' => array(
      'title' => t('edit client users'),
    ),
    'edit client uname' => array(
      'title' => t('edit client uname'),
    ),
  );
}

/**
 * Implements hook_node_access().
 */
function hosting_client_node_access($node, $op, $account) {
  if (!hosting_feature('client')) {

    // Multiple client support has been disabled for this site.
    return FALSE;
  }
  $type = is_string($node) ? $node : $node->type;
  if ($type != 'client') {
    return NODE_ACCESS_IGNORE;
  }
  if (user_access('administer clients', $account)) {
    return TRUE;
  }
  else {
    switch ($op) {
      case 'create':
        return user_access('create client', $account);
      case 'view':

        // TODO shoudn't this SQL query be covered by node_grants?
        return user_access('view client', $account) && db_query("SELECT user FROM {hosting_client_user} WHERE user = :user and client = :client", array(
          ':user' => $account->uid,
          ':client' => $node->nid,
        ))
          ->fetchAssoc();
      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.
 *
 * @param int|object $client
 *   Either the nid or the client title
 *
 * @return bool|object
 *   The client node object of FALSE.
 */
function hosting_get_client($client) {
  if (is_numeric($client)) {

    // @todo: confirm that we validate against numeric client names.
    return node_load($client);
  }
  else {
    if ($nodes = entity_load('node', FALSE, array(
      'type' => 'client',
      'title' => $client,
    ))) {
      return array_shift($nodes);
    }
    else {
      return FALSE;
    }
  }
}

/**
 * Get a client by internal name.
 *
 * @see hosting_get_client()
 *
 * @param string $uname
 *   The internal name of this client.
 *
 * @return object|bool
 *   The client node object of FALSE.
 */
function hosting_get_client_by_uname($uname) {
  $result = db_query("SELECT c.nid FROM {hosting_client} c JOIN {node} n ON c.nid = n.nid WHERE c.uname = :uname AND n.type = :type", array(
    ':uname' => $uname,
    ':type' => 'client',
  ))
    ->fetchField();
  if ($result) {
    return node_load($result);
  }
  else {
    return FALSE;
  }
}

/**
 * Implements hook_form().
 */
function hosting_client_form(&$node, &$form_state) {
  $type = node_type_get_type('client');
  $form['title'] = array(
    '#type' => 'textfield',
    '#title' => check_plain($type->title_label),
    '#required' => TRUE,
    '#size' => 40,
    '#default_value' => isset($node->title) ? $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' => 'machine_name',
    '#title' => t('Internal name'),
    '#size' => HOSTING_CLIENT_MAX_GROUP_LENGTH,
    '#maxlength' => HOSTING_CLIENT_MAX_GROUP_LENGTH,
    '#default_value' => isset($node->uname) ? $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'),
    )),
    '#machine_name' => array(
      'exists' => 'hosting_get_client_by_uname',
      'source' => array(
        'title',
      ),
      'label' => t('Internal name'),
      'replace_pattern' => '[^a-z0-9_-]+',
      'replace' => '_',
    ),
  );
  if (!isset($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' => isset($node->email) ? $node->email : '',
      '#maxlength' => 100,
    );
    $form['email_confirm'] = array(
      '#type' => 'textfield',
      '#title' => t('Confirm Email address'),
      '#required' => FALSE,
      '#size' => 40,
      '#maxlength' => 100,
    );
  }
  if (isset($node->nid)) {
    $users = hosting_client_users($node);
    $user_list = array();
    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' => isset($user->name) ? $user->name : '',
    );
  }
  return $form;

  // @ignore security_fapi_markup
}

/**
 * @todo Please document this function.
 * @see http://drupal.org/node/1354
 */
function theme_hosting_client_form($variables) {
  $form = $variables['form'];
  foreach (element_children($form['name'], FALSE) as $user) {
    $row = array();

    // @todo: Figure out why drupal_render isn't working here.

    //$row['data'][] = drupal_render($form['name'][$user]);
    $row['data'][] = $form['name'][$user]['#value'];
    if (user_access('edit client users')) {
      $row['data'][] = drupal_render($form['users'][$user]);
    }
    $rows[] = $row;
  }
  $output = theme('table', array(
    'header' => $form['header']['#value'],
    'rows' => $rows,
  ));
  $output .= drupal_render_children($form);
  return $output;
}

/**
 * Implements hook_validate().
 */
function hosting_client_validate($node, $form, &$form_state) {

  // We don't allow duplicate client names.
  $title = $node->form_id == 'hosting_signup_form' ? 'client_name' : 'title';
  $node_nid = db_query("SELECT nid FROM {node} WHERE type = 'client' AND title = ':title'", array(
    ':title' => $node->title,
  ))
    ->fetchField();
  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);
  }

  // @todo convert this statement to DBTNG syntax.
  $nid = db_query("SELECT nid FROM {hosting_client} WHERE uname = ':uname'", array(
    ':uname' => $node->uname,
  ))
    ->fetchField();
  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 (!empty($node->nid) && !empty($node->email)) {
    $user = user_load_by_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 string $name
 *   The client name being validated.
 * @param bool $internal
 *   Whether this is an internal client name.
 *
 * @return string
 *   Suggested 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++) {

    // @todo convert this statement to DBTNG syntax.
    $nid = db_query("SELECT nid\n                     FROM {:table}\n                     WHERE uname\n                     LIKE '%:name%'\n                    ", array(
      ':table' => $table,
      ':field' => $field,
      ':name' => $name . $i,
    ))
      ->fetchField();
    if (!$nid) {
      return $name . $i;
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function hosting_client_form_site_node_form_alter(&$form, &$form_state, $form_id) {
  global $user;
  $node = $form['#node'];
  $client_ids = hosting_get_client_from_user($user->uid);
  if (empty($client_ids) && !user_access('administer clients') && variable_get('hosting_client_require_client_to_create_site', FALSE)) {
    form_set_error('client', t('Your user is not associated with any clients so you are not allowed to create new sites'));
    $form['#access'] = 0;
    return $form;
  }
  $editable = (!isset($node->client) || !isset($node->nid) || user_access('administer sites')) && hosting_feature('client');
  $add_client_text = '';
  if (user_access('administer clients') || user_access('create client')) {
    $add_client_text = t('Click !here to add a new client.', array(
      '!here' => l(t('here'), 'node/add/client', array(
        'attributes' => array(
          'target' => '_blank',
        ),
      )),
    ));
  }
  _hosting_site_field($form, $node, 'client', array(
    '#type' => 'textfield',
    '#required' => TRUE,
    '#title' => t('Client'),
    '#default_value' => _hosting_client_site_default($node),
    '#description' => t('The client to whom this site belongs.') . ' ' . $add_client_text,
    '#autocomplete_path' => 'hosting_client/autocomplete/client',
  ), 'filter_xss', $editable);
}

/**
 * Implements hook_insert().
 */
function hosting_client_insert($node) {
  if (!empty($node->uname)) {
    $node->uname = hosting_client_sanitize($node->uname);
  }
  else {
    $node->uname = hosting_client_sanitize($node->title);
  }
  $id = db_insert('hosting_client')
    ->fields(array(
    'vid' => $node->vid,
    'nid' => $node->nid,
    'uname' => $node->uname,
  ))
    ->execute();
  if (variable_get('hosting_client_register_user', FALSE) && user_load_multiple(array(), array(
    'mail' => $node->email,
  )) == FALSE) {
    $user = hosting_client_register_user($node);
    $node->uid = $user->uid;
    db_update('node')
      ->fields(array(
      'uid' => $user->uid,
    ))
      ->condition('nid', $node->nid)
      ->execute();
    db_update('node_revision')
      ->fields(array(
      'uid' => $user->uid,
    ))
      ->condition('vid', $node->vid)
      ->execute();
  }
  if (isset($node->new_user)) {
    $user = user_load_multiple(array(), array(
      'name' => $node->new_user,
    ));
    $user = array_shift($user);
    if ($user) {
      $id = db_insert('hosting_client_user')
        ->fields(array(
        'client' => $node->nid,
        'user' => $user->uid,
        'contact_type' => '',
      ))
        ->execute();
    }
  }
}

/**
 * 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_query("SELECT rid FROM {role} WHERE name = :name", array(
    ':name' => 'aegir client',
  ))
    ->fetchField();
}

/**
 * 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(REQUEST_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;
}

/**
 * Implements 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;
  }
}

/**
 * Implements 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_update('hosting_client')
      ->fields(array(
      'uname' => $node->uname,
    ))
      ->condition('nid', $node->nid)
      ->execute();
  }
  if ($node->users) {
    foreach ($node->users as $user) {
      db_delete('hosting_client_user')
        ->condition('user', $user)
        ->condition('client', $node->nid)
        ->execute();
    }
  }
  if ($node->new_user) {
    $user = user_load_multiple(array(), array(
      'name' => $node->new_user,
    ));
    $user = array_shift($user);
    $id = db_insert('hosting_client_user')
      ->fields(array(
      'client' => $node->nid,
      'user' => $user->uid,
      'contact_type' => '',
    ))
      ->execute();
  }
}

/**
 * Implements hook_nodeapi_TYPE_OP().
 *
 * @see hosting_nodeapi()
 */
function hosting_nodeapi_client_delete_revision(&$node) {
  db_delete('hosting_client')
    ->condition('vid', $node->vid)
    ->execute();
}

/**
 * Implements hook_delete().
 */
function hosting_client_delete($node) {
  db_delete('hosting_client')
    ->condition('nid', $node->nid)
    ->execute();
  db_delete('hosting_client_user')
    ->condition('client', $node->nid)
    ->execute();
}

/**
 * Implements hook_load().
 */
function hosting_client_load($nodes) {
  foreach ($nodes as $nid => &$node) {
    $additions = db_query('SELECT uname FROM {hosting_client} WHERE vid = :vid', array(
      ':vid' => $node->vid,
    ))
      ->fetch();
    foreach ($additions as $property => &$value) {
      $node->{$property} = $value;
    }
  }
}

/**
 * Return a list of users for a given client.
 *
 * @param int|object $node
 *   Client node (as nid or node object).
 *
 * @return array
 *   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();
  }

  // @todo: Figure out why variable substitution doesn't work here.
  return db_query("SELECT u.uid, u.name, h.client FROM {hosting_client_user} h INNER JOIN {users} u ON u.uid = h.user WHERE h.client = {$node}")
    ->fetchAllKeyed();
}

/**
 * Implements hook_view().
 */
function hosting_client_view($node, $view_mode, $langcode = NULL) {
  $type = node_type_get_type($node);
  $node->content['info']['#prefix'] = '<div id="hosting-client-info">';
  $node->content['info']['title'] = array(
    '#type' => 'item',
    '#title' => $type->title_label,
    '#markup' => filter_xss($node->title),
  );
  $node->content['info']['uname'] = array(
    '#type' => 'item',
    '#title' => t('Internal name'),
    '#weight' => 1,
    '#markup' => filter_xss($node->uname),
  );
  $node->content['info']['#suffix'] = '</div>';
  if ($view_mode = 'full') {
    $rows = array();
    $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',
      '#markup' => theme('table', array(
        'header' => $header,
        'rows' => $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 string $name
 *   Client name.
 *
 * @return object
 *   The node object 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;
}

/**
 * Implements 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, $form_state, $node) {
  $form = array();
  $clients = isset($node->clients) ? $node->clients : array();
  if (count($clients)) {
    foreach ($clients as $client) {
      $client_node = hosting_get_client($client);
      $form['names'][$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['names']['_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($variables) {
  $form = $variables['form'];
  foreach (element_children($form['names'], FALSE) as $client) {
    $row = array();
    $row['data'][] = $form['names'][$client]['#value'];
    if ($client != '_all' && user_access('administer clients')) {
      $row['data'][] = drupal_render($form['clients'][$client]);
    }
    else {

      // Nothing to remove if there aren't any clients...
      unset($form['header']['#value'][1]);
    }
    $rows[] = $row;
  }
  $output = theme('table', array(
    'header' => $form['header']['#value'],
    'rows' => $rows,
  ));
  $output .= drupal_render_children($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;
  $platform = $form_state['build_info']['args'][0];
  $existing = isset($platform->clients) ? $platform->clients : array();

  // Remove the selected client(s).
  foreach ($clients as $cid => $remove) {
    if ($remove) {
      db_delete('hosting_platform_client_access')
        ->condition('pid', $platform->nid)
        ->condition('cid', $cid)
        ->execute();
    }
  }

  // Add the new client.
  if ($new_client) {
    $client = hosting_get_client($new_client);
    if ($client) {
      db_insert('hosting_platform_client_access')
        ->fields(array(
        'pid' => $platform->nid,
        'cid' => $client->nid,
      ))
        ->execute();
    }
    else {
      $add_client = '';
      if (user_access('create client') || user_access('administer clients')) {
        $add_client = ' ' . t('or') . '' . l(t('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 object $node
 *   The site to the the clients for.
 *
 * @return string|null
 *   The client name.
 */
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 (isset($node->client) && $node->client == $client_id || !$current_client_id) {
        $current_client_id = $client_id;
      }
    }
  }
  if (!$current_client_id) {
    $current_client_id = HOSTING_DEFAULT_CLIENT;
  }

  // Allow admins to override.
  if (isset($node->client) && $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 = isset($node->client) ? $node->client : NULL;
  }
  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) {

  // Must explicitly load node.pages.inc in order to load the node_form.
  module_load_include('module', 'node', 'pages.inc');
  $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, &$form_state) {
  $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),
  );
  $form['hosting_client_require_client_to_create_site'] = array(
    '#type' => 'checkbox',
    '#title' => t('Require users to have a client to create a site.'),
    '#description' => t('If this setting is on, users will not be able to create sites unless they are associated with a client.'),
    '#default_value' => variable_get('hosting_client_require_client_to_create_site', 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.
 *
 * DEPRECATED, moved to the hosting_client_list view.
 */
function _hosting_get_clients() {
  $return = array();
  $query = db_select('node', 'n')
    ->extend('PagerDefault');
  $result = $query
    ->fields('n', array(
    'nid',
    'title',
  ))
    ->condition('type', 'client')
    ->limit(25)
    ->addTag('node_access')
    ->execute();
  foreach ($result as $client) {
    $return[$client->nid] = $client->title;
  }
  return $return;
}

/**
 * Retrieve autocomplete suggestions.
 */
function hosting_client_autocomplete($type, $keyword) {
  $matches = array();
  if ($type == 'client') {

    // @todo Confirm that these are equivalent, especially the db_rewrite_sql -> addTag('node_access')
    // $query = db_query(db_rewrite_sql("SELECT * FROM {node} n WHERE type = '%s' AND title LIKE '%s%%'"), $type, addcslashes($keyword, '\%_'));
    $query = db_select('node', 'n')
      ->condition('type', $type);
    $query
      ->addField('n', 'title');
    $query = $query
      ->condition('title', '%' . $keyword . '%', 'LIKE')
      ->addTag('node_access');
    $result = $query
      ->execute();
    foreach ($result as $client) {
      $matches[$client->title] = $client->title;
    }
  }
  drupal_json_output($matches);
  exit;
}

/**
 * Implements hook_node_delete().
 */
function hosting_client_node_delete($node) {
  if ($node->type == 'platform') {
    db_delete('hosting_platform_client_access')
      ->condition('pid', $node->nid)
      ->execute();
  }
}

/**
 * Get accessible platforms that haven't been deleted or locked.
 *
 * 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 int $uid
 *   The user ID to retrieve the allowed platforms for. If none is specified the
 *   currently logged in user will be used.
 * @param bool $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\n                        FROM {node} n\n                        LEFT JOIN {hosting_platform} h\n                        ON n.nid = h.nid\n                        LEFT JOIN {hosting_platform_client_access} p\n                        ON n.nid = p.pid\n                        LEFT JOIN {hosting_client_user} c\n                        ON c.client = p.cid\n                        WHERE n.type = :type\n                        AND n.status = :nstatus\n                        AND h.status > :hstatus\n                        AND (c.user = :cuser OR p.pid IS NULL)", array(
      ':type' => 'platform',
      ':nstatus' => 1,
      ':hstatus' => HOSTING_PLATFORM_LOCKED,
      ':cuser' => $uid,
    ));
    foreach ($result as $server) {
      $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 HOSTING_CLIENT_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 HOSTING_CLIENT_MAX_GROUP_LENGTH
 */
function hosting_client_sanitize($title) {
  $prefix = variable_get('hosting_client_prefix', '');

  // Remove anything but "word characters", dots and dashes.
  $title = preg_replace("/^{$prefix}/", "", $title);

  // Remove the prefix in case we are validating an existing uname.
  $title = preg_replace("/[!\\W\\.\\-]/", "", $title);
  return substr(strtolower($prefix . $title), 0, HOSTING_CLIENT_MAX_GROUP_LENGTH);
}

/**
 * Implements hook_views_api().
 */
function hosting_client_views_api() {
  return array(
    'api' => 3,
    'path' => drupal_get_path('module', 'hosting_client') . '/includes/views',
  );
}

Functions

Namesort descending Description
hosting_client_autocomplete Retrieve autocomplete suggestions.
hosting_client_configure Menu callback for the module's settings.
hosting_client_delete Implements hook_delete().
hosting_client_form Implements hook_form().
hosting_client_form_site_node_form_alter Implements hook_form_FORM_ID_alter().
hosting_client_hosting_site_site_list_filters Implements hook_hosting_site_site_list_filters().
hosting_client_init Implements hook_init().
hosting_client_insert Implements hook_insert().
hosting_client_load Implements hook_load().
hosting_client_mail Implements hook_mail().
hosting_client_menu Implements hook_menu().
hosting_client_menu_access Menu access callback for the site creation tab in the client node.
hosting_client_node_access Implements hook_node_access().
hosting_client_node_delete Implements hook_node_delete().
hosting_client_node_info Implements hook_node_info().
hosting_client_permission Implements hook_permission().
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 Implements hook_theme().
hosting_client_update Implements hook_update().
hosting_client_users Return a list of users for a given client.
hosting_client_validate Implements hook_validate().
hosting_client_validate_suggest Helper for hosting_client_validate to suggest a new client name.
hosting_client_view Implements hook_view().
hosting_client_views_api Implements 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 Implements hook_nodeapi_TYPE_OP().
theme_hosting_client_form @todo Please document this function.
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 Get accessible platforms that haven't been deleted or locked.
_hosting_get_clients Get 25 clients in a paged query.

Constants

Namesort descending Description
HOSTING_CLIENT_MAX_GROUP_LENGTH The maximum length of a UNIX group