. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @file * Code for the Mica Data Access feature. */ include_once('mica_data_access.features.inc'); /** * Creates default content after Features successfully installed * Implements hook_features_rebuild_completed() */ function mica_data_access_features_rebuild_completed() { if (!variable_get('mica_data_access_features_rebuild_completed', FALSE)) { _mica_data_access_create_default_content(); variable_set('mica_data_access_features_rebuild_completed', TRUE); } } function _mica_data_access_create_default_content() { $data_access_page = mica_core_add_page( st('Data Access'), st('This section aims to inform researchers about the consortium data access policy and its committee. It provides a simple data request web form as an example of what can be achieved for managing researcher\'s requests. As usual, the content and the form of this section can be easily customized.'), 'data-access'); mica_core_create_node_menu($data_access_page, FALSE, TRUE, 20); $data_access_path = 'node/' . $data_access_page->nid; $policy_page = mica_core_add_page( st('Consortium Policies and Guidelines'), st('In planning the Consortium, the Scientific Planning Committee recognized the importance of generating a document that would be communicated widely, and contain sufficient information to allow funding agencies and scientists in many countries to make decisions on future participation. Incomplete scientific knowledge (such as tumor heterogeneity for many cancers), rapidly evolving technologies e.g., next generation sequencing technologies, diversity of funding mechanisms, and differences across nations in regards to informed consent and/or sharing of samples across international boundaries are examples of issues that were considered by the committee and its working groups. The approach adopted by the planning teams has been to define a limited number of principles that are central to participation in the project, and provide recommendations to readers based on what is considered “best practices” at the time of writing this document. The authors attempted to discriminate essential from recommended principles using the terms “policy” and “guideline”.

What is a consortium policy?

A consortium policy is a principle which consortium members agree to follow, during the course of the project. Although policies will likely be long-lasting, the Consortium will periodically review its policies.

What is a consortium guideline?

Consortium guidelines refer to recommendations made by Consortium working groups that offer advice as to what is believed to constitute “best practices” at a given time. Given the rapid evolution in technologies or new knowledge gleaned from the data generated by Consortium or other groups, it is expected that guidelines will evolve. It is also expected that approaches will need to vary based on tumor types, local laws, or other factors. In such cases, it is expected that Consortium members will be able to compare and explain differences in approaches, relative to Consortium guidelines. The Consortium has chosen to make most of its recommendations as guidelines rather than policies to allow flexibility in approaches and promote innovation. In this document, guidelines are often written in blue-shaded boxes. In this first document prepared by the Consortium, the authors strived to differentiate recommendations that are policy from those that are guidelines (even if some issues are clearly a mix of both). It is up to individual projects that join the Consortium to declare a clear plan, e.g., samples, criteria for being a sample, exons used, quality control, etc. Over time, the Consortium will generate best practices documents that will describe the current state-of-the-art, and propose modifications of the guidelines.'), 'data-access/policy'); mica_core_create_node_menu($policy_page, FALSE, FALSE, 0, $data_access_path); mica_core_create_menu(st('Data Access Administration'), 'data-access/admin', 'data-access/admin', FALSE, FALSE, 1, $data_access_path); mica_core_create_menu(st('Data Access Committee'), 'data-access/committee', 'data-access/committee', FALSE, FALSE, 2, $data_access_path); mica_core_create_menu(st('Data Access Request'), 'data-access/request', 'data-access/request', FALSE, FALSE, 3, $data_access_path); mica_core_create_menu(st('My Data Access Requests'), 'data-access/requests', 'data-access/requests', FALSE, FALSE, 4, $data_access_path); $research_menu = mica_core_find_menu_for_alias('research'); mica_core_create_menu(st('DACO Approved Projects'), 'data-access/approved', 'data-access/approved', FALSE, FALSE, 1, $research_menu->link_path); } /** * Implements hook_form(). */ function mica_data_access_form($node, &$form_state) { return node_content_form($node, $form_state); } /** * Implements hook_menu(). */ function mica_data_access_menu() { $items = array(); $items['data-access/request'] = array( 'title' => 'Request for Data Access', 'page callback' => 'mica_data_access_request_add', 'access callback' => TRUE, 'type' => MENU_LOCAL_ACTION, 'file path' => drupal_get_path('module', 'node'), 'file' => 'node.pages.inc', ); $items['node/%node/review'] = array( 'title' => 'Review Data Access Request', 'page callback' => 'mica_data_access_request_review', 'page arguments' => array(1), 'access callback' => 'mica_data_access_request_review_access', 'access arguments' => array(1), 'type' => MENU_LOCAL_ACTION, ); return $items; } /** * Implements hook_node_access(). */ function mica_data_access_node_access($node, $op, $account) { $type = is_string($node) ? $node : $node->type; if ($type === 'data_access_request' && $op === 'update' && !mica_data_access_can_edit_request($node)) { return NODE_ACCESS_DENY; } return NODE_ACCESS_IGNORE; } /** * Implements hook_menu_local_tasks_alter(). */ function mica_data_access_menu_local_tasks_alter(&$data, $router_item, $root_path) { $links = array(); if ($root_path === 'data-access/approved' || $root_path === 'node/%/queries/%' || $root_path === 'data-access/requests') { if (node_access('create', 'data_access_request')) { $href = url('data-access/request'); if ($root_path === 'node/%/queries/%') { $dataset_node = $router_item['page_arguments'][1]; $href = url('data-access/request', array('query' => array('field_dataset_access' => $dataset_node->nid))); } $links['add-data-access-request'] = array( '#theme' => 'menu_local_action', '#link' => array( 'title' => t('Request for Data Access', array('@url' => $href)), 'localized_options' => array('html' => TRUE), ), ); } } if ($root_path === 'data-access/request') { global $user; if ($user->uid == 0 && !node_access('create', 'data_access_request')) { $links['login'] = array( '#theme' => 'menu_local_action', '#link' => array( 'title' => t('Log in or Register to submit a Data Access Request.', array( '@login' => url('user/login', array('query' => drupal_get_destination())), '@register' => url('user/register', array('query' => drupal_get_destination())), )), 'localized_options' => array('html' => TRUE), ), ); } } if ($root_path === 'node/%') { $explode = explode('/', $data['tabs'][0]['output'][0]['#link']['href']); $node = node_load($explode[1]); if ($node && $node->type === 'data_access_request') { $href = 'javascript:window.print();'; $links['print'] = array( '#theme' => 'menu_local_action', '#link' => array( 'title' => t('Print', array('@url' => $href)), 'localized_options' => array('html' => TRUE), ), ); } } $data['actions']['output'] = array_merge($data['actions']['output'], $links); } function mica_data_access_can_edit_request($node) { $node_loaded = node_load($node->nid); if (isset($node_loaded->field_data_access_review[LANGUAGE_NONE][0]['nid'])) { $review_id = $node_loaded->field_data_access_review[LANGUAGE_NONE][0]['nid']; $result = db_query("SELECT * from {field_data_field_review_status} WHERE " . "entity_type = 'node' AND " . "bundle = 'data_access_review' AND " . "entity_id = " . $review_id . " AND " . "field_review_status_value IN ('accepted', 'rejected', 'submitted') "); return $result->rowCount() == 0; } return TRUE; } /** * Implements hook_field_attach_form() */ function mica_data_access_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) { if ($entity_type == 'node' && $entity->type == 'data_access_request') { // remove the data access review field unset($form['field_data_access_review']); // set default values provided as arguments (if any) if (array_key_exists('field_dataset_access', $_GET)) { $form['field_dataset_access'][LANGUAGE_NONE]['#default_value'] = array($_GET['field_dataset_access']); } } elseif ($entity_type == 'node' && $entity->type == 'data_access_review') { unset($form['field_data_access']); unset($form['title']); } } function mica_data_access_request_add() { if (node_access('create', 'data_access_request')) { $content = node_add('data_access_request'); drupal_set_title(t('Request for Data Access')); return $content; } else { global $user; drupal_set_title(t('Request for Data Access is restricted')); $output = 'You must have an account before submitting any data access application to the Data Access Officer.'; if ($user->uid != 0) { $output = '

' . t('Submitting a Data Access Request is restricted to authorized users.') . '

'; } return $output; } } function mica_data_access_request_review_access($node, $op = NULL) { if ($node->type != 'data_access_request') { return FALSE; } if (!empty($node->field_data_access_review)) { return FALSE; } return node_access('create', 'data_access_review'); } function mica_data_access_request_review($node) { global $user; if (empty($node->field_data_access_review)) { // create a data access review associated to this request $review_node = new stdClass(); $review_node->type = 'data_access_review'; node_object_prepare($review_node); $review_node->title = $node->title; $review_node->language = $node->language; $review_node->uid = $user->uid; // current user is the author $review_node->status = $node->status; // same publication status $review_node->field_data_access[$node->language] = array(array('nid' => $node->nid)); $review_node->field_review_status[$node->language] = array(array('value' => 'submitted')); node_save($review_node); watchdog('mica', 'Created data access review %review for request %request by user %user', array( '%review' => $review_node->nid, '%request' => $node->nid, '%user' => $user->uid, ), WATCHDOG_INFO); $node->field_data_access_review[$node->language] = array(array('nid' => $review_node->nid)); node_save($node); } // go to the associated review drupal_goto('node/' . $node->field_data_access_review[$node->language][0]['nid']); } /** * Get the first contact associated with the provided user. * @param user $user * @return FALSE or the result of contact node entity query */ function mica_data_access_reviewer_contact($user) { $field = field_info_field('field_daco'); $query = new EntityFieldQuery; $query->entityCondition('entity_type', 'node') ->entityCondition('bundle', 'contact') ->fieldCondition($field, 'uid', $user->uid); $entities = $query->execute(); if (!empty($entities)) { return array_shift($entities['node']); } else { return FALSE; } } function mica_data_access_delete($node) { $node_loaded = node_load($node->nid); if ($node_loaded->type === 'data_access_request') { if (isset($node_loaded->field_data_access_review[LANGUAGE_NONE][0]['nid'])) { $nid_review_to_delete = $node_loaded->field_data_access_review[LANGUAGE_NONE][0]['nid']; mica_data_access_delete_without_calling_delete_hook(array($nid_review_to_delete)); } } if ($node_loaded->type === 'data_access_review') { $field = field_info_field('field_data_access_review'); $query = new EntityFieldQuery; $query->entityCondition('entity_type', 'node') ->entityCondition('bundle', 'data_access_request') ->fieldCondition($field, 'nid', $node_loaded->nid); $entity = $query->execute(); $keys = array_keys($entity['node']); $nid_request_to_delete = $keys[0]; mica_data_access_delete_without_calling_delete_hook(array($nid_request_to_delete)); } } /** * Copy of node_delete_multiple from node.module * BUT don't call node_invoke($node, 'delete') to avoid hook loop * * @param $nids * An array of node IDs. */ function mica_data_access_delete_without_calling_delete_hook($nids) { $transaction = db_transaction(); if (!empty($nids)) { $nodes = node_load_multiple($nids, array()); try { foreach ($nodes as $nid => $node) { // Call the node-specific callback (if any): //node_invoke($node, 'delete'); module_invoke_all('node_delete', $node); module_invoke_all('entity_delete', $node, 'node'); field_attach_delete('node', $node); // Remove this node from the search index if needed. // This code is implemented in node module rather than in search module, // because node module is implementing search module's API, not the other // way around. if (module_exists('search')) { search_reindex($nid, 'node'); } } // Delete after calling hooks so that they can query node tables as needed. db_delete('node') ->condition('nid', $nids, 'IN') ->execute(); db_delete('node_revision') ->condition('nid', $nids, 'IN') ->execute(); db_delete('history') ->condition('nid', $nids, 'IN') ->execute(); db_delete('node_access') ->condition('nid', $nids, 'IN') ->execute(); } catch (Exception $e) { $transaction->rollback(); watchdog_exception('node', $e); throw $e; } // Clear the page and block and node_load_multiple caches. entity_get_controller('node')->resetCache(); } }