hosting_client.module

Allow clients to be created and granted permissions over sites, tasks etc.

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_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_node_load Implements hook_node_load().
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 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

File

client/hosting_client.module
View source
  1. <?php
  2. /**
  3. * @file
  4. * Allow clients to be created and granted permissions over sites, tasks etc.
  5. */
  6. include drupal_get_path('module', 'node') . '/node.pages.inc';
  7. include dirname(__FILE__) . '/hosting_client.access.inc';
  8. /**
  9. * Implements hook_init().
  10. */
  11. function hosting_client_init() {
  12. drupal_add_css(drupal_get_path('module', 'hosting_client') . '/hosting_client.css');
  13. }
  14. /**
  15. * The maximum length of a UNIX group
  16. *
  17. * Used to determine the sane maximum length of the internal name of a
  18. * client.
  19. *
  20. * @see hosting_client_sanitize()
  21. * @see http://community.aegirproject.org/node/557
  22. */
  23. define('HOSTING_CLIENT_MAX_GROUP_LENGTH', 16);
  24. /**
  25. * Implements hook_node_info().
  26. */
  27. function hosting_client_node_info() {
  28. $types["client"] = array(
  29. "name" => t('Client'),
  30. 'base' => 'hosting_client',
  31. "has_title" => TRUE,
  32. "title_label" => 'Client name',
  33. "description" => hosting_node_help("client"),
  34. "has_body" => 0,
  35. "body_label" => '',
  36. "min_word_count" => 0,
  37. );
  38. return $types;
  39. }
  40. /**
  41. * Implements hook_theme().
  42. */
  43. function hosting_client_theme($existing, $type, $theme, $path) {
  44. return array(
  45. 'hosting_client_user_form' => array(
  46. 'file' => 'hosting_client.access.inc',
  47. 'render element' => 'form',
  48. ),
  49. 'hosting_client_form' => array(
  50. 'file' => 'hosting_client.module',
  51. 'render element' => 'form',
  52. ),
  53. 'hosting_client_platform_access_form' => array(
  54. 'file' => 'hosting_client.module',
  55. 'render element' => 'form',
  56. ),
  57. );
  58. }
  59. /**
  60. * Implements hook_permission().
  61. */
  62. function hosting_client_permission() {
  63. return array(
  64. 'create client' => array(
  65. 'title' => t('create client'),
  66. ),
  67. 'view client' => array(
  68. 'title' => t('view client'),
  69. ),
  70. 'edit own client' => array(
  71. 'title' => t('edit own client'),
  72. ),
  73. 'delete own client' => array(
  74. 'title' => t('delete own client'),
  75. ),
  76. 'administer clients' => array(
  77. 'title' => t('administer clients'),
  78. ),
  79. 'edit client users' => array(
  80. 'title' => t('edit client users'),
  81. ),
  82. 'edit client uname' => array(
  83. 'title' => t('edit client uname'),
  84. ),
  85. );
  86. }
  87. /**
  88. * Implements hook_node_access().
  89. */
  90. function hosting_client_node_access($node, $op, $account) {
  91. if (!hosting_feature('client')) {
  92. // Multiple client support has been disabled for this site.
  93. return FALSE;
  94. }
  95. if (user_access('administer clients', $account)) {
  96. return TRUE;
  97. }
  98. else {
  99. switch ($op) {
  100. case 'create':
  101. return user_access('create client', $account);
  102. case 'view':
  103. 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();
  104. case 'update':
  105. if (user_access('edit own client', $account) && $account->uid == $node->uid) {
  106. return TRUE;
  107. }
  108. break;
  109. case 'delete':
  110. if ((user_access('delete own client', $account) && $account->uid == $node->uid)) {
  111. return TRUE;
  112. }
  113. break;
  114. default:
  115. break;
  116. }
  117. }
  118. }
  119. /**
  120. * Get a client by name or nid.
  121. *
  122. * @param int|object $client
  123. * Either the nid or the client title
  124. *
  125. * @return bool|object
  126. * The client node object of FALSE.
  127. */
  128. function hosting_get_client($client) {
  129. if (is_numeric($client)) {
  130. // @todo: confirm that we validate against numeric client names.
  131. return node_load($client);
  132. }
  133. else {
  134. if ($nodes = entity_load('node', FALSE, array('type' => 'client', 'title' => $client))) {
  135. return array_shift($nodes);
  136. }
  137. else {
  138. return FALSE;
  139. }
  140. }
  141. }
  142. /**
  143. * Get a client by internal name.
  144. *
  145. * @see hosting_get_client()
  146. *
  147. * @param string $uname
  148. * The internal name of this client.
  149. *
  150. * @return object|bool
  151. * The client node object of FALSE.
  152. */
  153. function hosting_get_client_by_uname($uname) {
  154. $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();
  155. if ($result) {
  156. return node_load($result);
  157. }
  158. else {
  159. return FALSE;
  160. }
  161. }
  162. /**
  163. * Implements hook_form().
  164. */
  165. function hosting_client_form(&$node, &$form_state) {
  166. $type = node_type_get_type('client');
  167. $form['title'] = array(
  168. '#type' => 'textfield',
  169. '#title' => check_plain($type->title_label),
  170. '#required' => TRUE,
  171. '#size' => 40,
  172. '#default_value' => isset($node->title) ? $node->title : '',
  173. '#maxlength' => 100,
  174. '#description' => t('The name of this client, generally the organization name or the contact name for individuals.'),
  175. );
  176. $form['uname'] = array(
  177. '#type' => 'machine_name',
  178. '#title' => t('Internal name'),
  179. '#size' => HOSTING_CLIENT_MAX_GROUP_LENGTH,
  180. '#maxlength' => HOSTING_CLIENT_MAX_GROUP_LENGTH,
  181. '#default_value' => isset($node->uname) ? $node->uname : '',
  182. '#access' => user_access('edit client uname'),
  183. '#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'))),
  184. '#machine_name' => array(
  185. 'exists' => 'hosting_get_client_by_uname',
  186. 'source' => array('title'),
  187. 'label' => t('Internal name'),
  188. 'replace_pattern' => '[^a-z0-9_-]+',
  189. 'replace' => '_',
  190. ),
  191. );
  192. if (!isset($node->nid) && variable_get('hosting_client_register_user', FALSE)) {
  193. $form['email'] = array(
  194. '#type' => 'textfield',
  195. '#title' => t('Email address'),
  196. '#description' => t('Email address of the contact created with this client. Optional - if none is provided, no user will be created with this client.'),
  197. '#required' => FALSE,
  198. '#size' => 40,
  199. '#default_value' => isset($node->email) ? $node->email : '',
  200. '#maxlength' => 100,
  201. );
  202. $form['email_confirm'] = array(
  203. '#type' => 'textfield',
  204. '#title' => t('Confirm Email address'),
  205. '#required' => FALSE,
  206. '#size' => 40,
  207. '#maxlength' => 100,
  208. );
  209. }
  210. if (isset($node->nid)) {
  211. $users = hosting_client_users($node);
  212. $user_list = array();
  213. foreach ($users as $uid => $uname) {
  214. $form['user_edit']['name'][$uid] = array(
  215. '#type' => 'markup',
  216. '#value' => l($uname, 'user/' . $uid),
  217. );
  218. $user_list[$uid] = '';
  219. }
  220. if (user_access('edit client users')) {
  221. $form['user_edit']['users'] = array(
  222. '#type' => 'checkboxes',
  223. '#options' => $user_list,
  224. );
  225. }
  226. $form['user_edit']['header'] = array(
  227. '#type' => 'value',
  228. '#value' => array(array('data' => t('Allowed users')), array('data' => t('Remove'))),
  229. );
  230. if (user_access('edit client users')) {
  231. $form['user_edit']['new_user'] = array(
  232. '#type' => 'textfield',
  233. '#title' => t('Associate a user to this Client'),
  234. '#weight' => 2,
  235. '#autocomplete_path' => 'user/autocomplete',
  236. '#description' => t('This field allows you to associate an existing system user to this Client.
  237. It does not create a new system user, but allows an existing user
  238. to manage sites belonging to the Client.'),
  239. );
  240. }
  241. $form['user_edit']['#theme'] = 'hosting_client_form';
  242. }
  243. else {
  244. global $user;
  245. $form['new_user'] = array(
  246. '#type' => 'value',
  247. '#value' => isset($user->name) ? $user->name : '',
  248. );
  249. }
  250. return $form;
  251. // @ignore security_fapi_markup
  252. }
  253. /**
  254. * @todo Please document this function.
  255. * @see http://drupal.org/node/1354
  256. */
  257. function theme_hosting_client_form($variables) {
  258. $form = $variables['form'];
  259. foreach (element_children($form['name'], FALSE) as $user) {
  260. $row = array();
  261. // @todo: Figure out why drupal_render isn't working here.
  262. //$row['data'][] = drupal_render($form['name'][$user]);
  263. $row['data'][] = $form['name'][$user]['#value'];
  264. if (user_access('edit client users')) {
  265. $row['data'][] = drupal_render($form['users'][$user]);
  266. }
  267. $rows[] = $row;
  268. }
  269. $output = theme('table', array('header' => $form['header']['#value'], 'rows' => $rows));
  270. $output .= drupal_render_children($form);
  271. return $output;
  272. }
  273. /**
  274. * Implements hook_validate().
  275. */
  276. function hosting_client_validate($node, $form, &$form_state) {
  277. // We don't allow duplicate client names.
  278. $title = $node->form_id == 'hosting_signup_form' ? 'client_name' : 'title';
  279. $node_nid = db_query("SELECT nid FROM {node} WHERE type = 'client' AND title = ':title'", array(':title' => $node->title))->fetchField();
  280. if ($node_nid && $node->nid != $node_nid) {
  281. form_set_error($title, t("Client name already in use, try %suggestion.",
  282. array('%suggestion' => hosting_client_validate_suggest($node->title))));
  283. }
  284. // We don't allow duplicate internal client names.
  285. if ($node->uname) {
  286. $node->uname = hosting_client_sanitize($node->uname);
  287. }
  288. else {
  289. $node->uname = hosting_client_sanitize($node->title);
  290. }
  291. // @todo convert this statement to DBTNG syntax.
  292. $nid = db_query("SELECT nid FROM {hosting_client} WHERE uname = ':uname'", array(':uname' => $node->uname))->fetchField();
  293. if ($nid && $node->nid != $nid) {
  294. form_set_error('uname', t("Client internal name already in use, try %suggestion.",
  295. array('%suggestion' => hosting_client_validate_suggest($node->uname, TRUE))));
  296. }
  297. if (!empty($node->nid) && !empty($node->email)) {
  298. $user = user_load_by_mail($node->email);
  299. if ($user) {
  300. form_set_error('email', t("Email address already exists."));
  301. }
  302. if ($node->email != $node->email_confirm) {
  303. form_set_error('email_confirm', t("Email addresses do not match"));
  304. }
  305. if (!valid_email_address($node->email)) {
  306. form_set_error('email', t("Email address invalid."));
  307. }
  308. }
  309. }
  310. /**
  311. * Helper for hosting_client_validate to suggest a new client name.
  312. *
  313. * @see hosting_client_validate()
  314. *
  315. * @param string $name
  316. * The client name being validated.
  317. * @param bool $internal
  318. * Whether this is an internal client name.
  319. *
  320. * @return string
  321. * Suggested client name.
  322. */
  323. function hosting_client_validate_suggest($name, $internal = FALSE) {
  324. $suggestion = FALSE;
  325. $table = $internal ? 'hosting_client' : 'node';
  326. $field = $internal ? 'uname' : 'title';
  327. $name = $internal ? hosting_client_sanitize($name) : $name;
  328. for ($i = 0; $i < 20; $i++) {
  329. // @todo convert this statement to DBTNG syntax.
  330. $nid = db_query("SELECT nid
  331. FROM {:table}
  332. WHERE uname
  333. LIKE '%:name%'
  334. ", array(
  335. ':table' => $table,
  336. ':field' => $field,
  337. ':name' => $name . $i,
  338. )
  339. )->fetchField();
  340. if (!$nid) {
  341. return $name . $i;
  342. }
  343. }
  344. }
  345. /**
  346. * Implements hook_insert().
  347. */
  348. function hosting_client_insert($node) {
  349. if (!empty($node->uname)) {
  350. $node->uname = hosting_client_sanitize($node->uname);
  351. }
  352. else {
  353. $node->uname = hosting_client_sanitize($node->title);
  354. }
  355. $id = db_insert('hosting_client')
  356. ->fields(array(
  357. 'vid' => $node->vid,
  358. 'nid' => $node->nid,
  359. 'uname' => $node->uname,
  360. ))
  361. ->execute();
  362. if (variable_get('hosting_client_register_user', FALSE)
  363. && user_load_multiple(array(), array('mail' => $node->email)) == FALSE) {
  364. $user = hosting_client_register_user($node);
  365. $node->uid = $user->uid;
  366. db_update('node')
  367. ->fields(array(
  368. 'uid' => $user->uid,
  369. ))
  370. ->condition('nid', $node->nid)
  371. ->execute();
  372. db_update('node_revision')
  373. ->fields(array(
  374. 'uid' => $user->uid,
  375. ))
  376. ->condition('vid', $node->vid)
  377. ->execute();
  378. }
  379. if (isset($node->new_user)) {
  380. $user = user_load_multiple(array(), array('name' => $node->new_user));
  381. $user = array_shift($user);
  382. if ($user) {
  383. $id = db_insert('hosting_client_user')
  384. ->fields(array(
  385. 'client' => $node->nid,
  386. 'user' => $user->uid,
  387. 'contact_type' => '',
  388. ))
  389. ->execute();
  390. }
  391. }
  392. }
  393. /**
  394. * Shortcut to get the rid of the 'aegir client' role.
  395. *
  396. * This hardcodes the 'aegir client' role name, so if it is changed,
  397. * this will fail.
  398. *
  399. * @todo the rid or role name should be a variable
  400. * @deprecated remove this function once the above setting is implemented
  401. */
  402. function _hosting_client_get_role() {
  403. return db_query("SELECT rid FROM {role} WHERE name = :name", array(':name' => 'aegir client'))->fetchField();
  404. }
  405. /**
  406. * Register a new user account for the client.
  407. *
  408. * This is a helper function for client forms that will create a user
  409. * alongside a new client if the hosting_client_register_user setting
  410. * is true.
  411. *
  412. * @see hosting_client_insert()
  413. */
  414. function hosting_client_register_user($node) {
  415. $pass = user_password();
  416. $user = new stdClass();
  417. $edit['name'] = $node->uname;
  418. $edit['hosting_client'] = $node->nid;
  419. $edit['mail'] = $node->email;
  420. $edit['pass'] = $pass;
  421. $edit['status'] = 1;
  422. $edit['roles'][_hosting_client_get_role()] = TRUE;
  423. $user = user_save($user, $edit);
  424. if ($user->uid && variable_get('hosting_client_send_welcome', FALSE)) {
  425. if ($node->client_name) {
  426. $to = sprintf("%s <%s>", $node->client_name, $node->email);
  427. }
  428. else {
  429. $to = $node->email;
  430. }
  431. $params = array(
  432. '!username' => $user->name,
  433. '!site' => variable_get('site_name', 'Drupal'),
  434. '!password' => $pass,
  435. '!uri' => $GLOBALS['base_url'],
  436. '!uri_brief' => substr($base_url, strlen('http://')),
  437. '!date' => format_date(REQUEST_TIME),
  438. '!login_uri' => url('user', array('absolute' => TRUE)),
  439. '!edit_uri' => url('user/' . $user->uid . '/edit', array('absolute' => TRUE)),
  440. '!login_url' => user_pass_reset_url($user),
  441. );
  442. // No e-mail verification is required.
  443. // Create new user account, and login user immediately.
  444. $language = user_preferred_language($user);
  445. drupal_mail('hosting_client', 'hosting-client-register-welcome', $to, $language, $params);
  446. }
  447. return $user;
  448. }
  449. /**
  450. * Implements hook_mail().
  451. */
  452. function hosting_client_mail($key, &$message, $params) {
  453. switch ($key) {
  454. case 'hosting-client-register-welcome':
  455. $message['subject'] = _hosting_client_mail_text('welcome_subject', $params);
  456. $message['body'] = _hosting_client_mail_text('welcome_body', $params);
  457. break;
  458. }
  459. }
  460. /**
  461. * Implements hook_update().
  462. *
  463. * As an existing node is being updated in the database, we need to do our own
  464. * database updates.
  465. */
  466. function hosting_client_update($node) {
  467. // If this is a new node or we're adding a new revision.
  468. if ($node->revision) {
  469. hosting_client_insert($node);
  470. }
  471. else {
  472. if ($node->uname) {
  473. $node->uname = hosting_client_sanitize($node->uname);
  474. }
  475. else {
  476. $node->uname = hosting_client_sanitize($node->title);
  477. }
  478. db_update('hosting_client')
  479. ->fields(array(
  480. 'uname' => $node->uname,
  481. ))
  482. ->condition('nid', $node->nid)
  483. ->execute();
  484. }
  485. if ($node->users) {
  486. foreach ($node->users as $user) {
  487. db_delete('hosting_client_user')
  488. ->condition('user', $user)
  489. ->condition('client', $node->nid)
  490. ->execute();
  491. }
  492. }
  493. if ($node->new_user) {
  494. $user = user_load_multiple(array(), array('name' => $node->new_user));
  495. $user = array_shift($user);
  496. $id = db_insert('hosting_client_user')
  497. ->fields(array(
  498. 'client' => $node->nid,
  499. 'user' => $user->uid,
  500. 'contact_type' => '',
  501. ))
  502. ->execute();
  503. }
  504. }
  505. /**
  506. * Implements hook_nodeapi_TYPE_OP().
  507. *
  508. * @see hosting_nodeapi()
  509. */
  510. function hosting_nodeapi_client_delete_revision(&$node) {
  511. db_delete('hosting_client')
  512. ->condition('vid', $node->vid)
  513. ->execute();
  514. }
  515. /**
  516. * Implements hook_delete().
  517. */
  518. function hosting_client_delete($node) {
  519. db_delete('hosting_client')
  520. ->condition('nid', $node->nid)
  521. ->execute();
  522. db_delete('hosting_client_user')
  523. ->condition('client', $node->nid)
  524. ->execute();
  525. }
  526. /**
  527. * Implements hook_load().
  528. */
  529. function hosting_client_load($nodes) {
  530. foreach ($nodes as $nid => &$node) {
  531. $additions = db_query('SELECT uname FROM {hosting_client} WHERE vid = :vid', array(':vid' => $node->vid))->fetch();
  532. foreach ($additions as $property => &$value) {
  533. $node->$property = $value;
  534. }
  535. }
  536. }
  537. /**
  538. * Implements hook_node_load().
  539. */
  540. function hosting_client_node_load($arg) {
  541. if (!is_numeric($arg)) {
  542. return FALSE;
  543. }
  544. if ($node = node_load_multiple(array($arg))) {
  545. if ($node->type == 'client') {
  546. return $node;
  547. }
  548. }
  549. return FALSE;
  550. }
  551. /**
  552. * Return a list of users for a given client.
  553. *
  554. * @param int|object $node
  555. * Client node (as nid or node object).
  556. *
  557. * @return array
  558. * Array of user names indexed by uid.
  559. */
  560. function hosting_client_users($node) {
  561. if (is_object($node)) {
  562. $node = $node->nid;
  563. }
  564. elseif (!is_numeric($node)) {
  565. return array();
  566. }
  567. // @todo: Figure out why variable substitution doesn't work here.
  568. 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();
  569. }
  570. /**
  571. * Implements hook_view().
  572. */
  573. function hosting_client_view($node, $teaser = FALSE, $page = FALSE) {
  574. $type = node_type_get_type($node);
  575. $node->content['info']['#prefix'] = '<div id="hosting-client-info">';
  576. $node->content['info']['title'] = array(
  577. '#type' => 'item',
  578. '#title' => $type->title_label,
  579. '#markup' => filter_xss($node->title),
  580. );
  581. $node->content['info']['uname'] = array(
  582. '#type' => 'item',
  583. '#title' => t('Internal name'),
  584. '#weight' => 1,
  585. '#markup' => filter_xss($node->uname),
  586. );
  587. $node->content['info']['#suffix'] = '</div>';
  588. if ($page) {
  589. $users = hosting_client_users($node);
  590. foreach ($users as $uid => $uname) {
  591. if (user_access('access user profiles') || ($uid == $GLOBALS['user']->uid)) {
  592. $rows[] = array(l($uname, 'user/' . $uid));
  593. }
  594. else {
  595. $rows[] = array($uname);
  596. }
  597. }
  598. $header = array(t('Allowed users'));
  599. $node->content['users_view'] = array(
  600. '#type' => 'item',
  601. '#value' => theme('table', array('header' => $header, 'rows' => $rows)),
  602. '#class' => 'client',
  603. '#prefix' => '<div id="hosting-site-list">',
  604. '#suffix' => '</div>',
  605. '#weight' => 11,
  606. );
  607. }
  608. return $node;
  609. }
  610. /**
  611. * Implements hook_hosting_site_site_list_filters().
  612. */
  613. function hosting_client_hosting_site_site_list_filters() {
  614. return array('client');
  615. }
  616. /**
  617. * Helper function to generate new client node during import.
  618. *
  619. * @param string $name
  620. * Client name.
  621. *
  622. * @return object
  623. * The node object of the generated client.
  624. */
  625. function hosting_import_client($name) {
  626. $client = hosting_get_client_by_uname($name);
  627. if (!$client) {
  628. $client = new stdClass();
  629. $client->type = 'client';
  630. $client->uid = 1;
  631. $client->title = trim($name);
  632. $client->status = 1;
  633. node_save($client);
  634. }
  635. return $client;
  636. }
  637. /**
  638. * Implements hook_menu().
  639. */
  640. function hosting_client_menu() {
  641. $items['node/%node/site/add'] = array(
  642. 'title' => 'Add site',
  643. 'description' => 'Add a site to the current client',
  644. 'page callback' => 'hosting_client_site_form',
  645. 'page arguments' => array('site_node_form', 1),
  646. 'access callback' => 'hosting_client_menu_access',
  647. 'access arguments' => array('create site', 1),
  648. 'type' => MENU_LOCAL_TASK,
  649. 'weight' => 5,
  650. );
  651. $items['hosting_client/autocomplete'] = array(
  652. 'title' => 'hosting client get client autocomplete',
  653. 'page callback' => 'hosting_client_autocomplete',
  654. 'access arguments' => array('access content'),
  655. 'type' => MENU_CALLBACK,
  656. );
  657. $items['admin/hosting/client'] = array(
  658. 'title' => 'Clients',
  659. 'page callback' => 'drupal_get_form',
  660. 'page arguments' => array('hosting_client_configure'),
  661. 'access callback' => '_hosting_client_configure_access',
  662. 'type' => MENU_LOCAL_TASK,
  663. );
  664. $items['hosting/clients/list'] = array(
  665. 'title' => 'List',
  666. 'type' => MENU_DEFAULT_LOCAL_TASK,
  667. 'weight' => -10,
  668. );
  669. $items['hosting/clients/add'] = array(
  670. 'title' => 'Add client',
  671. 'type' => MENU_LOCAL_TASK,
  672. 'page callback' => 'drupal_goto',
  673. 'page arguments' => array('node/add/client'),
  674. 'access callback' => 'node_access',
  675. 'access arguments' => array('create', 'client'),
  676. );
  677. $items['node/%node/clients'] = array(
  678. 'title' => 'Clients',
  679. 'type' => MENU_LOCAL_TASK,
  680. 'page callback' => 'drupal_get_form',
  681. 'page arguments' => array('hosting_client_platform_access_form', 1),
  682. 'access callback' => '_hosting_client_platform_access',
  683. 'access arguments' => array(1),
  684. );
  685. return $items;
  686. }
  687. /**
  688. * Access callback for the client platform access tab.
  689. */
  690. function _hosting_client_platform_access($node) {
  691. return $node->type == 'platform' &&
  692. $node->platform_status != HOSTING_PLATFORM_DELETED &&
  693. node_access('update', $node) &&
  694. user_access('administer clients');
  695. }
  696. /**
  697. * Page callback for the client platform access form.
  698. */
  699. function hosting_client_platform_access_form($form, $form_state, $node) {
  700. $form = array();
  701. $clients = isset($node->clients) ? $node->clients : array();
  702. if (count($clients)) {
  703. foreach ($clients as $client) {
  704. $client_node = hosting_get_client($client);
  705. $form['names'][$client] = array(
  706. '#type' => 'markup',
  707. '#value' => l($client_node->title, 'node/' . $client),
  708. );
  709. // Remove the label for the checkbox.
  710. $clients[$client] = '';
  711. }
  712. }
  713. else {
  714. // No access control on this platform.
  715. $form['names']['_all'] = array(
  716. '#type' => 'markup',
  717. '#value' => 'All clients have access to this platform.',
  718. );
  719. }
  720. $form['clients'] = array(
  721. '#type' => 'checkboxes',
  722. '#options' => $clients,
  723. );
  724. $form['header'] = array(
  725. '#type' => 'value',
  726. '#value' => array(
  727. array('data' => t('Allowed clients')),
  728. array('data' => t('Remove')),
  729. ),
  730. );
  731. $form['new_client'] = array(
  732. '#type' => 'textfield',
  733. '#title' => t('Grant a client access to this platform'),
  734. '#weight' => 2,
  735. '#autocomplete_path' => 'hosting_client/autocomplete/client',
  736. '#description' => t('This field allows you to grant a client access to this platform.
  737. Remove all clients from this list to grant all clients access to this platform'),
  738. );
  739. $form['#theme'] = 'hosting_client_platform_access_form';
  740. $form['apply'] = array(
  741. '#type' => 'submit',
  742. '#value' => t('Apply'),
  743. '#access' => user_access('administer clients'),
  744. '#submit' => array('hosting_client_platform_access_form_submit'),
  745. '#weight' => 10,
  746. );
  747. return $form;
  748. }
  749. /**
  750. * Callback to theme the client platform access form.
  751. */
  752. function theme_hosting_client_platform_access_form($variables) {
  753. $form = $variables['form'];
  754. foreach (element_children($form['names'], FALSE) as $client) {
  755. $row = array();
  756. $row['data'][] = $form['names'][$client]['#value'];
  757. if ($client != '_all' && user_access('administer clients')) {
  758. $row['data'][] = drupal_render($form['clients'][$client]);
  759. }
  760. else {
  761. // Nothing to remove if there aren't any clients...
  762. unset($form['header']['#value'][1]);
  763. }
  764. $rows[] = $row;
  765. }
  766. $output = theme('table', array('header' => $form['header']['#value'], 'rows' => $rows));
  767. $output .= drupal_render_children($form);
  768. return $output;
  769. }
  770. /**
  771. * Submit handler for the client platform access form.
  772. */
  773. function hosting_client_platform_access_form_submit($form, $form_state) {
  774. $clients = $form_state['values']['clients'];
  775. $new_client = isset($form_state['values']['new_client']) ? $form_state['values']['new_client'] : FALSE;
  776. $platform = $form_state['build_info']['args'][0];
  777. $existing = isset($platform->clients) ? $platform->clients : array();
  778. // Remove the selected client(s).
  779. foreach ($clients as $cid => $remove) {
  780. if ($remove) {
  781. db_delete('hosting_platform_client_access')
  782. ->condition('pid', $platform->nid)
  783. ->condition('cid', $cid)
  784. ->execute();
  785. }
  786. }
  787. // Add the new client.
  788. if ($new_client) {
  789. $client = hosting_get_client($new_client);
  790. if ($client) {
  791. db_insert('hosting_platform_client_access')
  792. ->fields(array(
  793. 'pid' => $platform->nid,
  794. 'cid' => $client->nid,
  795. ))
  796. ->execute();
  797. }
  798. else {
  799. $add_client = '';
  800. if (user_access('create client') || user_access('administer clients')) {
  801. $add_client = ' ' . t('or') . '' . l(t('add a new client'), 'node/add/client');
  802. }
  803. form_set_error('new_client', t('The client name (%client) was not recognized. Please try again', array('%client' => $new_client)) . $add_client . '.');
  804. }
  805. }
  806. }
  807. /**
  808. * Get the default value of the client field for a site node.
  809. *
  810. * @param object $node
  811. * The site to the the clients for.
  812. *
  813. * @return string|null
  814. * The client name.
  815. */
  816. function _hosting_client_site_default($node) {
  817. // Find the right client.
  818. global $user;
  819. $current_client_id = 0;
  820. if ($user->uid) {
  821. $client_ids = hosting_get_client_from_user($user->uid);
  822. $clients = array();
  823. foreach ($client_ids as $client_id => $client_permissions) {
  824. $client_id = $client_id ? $client_id : HOSTING_DEFAULT_CLIENT;
  825. $client = node_load($client_id);
  826. $clients[$client->title] = $client->title;
  827. if ((isset($node->client) && $node->client == $client_id) || !$current_client_id) {
  828. $current_client_id = $client_id;
  829. }
  830. }
  831. if (!$current_client_id && !user_access('administer clients')) {
  832. form_set_error('client', t('Your user is not associated with any clients so you are not allowed to create new sites'));
  833. }
  834. }
  835. if (!$current_client_id) {
  836. $current_client_id = HOSTING_DEFAULT_CLIENT;
  837. }
  838. // Allow admins to override.
  839. if (isset($node->client) && $node->client && user_access('administer clients')) {
  840. $current_client_id = $node->client;
  841. }
  842. $client = node_load($current_client_id);
  843. if (!$client) {
  844. // We give up, couldn't find a client, we're probably in a preview so
  845. // just use the node client.
  846. $client = new stdClass();
  847. $client->title = isset($node->client) ? $node->client : NULL;
  848. }
  849. return $client->title;
  850. }
  851. /**
  852. * Wrapper around the regular site_node_form that passes a dummy site with a proper client.
  853. */
  854. function hosting_client_site_form($form, $node) {
  855. $site = new stdClass();
  856. $site->type = 'site';
  857. $site->client = $node->nid;
  858. return drupal_get_form('site_node_form', $site);
  859. }
  860. /**
  861. * Menu access callback for the site creation tab in the client node.
  862. *
  863. * @see hosting_client_menu()
  864. */
  865. function hosting_client_menu_access($perm, $node) {
  866. if ($node->type == 'client') {
  867. return user_access($perm);
  868. }
  869. else {
  870. return FALSE;
  871. }
  872. }
  873. /**
  874. * Menu access callback for the client settings.
  875. *
  876. * @see hosting_client_menu()
  877. */
  878. function _hosting_client_configure_access() {
  879. return user_access('administer clients') && hosting_feature('client');
  880. }
  881. /**
  882. * Expand the client registration email message based on the variables.
  883. *
  884. * This will check for a custom message set in the
  885. * hosting_client_mail_welcome_subject and
  886. * hosting_client_mail_welcome_body variables.
  887. */
  888. function _hosting_client_mail_text($messageid, $variables = array()) {
  889. // Check if an admin setting overrides the default string.
  890. if ($admin_setting = variable_get('hosting_client_mail_' . $messageid, FALSE)) {
  891. return strtr($admin_setting, $variables);
  892. }
  893. switch ($messageid) {
  894. case 'welcome_subject':
  895. return t('Account details for !username at !site', $variables);
  896. case 'welcome_body':
  897. 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);
  898. }
  899. }
  900. /**
  901. * Menu callback for the module's settings.
  902. *
  903. * @see hosting_client_menu()
  904. */
  905. function hosting_client_configure($form, &$form_state) {
  906. $form['hosting_client_prefix'] = array(
  907. '#type' => 'textfield',
  908. '#title' => t('Client internal name prefix'),
  909. '#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.'),
  910. '#default_value' => variable_get('hosting_client_prefix', ''),
  911. '#size' => 5,
  912. '#maxlength' => 16,
  913. );
  914. $form['hosting_client_register_user'] = array(
  915. '#type' => 'checkbox',
  916. '#title' => t('Automatically create user accounts for new clients.'),
  917. '#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.'),
  918. '#default_value' => variable_get('hosting_client_register_user', FALSE),
  919. );
  920. // User e-mail settings.
  921. $form['email'] = array(
  922. '#type' => 'fieldset',
  923. '#title' => t('User e-mail settings'),
  924. );
  925. $form['email']['hosting_client_send_welcome'] = array(
  926. '#type' => 'checkbox',
  927. '#title' => t('Send welcome mail to new clients.'),
  928. '#description' => t('If this setting is on, new clients will receive a welcome email containing their login details.'),
  929. '#default_value' => variable_get('hosting_client_send_welcome', FALSE),
  930. );
  931. $form['email']['hosting_client_mail_welcome_subject'] = array(
  932. '#type' => 'textfield',
  933. '#title' => t('Subject of welcome e-mail'),
  934. '#default_value' => _hosting_client_mail_text('welcome_subject'),
  935. '#maxlength' => 180,
  936. '#description' => t('Customize the subject of your welcome e-mail, which is sent to new members upon registering.')
  937. . ' ' . t('Available variables are:') . ' !username, !site, !password, !uri, !uri_brief, !date, !login_uri, !edit_uri, !login_url.',
  938. );
  939. $form['email']['hosting_client_mail_welcome_body'] = array(
  940. '#type' => 'textarea',
  941. '#title' => t('Body of welcome e-mail'),
  942. '#default_value' => _hosting_client_mail_text('welcome_body'),
  943. '#rows' => 15,
  944. '#description' => t('Customize the body of the welcome e-mail, which is sent to new members upon registering.')
  945. . ' ' . t('Available variables are:') . ' !username, !site, !password, !uri, !uri_brief, !login_uri, !edit_uri, !login_url.',
  946. );
  947. return system_settings_form($form);
  948. }
  949. /**
  950. * Get 25 clients in a paged query.
  951. *
  952. * DEPRECATED, moved to the hosting_client_list view.
  953. */
  954. function _hosting_get_clients() {
  955. $return = array();
  956. $query = db_select('node', 'n')->extend('PagerDefault');
  957. $result = $query
  958. ->fields('n', array('nid', 'title'))
  959. ->condition('type', 'client')
  960. ->limit(25)
  961. ->addTag('node_access')
  962. ->execute();
  963. foreach ($result as $client) {
  964. $return[$client->nid] = $client->title;
  965. }
  966. return $return;
  967. }
  968. /**
  969. * Retrieve autocomplete suggestions.
  970. */
  971. function hosting_client_autocomplete($type, $keyword) {
  972. $matches = array();
  973. if ($type == 'client') {
  974. // @todo Confirm that these are equivalent, especially the db_rewrite_sql -> addTag('node_access')
  975. // $query = db_query(db_rewrite_sql("SELECT * FROM {node} n WHERE type = '%s' AND title LIKE '%s%%'"), $type, addcslashes($keyword, '\%_'));
  976. $query = db_select('node', 'n')
  977. ->condition('type', $type);
  978. $query->addField('n', 'title');
  979. $query = $query
  980. ->condition('title', '%' . $keyword . '%', 'LIKE')
  981. ->addTag('node_access');
  982. $result = $query->execute();
  983. foreach ($result as $client) {
  984. $matches[$client->title] = $client->title;
  985. }
  986. }
  987. drupal_json_output($matches);
  988. exit();
  989. }
  990. /**
  991. * Implements hook_node_delete().
  992. */
  993. function hosting_client_node_delete($node) {
  994. if ($node->type == 'platform') {
  995. db_delete('hosting_platform_client_access')
  996. ->condition('pid', $node->nid)
  997. ->execute();
  998. }
  999. }
  1000. /**
  1001. * Get accessible platforms that haven't been deleted or locked.
  1002. *
  1003. * We can get called a few times during a page request, so we implement static
  1004. * caching for speed, and because the platforms that are available are unlikely
  1005. * to change during a single page request.
  1006. *
  1007. * @param int $uid
  1008. * The user ID to retrieve the allowed platforms for. If none is specified the
  1009. * currently logged in user will be used.
  1010. * @param bool $reset
  1011. * Whether to reset the internal static cache or not.
  1012. *
  1013. * @todo this is not the right way. we need to implement node-level
  1014. * access permissions here, the same way we do for sites. see
  1015. * http://drupal.org/node/725952
  1016. */
  1017. function _hosting_get_allowed_platforms($uid = NULL, $reset = FALSE) {
  1018. static $platforms = array();
  1019. if ($reset) {
  1020. $platforms = array();
  1021. }
  1022. if (is_null($uid)) {
  1023. global $user;
  1024. $uid = $user->uid;
  1025. }
  1026. if (!isset($platforms[$uid])) {
  1027. $platforms[$uid] = array();
  1028. $result = db_query("SELECT n.nid, n.title
  1029. FROM {node} n
  1030. LEFT JOIN {hosting_platform} h
  1031. ON n.nid = h.nid
  1032. LEFT JOIN {hosting_platform_client_access} p
  1033. ON n.nid = p.pid
  1034. LEFT JOIN {hosting_client_user} c
  1035. ON c.client = p.cid
  1036. WHERE n.type = :type
  1037. AND n.status = :nstatus
  1038. AND h.status > :hstatus
  1039. AND (c.user = :cuser OR p.pid IS NULL)",
  1040. array(
  1041. ':type' => 'platform',
  1042. ':nstatus' => 1,
  1043. ':hstatus' => HOSTING_PLATFORM_LOCKED,
  1044. ':cuser' => $uid,
  1045. )
  1046. );
  1047. foreach ($result as $server) {
  1048. $platforms[$uid][$server->nid] = $server->title;
  1049. }
  1050. }
  1051. return $platforms[$uid];
  1052. }
  1053. /**
  1054. * Return a machine-usable name for a client.
  1055. *
  1056. * This aims to be usable for unix group/user names and shells.
  1057. *
  1058. * It adds a prefix configured in the frontend settings (defaulting to
  1059. * 'cl-'), and strips the total length of the string (including the
  1060. * prefix) to HOSTING_CLIENT_MAX_GROUP_LENGTH.
  1061. *
  1062. * This can also be used to validate user-provided client unames, as
  1063. * it strips and readds the prefix and performs the same validation
  1064. * and corrections on the field.
  1065. *
  1066. * This is inspired from the context sanitization stuff.
  1067. *
  1068. * @see HOSTING_CLIENT_MAX_GROUP_LENGTH
  1069. */
  1070. function hosting_client_sanitize($title) {
  1071. $prefix = variable_get('hosting_client_prefix', '');
  1072. // Remove anything but "word characters", dots and dashes.
  1073. $title = preg_replace("/^$prefix/", "", $title);
  1074. // Remove the prefix in case we are validating an existing uname.
  1075. $title = preg_replace("/[!\W\.\-]/", "", $title);
  1076. return substr(strtolower($prefix . $title), 0, HOSTING_CLIENT_MAX_GROUP_LENGTH);
  1077. }
  1078. /**
  1079. * Implements hook_views_api().
  1080. */
  1081. function hosting_client_views_api() {
  1082. return array(
  1083. 'api' => 3,
  1084. 'path' => drupal_get_path('module', 'hosting_client') . '/includes/views',
  1085. );
  1086. }