You are here

lingotek.module in Lingotek Translation 7.4

File

lingotek.module
View source
<?php

/**
 * @file
 * Module core functionality.
 */
include_once 'lingotek.define.inc';
include_once 'lingotek.session.inc';
include_once 'lingotek.reference.inc';
include_once 'lingotek.util.inc';
include_once 'lingotek.api.inc';
include_once 'lingotek.sync.inc';
include_once 'lingotek.batch.inc';
include_once 'lingotek.bulk_grid.inc';

/**
 * Auto-loader function used for upgrades
 * @param type $class
 */
function lingotek_auto_loader($class) {
  $module_location = drupal_get_path('module', 'lingotek');
  $paths = array(
    $module_location,
    $module_location . '/lib/Drupal/lingotek/',
  );
  foreach ($paths as $path) {
    $filename = $path . $class . '.php';
    if (file_exists($filename)) {
      include $filename;
    }
  }
}

/**
 * Implements hook_menu().
 */
function lingotek_menu() {
  $items = array();
  $items[LINGOTEK_MENU_MAIN_BASE_URL] = array(
    //main menu bar link including module description
    'title' => 'Translation',
    'access arguments' => array(
      'administer lingotek',
    ),
    'description' => "Translate and keep your site translated using Lingotek.",
    'file' => 'lingotek.admin.inc',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'lingotek_admin_form',
    ),
  );
  $items['node/%node/lingotek_pm'] = array(
    'title' => 'Translations',
    'access arguments' => array(
      1,
      'manage projects',
    ),
    'access callback' => 'lingotek_access',
    // Enterprise Only Function
    'file' => 'lingotek.page.inc',
    'page arguments' => array(
      1,
    ),
    'page callback' => 'lingotek_pm',
    'weight' => 1,
    'type' => MENU_LOCAL_TASK,
  );
  $items['node/%/lingotekworkbench/%'] = array(
    'title' => 'Workbench',
    'access arguments' => array(
      'translation',
    ),
    'file' => 'lingotek.page.inc',
    'page arguments' => array(
      0,
      1,
      3,
    ),
    'page callback' => 'lingotek_workbench_redirect',
    'weight' => 1,
    'type' => MENU_CALLBACK,
  );
  $items['comment/%/lingotekworkbench/%'] = array(
    'title' => 'Workbench',
    'access arguments' => array(
      'translation',
    ),
    'file' => 'lingotek.page.inc',
    'page arguments' => array(
      0,
      1,
      3,
    ),
    'page callback' => 'lingotek_workbench_redirect',
    'weight' => 1,
    'type' => MENU_CALLBACK,
  );
  $items['node/%node/lingotek_dev'] = array(
    'title' => 'Lingotek Developer Tools',
    'access callback' => 'lingotek_access_dev_tools',
    // Enterprise Only Function
    'access arguments' => array(
      1,
      'use lingotek developer tools',
    ),
    'description' => 'Developer Tools',
    'file' => 'lingotek.dev.inc',
    'page callback' => 'lingotek_dev_page',
    'page arguments' => array(
      1,
    ),
    'weight' => 2,
    'type' => MENU_LOCAL_TASK,
  );

  // Use the Standard Dashboard Interface
  // Lingotek Dashboard and Settings Tabs
  $items[LINGOTEK_MENU_MAIN_BASE_URL] = array_merge($items[LINGOTEK_MENU_MAIN_BASE_URL], array(
    'file' => 'lingotek.dashboard.inc',
    'page callback' => 'lingotek_dashboard',
    'weight' => 1,
    'type' => MENU_NORMAL_ITEM,
  ));
  $items[LINGOTEK_MENU_MAIN_BASE_URL . '/dashboard'] = array(
    'title' => 'Dashboard',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'file' => 'lingotek.dashboard.inc',
    'page callback' => 'lingotek_dashboard',
    'weight' => 1,
    'access arguments' => array(
      'administer lingotek',
    ),
  );
  $items[LINGOTEK_MENU_LANG_BASE_URL . '/dashboard'] = $items[LINGOTEK_MENU_MAIN_BASE_URL . '/dashboard'];
  $items[LINGOTEK_MENU_LANG_BASE_URL] = $items[LINGOTEK_MENU_MAIN_BASE_URL];

  // Add a link to regional settings
  $items[LINGOTEK_MENU_LANG_BASE_URL]['title'] = 'Lingotek Translation';
  $items[LINGOTEK_MENU_MAIN_BASE_URL]['access callback'] = 'lingotek_access_tlmi';

  // Tab:  Settings
  $items[LINGOTEK_MENU_MAIN_BASE_URL . '/settings'] = array(
    'title' => 'Settings',
    'type' => MENU_LOCAL_TASK,
    'weight' => 5,
    'access arguments' => array(
      'administer lingotek',
    ),
    'description' => 'Community Translation Settings',
    'file' => 'lingotek.admin.inc',
    'page callback' => 'lingotek_admin_configuration_view',
  );
  $items[LINGOTEK_MENU_LANG_BASE_URL . '/settings'] = $items[LINGOTEK_MENU_MAIN_BASE_URL . '/settings'];
  $items[LINGOTEK_MENU_MAIN_BASE_URL . '/settings/profile/%'] = array(
    'type' => MENU_CALLBACK,
    'access arguments' => array(
      'administer lingotek',
    ),
    'file' => 'lingotek.admin.inc',
    'page callback' => 'lingotek_admin_profile_manage',
    'page arguments' => array(
      5,
    ),
  );

  // Tab: Bulk Management
  $items[LINGOTEK_MENU_MAIN_BASE_URL . '/manage/sync'] = array(
    'title' => 'Sync',
    'type' => MENU_LOCAL_TASK,
    'weight' => 4,
    'access arguments' => array(
      'administer lingotek',
    ),
    'description' => 'Bulk management of content translations',
    'file' => 'lingotek.sync.inc',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'lingotek_form_bulk_sync',
    ),
  );
  $items[LINGOTEK_MENU_LANG_BASE_URL . '/manage/sync'] = $items[LINGOTEK_MENU_MAIN_BASE_URL . '/manage/sync'];

  // Content Update Notifications Callback
  $items[LINGOTEK_NOTIFY_URL] = array(
    'title' => 'Content Translation Notifications',
    'type' => MENU_CALLBACK,
    'access arguments' => array(
      'access content',
    ),
    'description' => 'When content translations are ready, this receives the notifications.',
    'file' => 'lingotek.sync.inc',
    'page callback' => 'lingotek_notifications',
  );
  $items[LINGOTEK_MENU_MAIN_BASE_URL . '/notifications'] = $items[LINGOTEK_NOTIFY_URL];

  // allow early auto-provision community callback urls to wor
  // Dashboard:  Ajax Command Processor
  $items['lingotek/target'] = array(
    'type' => MENU_CALLBACK,
    'access arguments' => array(
      'administer lingotek',
    ),
    'description' => 'Lingotek Command Router',
    'file' => 'lingotek.dashboard.inc',
    'page callback' => 'lingotek_dashboard_command_ajax',
  );

  // Admin Configuration
  $items[LINGOTEK_MENU_LANG_BASE_URL . '/config'] = array(
    'title' => 'Lingotek Configuration Options',
    'description' => 'The configuration options',
    'file' => 'lingotek.admin.inc',
    'page callback' => 'lingotek_admin_configuration_view',
    'type' => MENU_CALLBACK,
    'access arguments' => array(
      'administer lingotek',
    ),
  );
  $items['lingotek/sync/comment/%'] = array(
    'access arguments' => array(
      'administer comments',
    ),
    'file' => 'lingotek.page.inc',
    'page callback' => 'page_sync_comment_translations',
    'page arguments' => array(
      3,
    ),
    'type' => MENU_CALLBACK,
  );
  $items['lingotek/mark-phases-complete/%node'] = array(
    'access arguments' => array(
      'access content',
    ),
    'file' => 'includes/lingotek.ajax.inc',
    'page callback' => 'lingotek_page_mark_phases_complete',
    'page arguments' => array(
      2,
    ),
    'type' => MENU_CALLBACK,
  );

  //Tab: Bulk Grid
  $items[LINGOTEK_MENU_MAIN_BASE_URL . '/manage'] = array(
    'title' => 'Manage',
    'type' => MENU_LOCAL_TASK,
    'weight' => 5,
    'access arguments' => array(
      'administer lingotek',
    ),
    'description' => 'Bulk grid of content translations',
    'file' => 'lingotek.bulk_grid.inc',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'lingotek_bulk_grid_form',
    ),
  );
  $items[LINGOTEK_MENU_LANG_BASE_URL . '/manage'] = $items[LINGOTEK_MENU_MAIN_BASE_URL . '/manage'];
  $items[LINGOTEK_MENU_MAIN_BASE_URL . '/manage/list'] = array(
    'title' => 'Content',
    'description' => 'Bulk grid of content translations',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -8,
  );
  $items[LINGOTEK_MENU_LANG_BASE_URL . '/manage/list'] = $items[LINGOTEK_MENU_MAIN_BASE_URL . '/manage/list'];
  $items[LINGOTEK_MENU_MAIN_BASE_URL . '/manage/customize'] = array(
    'access arguments' => array(
      'administer lingotek',
    ),
    'file' => 'lingotek.bulk_grid.inc',
    'page callback' => 'lingotek_grid_customize',
    'page arguments' => array(),
    'type' => MENU_CALLBACK,
  );
  $items[LINGOTEK_MENU_MAIN_BASE_URL . '/manage/filters'] = array(
    'access arguments' => array(
      'administer lingotek',
    ),
    'file' => 'lingotek.bulk_grid.inc',
    'page callback' => 'lingotek_filters_popup',
    'page arguments' => array(),
    'type' => MENU_CALLBACK,
  );
  $items[LINGOTEK_MENU_MAIN_BASE_URL . '/manage/filters/clear'] = array(
    'access arguments' => array(
      'administer lingotek',
    ),
    'file' => 'lingotek.bulk_grid.inc',
    'page callback' => 'lingotek_grid_clear_filters_page',
    'page arguments' => array(),
    'type' => MENU_CALLBACK,
  );
  $items[LINGOTEK_MENU_MAIN_BASE_URL . '/manage/update'] = array(
    'access arguments' => array(
      'administer lingotek',
    ),
    'file' => 'lingotek.bulk_grid.inc',
    'page callback' => 'lingotek_grid_update',
    'page arguments' => array(),
    'type' => MENU_CALLBACK,
  );
  $items[LINGOTEK_MENU_MAIN_BASE_URL . '/manage/download-ready'] = array(
    'access arguments' => array(
      'administer lingotek',
    ),
    'file' => 'lingotek.bulk_grid.inc',
    'page callback' => 'lingotek_grid_download_ready',
    'page arguments' => array(),
    'type' => MENU_CALLBACK,
  );
  $items[LINGOTEK_MENU_MAIN_BASE_URL . '/manage/edit/%'] = array(
    'access arguments' => array(
      'administer lingotek',
    ),
    'file' => 'lingotek.bulk_grid.inc',
    'page callback' => 'lingotek_edit_nodes',
    'page arguments' => array(
      5,
    ),
    'type' => MENU_CALLBACK,
  );
  $items[LINGOTEK_MENU_MAIN_BASE_URL . '/manage/change-workflow/%'] = array(
    'access arguments' => array(
      'administer lingotek',
    ),
    'file' => 'lingotek.bulk_grid.inc',
    'page callback' => 'lingotek_change_workflow',
    'page arguments' => array(
      5,
    ),
    'type' => MENU_CALLBACK,
  );
  $items[LINGOTEK_MENU_MAIN_BASE_URL . '/manage/reset/%'] = array(
    'access arguments' => array(
      'administer lingotek',
    ),
    'file' => 'lingotek.page.inc',
    'page callback' => 'lingotek_disassociate_nodes',
    'page arguments' => array(
      5,
    ),
    'type' => MENU_CALLBACK,
  );
  $items[LINGOTEK_MENU_MAIN_BASE_URL . '/manage/delete/%'] = array(
    'access arguments' => array(
      'administer lingotek',
    ),
    'file' => 'lingotek.page.inc',
    'page callback' => 'lingotek_disassociate_nodes',
    'page arguments' => array(
      5,
    ),
    'type' => MENU_CALLBACK,
  );

  // Batch Process Pages ----------------------------------------
  // Sync Progress Report
  $items['lingotek/sync/report'] = array(
    'type' => MENU_CALLBACK,
    'access arguments' => array(
      'administer lingotek',
    ),
    'description' => 'Lingotek Sync Endpoint',
    'file' => 'lingotek.sync.inc',
    'page callback' => 'lingotek_sync_endpoint',
  );
  $items[LINGOTEK_MENU_LANG_BASE_URL . '/identify-content'] = array(
    'title' => 'Test',
    'type' => MENU_CALLBACK,
    'weight' => 30,
    'file' => 'lib/Drupal/batch/lingotek.batch.inc',
    'page callback' => 'lingotek_batch_identify_content',
    'access arguments' => array(
      'administer lingotek',
    ),
  );

  // Easy Install Screens ----------------------------------------
  // Setup Path Router
  $items['admin/config/lingotek/setup'] = array(
    'title' => 'Lingotek Setup Path Router',
    'description' => 'Figures out the necessary setup path.',
    'file' => 'lingotek.setup.inc',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'lingotek_setup',
    ),
    'type' => MENU_CALLBACK,
    'access arguments' => array(
      'administer lingotek',
    ),
  );

  // New Account
  $items['admin/config/lingotek/new-account'] = array(
    'title' => 'Create a Lingotek Account',
    'description' => 'Setup a new Lingotek account.',
    'file' => 'lingotek.setup.inc',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'lingotek_setup_new_account_form',
    ),
    'type' => MENU_CALLBACK,
    'access arguments' => array(
      'administer lingotek',
    ),
  );

  // Current Users
  $items['admin/config/lingotek/account-settings'] = array(
    'title' => 'Lingotek Account Login',
    'description' => 'Manage your Lingotek account settings.',
    'file' => 'lingotek.setup.inc',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'lingotek_setup_account_settings_form',
    ),
    'type' => MENU_CALLBACK,
    'access arguments' => array(
      'administer lingotek',
    ),
  );
  $items['admin/config/lingotek/community-select'] = array(
    'title' => 'Choose Your Community',
    'description' => 'Select a community to use with this site.',
    'file' => 'lingotek.setup.inc',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'lingotek_community_select_form',
    ),
    'type' => MENU_CALLBACK,
    'access arguments' => array(
      'administer lingotek',
    ),
  );
  $items['admin/config/lingotek/project-vault-select'] = array(
    'title' => 'Choose Your Project, Workflow, and TM Vault',
    'description' => 'Select project, workflow, and translation memory vault to use.',
    'file' => 'lingotek.setup.inc',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'lingotek_project_vault_select_form',
    ),
    'type' => MENU_CALLBACK,
    'access arguments' => array(
      'administer lingotek',
    ),
  );

  // Standard Screens
  $items['admin/config/lingotek/language-settings'] = array(
    'title' => 'Choose Your Languages',
    'description' => 'Manage your Lingotek language settings.',
    'file' => 'lingotek.setup.inc',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'lingotek_setup_language_settings_form',
    ),
    'type' => MENU_CALLBACK,
    'access arguments' => array(
      'administer lingotek',
    ),
  );
  $items['admin/config/lingotek/node-translation-settings'] = array(
    'title' => 'Enable Content Types',
    'description' => 'Select the nodes and fields you want translated.',
    'file' => 'lingotek.setup.inc',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'lingotek_setup_node_translation_settings_form',
    ),
    'type' => MENU_CALLBACK,
    'access arguments' => array(
      'administer lingotek',
    ),
  );
  $items['admin/config/lingotek/comment-translation-settings'] = array(
    'title' => 'Enable Comment Translation',
    'description' => 'Select any node types you want the comments translated.',
    'file' => 'lingotek.setup.inc',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'lingotek_setup_comment_translation_settings_form',
    ),
    'type' => MENU_CALLBACK,
    'access arguments' => array(
      'administer lingotek',
    ),
  );
  $items['admin/config/lingotek/additional-translation-settings'] = array(
    'title' => 'Enable Additional Translation',
    'description' => 'Select any additional types you want translated.',
    'file' => 'lingotek.setup.inc',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'lingotek_setup_additional_translation_settings_form',
    ),
    'type' => MENU_CALLBACK,
    'access arguments' => array(
      'administer lingotek',
    ),
  );
  $items['admin/config/lingotek/content-type-choose-fields-ajax/%'] = array(
    'title' => 'Content Types Ajax',
    'description' => 'Ajax functionality for content options.',
    'file' => 'lingotek.setup.inc',
    'page callback' => 'lingotek_content_type_choose_fields_callback',
    'page arguments' => array(
      4,
    ),
    'type' => MENU_CALLBACK,
    'access arguments' => array(
      'administer lingotek',
    ),
  );
  $items['admin/config/lingotek/setup-language-switcher'] = array(
    'title' => 'Enable Language Switcher',
    'description' => 'Enable the default language switcher',
    'file' => 'lingotek.setup.inc',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'lingotek_setup_language_switcher_form',
    ),
    'type' => MENU_CALLBACK,
    'access arguments' => array(
      'administer lingotek',
    ),
  );
  $items['admin/config/lingotek/setup-complete'] = array(
    'title' => 'Setup Complete',
    'description' => 'The Lingotek Translation module has been configured and is now ready to use.',
    'file' => 'lingotek.setup.inc',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'lingotek_setup_complete_form',
    ),
    'type' => MENU_CALLBACK,
    'access arguments' => array(
      'administer lingotek',
    ),
  );
  $items['admin/config/lingotek/node-updates'] = array(
    'title' => 'Lingotek Node Updates',
    'description' => 'Updates your nodes to support multiple languages.',
    'file' => 'lingotek.setup.inc',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'lingotek_setup_node_updates_form',
    ),
    'type' => MENU_CALLBACK,
    'access arguments' => array(
      'administer lingotek',
    ),
  );
  return $items;
}

/**
 * Implements hook_menu_alter().
 */
function lingotek_menu_alter(&$items) {

  // If the Entity Translation module is enabled alongside Lingotek,
  // allow nodes that haven't yet been associated with Lingotek to be
  // translated using Entity Translation's tools.
  if (module_exists('entity_translation')) {
    $translate_path = 'node/%node/translate';
    $lingotek_access_callback = 'lingotek_entity_translation_node_tab_access';
    if (isset($items[$translate_path])) {
      $items[$translate_path]['title'] = 'Translate';
      $items[$translate_path]['access callback'] = $lingotek_access_callback;
    }
    $menu_targets = array(
      'node/%node/translate/add/%entity_translation_language/%entity_translation_language',
      'node/%node/translate/delete/%entity_translation_language',
    );
    foreach ($menu_targets as $target) {
      if (isset($items[$target])) {
        $items[$target]['access callback'] = $lingotek_access_callback;
      }
    }
    $edit_target = 'node/%node/translate/edit/%entity_translation_language';
    if (isset($items[$edit_target])) {
      $items[$edit_target]['access callback'] = 'lingotek_entity_translation_edit_access';
      $items[$edit_target]['access arguments'][3] = $lingotek_access_callback;
    }
  }
}

/**
 * Implements hook_module_implements_alter().
 */
function lingotek_module_implements_alter(&$implementations, $hook) {
  switch ($hook) {
    case 'menu_alter':

      //case 'entity_info_alter':

      // Move some of our hook implementations to the end of the list.
      $group = $implementations['lingotek'];
      unset($implementations['lingotek']);
      $implementations['lingotek'] = $group;
      break;
  }
}

/**
 * Entity Translation access callback when enabled with Lingotek Translation module.
 *
 * @return bool
 *   TRUE if the user should be able to access the requested resource, FALSE otherwise.
 *
 * @see entity_translation_node_tab_access().
 */
function lingotek_entity_translation_node_tab_access() {
  $args = func_get_args();
  $node = array_shift($args);
  if ($node->language !== LANGUAGE_NONE) {
    if (lingotek_supported_type($node->type) || lingotek_managed_by_entity_translation($node->type)) {

      // If the node has been pushed then return FALSE (don't show the "Translate" tab)
      // Otherwise, return TRUE (show the "Translate" tab)
      $user_access = drupal_multilingual() && (user_access('translate any entity') || user_access("translate node entities"));
      $translate_tab_access = $node->lingotek['profile'] == LingotekSync::PROFILE_DISABLED && $user_access;
      return $translate_tab_access;
    }
    elseif (entity_translation_node_supported_type($node->type)) {

      // This is not a Lingotek-supported type, but is a type
      // that is set up for Entity Translation.
      // Fall back to the Entity Translation module's access callback.
      return entity_translation_tab_access('node', $node);
    }
    elseif (entity_translation_node('node', $node) && module_exists('translation')) {

      // This node is set up to use the Translation module's
      // node translation (as opposed to Entity Translation).
      return _translation_tab_access($node);
    }
  }
  return FALSE;
}

/**
 * Entity Translation edit access callback when enabled with Lingotek Translation module.
 *
 * @return bool
 *   TRUE if the user should be able to access the requested resource, FALSE otherwise.
 *
 * @see entity_translation_edit_access()
 */
function lingotek_entity_translation_edit_access($entity_type, $entity, $langcode) {
  $translations = entity_translation_get_handler($entity_type, $entity)
    ->getTranslations();

  // If a translations for the given language does not exist we cannot edit it.
  if (!isset($translations->data[$langcode])) {
    return FALSE;
  }

  // Invoke the actual callback with its arguments.
  $args = func_get_args();
  return call_user_func_array($args[3], array_slice($args, 4));
}

/**
 * Implements hook_permission().
 */
function lingotek_permission() {
  $permissions = array(
    'administer lingotek' => array(
      'title' => 'Administer Lingotek',
      'description' => t('Access the administrative settings for the module.'),
    ),
    'translation' => array(
      'title' => 'Translate',
      'description' => t('Access to the translate content (e.g., the "Translations" tab will be available on nodes, comments will be translatable)'),
    ),
    'manage projects' => array(
      'title' => 'Project Management',
      'description' => t('Access the Lingotek tab on content types (Must also have permission to edit the content type in question).'),
    ),
    'use lingotek developer tools' => array(
      'title' => 'Developer',
      'description' => t('Access developer tools useful for detailed information and debugging'),
    ),
  );
  return $permissions;
}

/**
 * Implements hook_form_BASE_FORM_ID_alter().
 *
 * @param array $form
 *   A FAPI form array for the form being altered.
 * @param array $form_state
 *   A FAPI form state array for the form being altered.
 * @param string $form_id
 *   The ID of the form being altered.
 */
function lingotek_form_node_form_alter(&$form, $form_state, $form_id) {

  // On node create, set the language select box to our source language, if this is a node content type that we translate for, if the form is in Add mode.
  if (!isset($form['nid']['#value'])) {

    // add mode (no node id set)
    if (lingotek_supported_type($form['type']['#value'])) {

      // lingotek translated content type
      $form['language']['#default_value'] = lingotek_get_source_language();
    }
  }
  if (!user_access('manage projects')) {
    return;
  }
  $is_enterprise = LingotekAccount::instance()
    ->isEnterprise();
  if (isset($form['nid']['#value'])) {
    $nid = $form['nid']['#value'];
    $node = lingotek_node_load_default($nid);
  }
  else {
    $node = $form['#node'];
    lingotek_node_load(array(
      $node,
    ), array());
  }
  global $language;
  $drupal_language_code = $language->language;
  if ($drupal_language_code != $node->language && lingotek_node_pushed($node)) {
    $lingotek_locale = Lingotek::convertDrupal2Lingotek($drupal_language_code);
    LingotekSync::setTargetStatus($node->nid, $lingotek_locale, LingotekSync::STATUS_PENDING);

    //set to pending so any changes will be downloaded on next sync
    $language_text = lingotek_language_field_lookup('native', $lingotek_locale) . " (" . $drupal_language_code . ")";
    $translation_edit_link = lingotek_get_workbench_url($node->lingotek['document_id'], $lingotek_locale, t('Edit Translation: @language_text', array(
      '@language_text' => $language_text,
    )));
    $edit_translation_message = t('Editing the fields below will only change the content of the source language, not the translation.');
    $edit_translation_message .= "<br/>" . $translation_edit_link;
    drupal_set_message($edit_translation_message, 'warning', FALSE);
  }
  if (!isset($nid)) {
    $nid = NULL;
  }
  $form = array_merge($form, lingotek_get_node_settings_form($form, $form_state, $node));
  $api = LingotekApi::instance();

  // show workflow-change option only if there are more than one workflow and if it's an existing node
  $workflows = $api
    ->listWorkflows();
  if ($workflows && count($workflows) > 1) {
    $form['lingotek']['workflow_id'] = array(
      '#type' => 'select',
      '#title' => t('Workflow'),
      '#prefix' => '<div id="prefill-phases-div">',
      '#description' => t('Choose the Workflow to associate with this content item.'),
      '#default_value' => $node->lingotek['workflow_id'],
      '#options' => $workflows,
      '#empty_option' => '(select one)',
    );
    if (!$nid) {

      // don't show prefill stuff on new nodes
      $form['lingotek']['workflow_id']['#suffix'] = '</div>';
    }
    else {

      // add the callback and checkbox and phase-select
      $form['lingotek']['workflow_id']['#ajax'] = array(
        'callback' => 'lingotek_get_change_workflow_form_callback',
        'wrapper' => 'prefill-phases-div',
        'method' => 'replace',
        'effect' => 'fade',
      );
      $form['lingotek']['prefill_phases_checkbox'] = array(
        '#type' => 'checkbox',
        '#title' => t('Restore to a phase in the new workflow'),
        '#default_value' => TRUE,
        '#description' => t('Prefill the new workflow with translations from the previous workflow'),
        '#states' => array(
          'invisible' => array(
            ':input[name="lingotek[workflow_id]"]' => array(
              'value' => $node->lingotek['workflow_id'],
            ),
          ),
        ),
      );
      $form['lingotek']['prefill_phase_select'] = array(
        '#title' => t("Desired Prefill Phase"),
        '#description' => t('Please select the highest phase which should be prefilled for the new workflow'),
        '#type' => 'select',
        '#states' => array(
          'visible' => array(
            ':input[name="lingotek[workflow_id]"]' => array(
              '!value' => $node->lingotek['workflow_id'],
            ),
            array(
              ':input[name="lingotek[prefill_phases_checkbox]"]' => array(
                'checked' => TRUE,
              ),
            ),
          ),
        ),
        '#suffix' => '</div>',
      );
      if (isset($form_state['values']['lingotek']['workflow_id']) && $form_state['values']['lingotek']['workflow_id'] != NULL) {
        $form['lingotek']['prefill_phase_select']['#options'] = lingotek_get_phases_by_workflow_id($form_state['values']['lingotek']['workflow_id']);
      }
      else {
        $form['lingotek']['prefill_phase_select']['#options'] = array(
          '-1' => '(first choose a workflow)',
        );
        $form['lingotek']['prefill_phase_select']['#disabled'] = TRUE;
      }
    }
  }
  if ($is_enterprise) {

    // Only show these options if the Lingotek document hasn't yet been created.
    if (!$node->lingotek['document_id'] && class_exists('LingotekApi')) {

      // Available projects.
      if ($projects = $api
        ->listProjects()) {
        $form['lingotek']['project_id'] = array(
          '#type' => 'select',
          '#title' => 'Project',
          '#description' => t('Select the translation project with which this item should be associated.'),
          '#default_value' => $node->lingotek['project_id'],
          '#options' => $projects,
        );
      }

      // Translation Memory (TM) Vault.
      if ($vaults = $api
        ->listVaults()) {
        $form['lingotek']['vault_id'] = array(
          '#type' => 'select',
          '#title' => t('TM Vault'),
          '#description' => t('Choose the TM vault to associate with this content item.'),
          '#default_value' => $node->lingotek['vault_id'],
          '#options' => $vaults,
        );
      }
    }

    // END:  Document not created yet
  }
  $form['lingotek']['content_end'] = array(
    '#markup' => '</div>',
  );
  $form['lingotek']['advanced'] = array(
    '#type' => 'fieldset',
    '#title' => t('Advanced'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
    '#group' => 'developer_settings',
    '#access' => user_access('use lingotek developer tools'),
  );
  $form['lingotek']['advanced']['document_id'] = array(
    '#type' => 'textfield',
    '#title' => t('Document Id'),
    '#description' => t("Read/Overwrite the document ID associated with the document.  This can break the translation process but can also be used to help figure out if something is wrong."),
    '#default_value' => $node->lingotek['document_id'],
  );
  $form['lingotek']['advanced']['current_lingonode'] = array(
    '#type' => 'textarea',
    '#title' => t('Node Data'),
    '#value' => isset($nid) ? json_encode(lingotek_lingonode($nid)) : t('None'),
    '#disabled' => TRUE,
    '#attributes' => array(
      'rows' => '2',
    ),
  );
}
function lingotek_get_node_settings_form($form, &$form_state, $node = NULL) {
  $second_run = isset($form_state['input']['op']);
  $bulk_grid = FALSE;
  $multiple = FALSE;

  //$new_node = isset($node->nid) ? 0 : 1;
  if (isset($form_state['nids'])) {
    $nids = $form_state['nids'];
    $bulk_grid = TRUE;
    $multiple = count($nids) > 1;
    if (!$multiple) {
      $node = lingotek_node_load_default(reset($nids));
    }
    else {
      if (!$second_run) {
        drupal_set_message(t('You will be changing the settings for @number nodes.', array(
          '@number' => count($nids),
        )), 'warning');
      }
      $node = new stdClass();
      $node->lingotek = lingotek_get_global_profile();
      $node->lingotek['profile'] = LingotekSync::PROFILE_DISABLED;

      // Note: Consider making this default 'Automatic' (after it is a fixed profile; can't be deleted)
    }
  }
  drupal_add_css(drupal_get_path('module', 'lingotek') . '/style/form.css');
  $is_enterprise = LingotekAccount::instance()
    ->isEnterprise();
  $workbench_moderation_enabled = module_exists('workbench_moderation');
  $title = t('Translation Management');

  // Vertical Tab.
  $form['lingotek'] = array(
    '#title' => t('Translation management'),
    '#type' => 'fieldset',
    '#collapsible' => !$bulk_grid,
    '#collapsed' => !$bulk_grid,
    '#group' => 'additional_settings',
    '#attributes' => array(
      'id' => array(
        'lingotek_fieldset',
      ),
    ),
    '#attached' => array(
      'js' => array(
        drupal_get_path('module', 'lingotek') . '/js/lingotek.form.js',
      ),
    ),
    '#modal' => TRUE,
    '#tree' => TRUE,
  );
  if (isset($node->tnid) && $node->tnid != 0 && $node->tnid != $node->nid) {
    $form['lingotek']['note'] = array(
      '#markup' => t('This is a target node for the language code: @lang. To change
        the Lingotek settings please edit the source node.', array(
        '@lang' => $node->language,
      )),
    );
    return $form;

    //this is a target node and thus should not have lingotek settings
  }
  if (isset($nids)) {
    $form['lingotek']['nids'] = array(
      '#type' => 'hidden',
      '#default_value' => json_encode($nids),
    );
  }
  if (!$bulk_grid) {

    // $node will be set because $multiple has to be false
    $form['lingotek']['note'] = array(
      '#type' => 'item',
      '#title' => $title,
      '#description' => t('Please select a language for Lingotek to use as the source language.  The source language cannot be language neutral.'),
    );
  }

  // END: Entity Translation Settings
  $form['lingotek']['lingotek_note'] = array(
    '#type' => 'item',
    '#title' => $title,
    '#description' => t("The Lingotek Translation module was developed to help you translate your site. The module integrates the Lingotek translation management system directly into Drupal, so that your users can leverage the power of Lingotek's translation tools and services without ever having to leave the comfort of your Drupal environment."),
  );
  $profiles = lingotek_get_profiles();
  $profile_options = array();
  foreach ($profiles as $key => $profile) {
    $profile_options[$key] = $profile['name'];
  }
  $profile_options[LingotekSync::PROFILE_CUSTOM] = 'Custom';
  $profile_options[LingotekSync::PROFILE_DISABLED] = 'Disabled';
  $form['lingotek']['profile'] = array(
    '#type' => 'select',
    '#title' => 'Translation Profile',
    '#options' => $profile_options,
    '#default_value' => $node->lingotek['profile'],
  );
  if ($node->lingotek['node_sync_status'] == LingotekSync::STATUS_DISABLED) {
    $form['lingotek']['profile']['#default_value'] = LingotekSync::PROFILE_DISABLED;
  }
  $form['lingotek']['node_sync_status'] = array(
    '#type' => 'value',
    '#value' => $node->lingotek['node_sync_status'],
  );
  $form['lingotek']['content_begin'] = array(
    '#markup' => '<div id="edit-lingotek-content" class="form-wrapper">',
  );
  if (!$bulk_grid) {
    $form['lingotek']['lingotek_nodes_translation_method'] = array(
      '#type' => 'radios',
      '#title' => t('Method for storing translations'),
      '#options' => array(
        'field' => '<strong>Fields</strong> (Recommended) - all translations are stored in a single node',
        'node' => '<strong>Nodes</strong>  - create a new node per language ',
      ),
      'field' => array(
        '#description' => 'A newer and recommended method. It appears newer versions of Drupal will use this method.',
      ),
      'node' => array(
        '#description' => 'The classical method. Use for backwards compatibility.',
      ),
      '#disabled' => isset($node->nid),
      '#default_value' => $node->lingotek['lingotek_nodes_translation_method'],
    );
  }
  $form['lingotek']['create_lingotek_document'] = array(
    '#type' => 'checkbox',
    '#title' => t('Upload Content Automatically'),
    '#default_value' => $node->lingotek['create_lingotek_document'],
    '#description' => t('When enabled, your Drupal content (including saved edits) will automatically be uploaded to Lingotek for translation.<br/>When disabled, you are required to manually upload your content by clicking the "Upload" button on the Translations tab.'),
  );
  $show_moderation = FALSE;
  if ($workbench_moderation_enabled) {

    /*foreach ($nodes as $node) {
        $show_moderation = workbench_moderation_node_type_moderated($node->type);
        if ($show_moderation) {
        break;
        }
      }*/
    $show_moderation = $bulk_grid || workbench_moderation_node_type_moderated($node->type);
    if ($show_moderation) {
      $form['lingotek']['create_lingotek_document_workbench_moderation'] = array(
        '#type' => 'select',
        '#title' => t('Workbench Moderation Upload'),
        '#field_prefix' => t('Upload to Lingotek when node reaches'),
        '#field_suffix' => t('state.'),
        '#options' => lingotek_get_workbench_moderation_states(),
        '#default_value' => $multiple ? workbench_moderation_state_published() : $node->lingotek['create_lingotek_document_workbench_moderation'],
        '#description' => 'The most recent revision will be automatically uploaded when changed to this state.',
        '#states' => array(
          'invisible' => array(
            ':input[id="edit-lingotek-create-lingotek-document"]' => array(
              'checked' => FALSE,
            ),
          ),
        ),
      );
    }
  }
  $form['lingotek']['sync_method'] = array(
    '#type' => 'checkbox',
    '#title' => t('Download Translations Automatically'),
    '#default_value' => $node->lingotek['sync_method'],
    '#description' => t('When enabled, completed translations will automatically be downloaded from Lingotek.<br/>When disabled, you are required to manually download translations by clicking the "Download" button on the Translations tab.'),
  );
  if ($workbench_moderation_enabled) {
    $default_download_state = 'no_moderation';
    if (!$multiple && isset($node->nid)) {
      $default_download_state = $node->lingotek['sync_method_workbench_moderation'];
    }
    if ($show_moderation) {
      $transition_select = lingotek_workbench_moderation_get_mult_transitions();
      $form['lingotek']['sync_method_workbench_moderation'] = array(
        '#type' => 'select',
        '#title' => t('Workbench Moderation Download'),
        '#field_suffix' => t('after translations have downloaded from Lingotek.'),
        '#options' => lingotek_get_workbench_moderation_options(),
        '#default_value' => $default_download_state,
        '#description' => t("Transition will not occur until <i>all</i> of the node's translations have downloaded."),
        '#states' => array(
          'invisible' => array(
            ':input[id="edit-lingotek-syncmethod"]' => array(
              'checked' => FALSE,
            ),
          ),
        ),
      );
    }
  }
  if ($is_enterprise) {

    // Community Translation
    $form['lingotek']['allow_community_translation'] = array(
      '#type' => 'checkbox',
      '#title' => t('Allow Crowdsourced Translation'),
      '#description' => t('When enabled, anonymous site visitors will be presented with a link allowing them to contribute translations for this node.'),
      '#default_value' => $node->lingotek['allow_community_translation'],
    );

    // URL Alias Translation.
    $form['lingotek']['url_alias_translation'] = array(
      '#type' => 'select',
      '#title' => t('URL Alias Translation'),
      '#default_value' => $node->lingotek['url_alias_translation'],
      '#options' => lingotek_get_url_alias_translations(),
      '#description' => t('Choose how you would like to translate the URL alias. The last option requires that you install both the Title and Pathauto modules, define a path pattern, and check "Enable Lingotek Translation" for the Title field.'),
    );
  }
  if ($bulk_grid) {
    $form['submit'] = array(
      '#type' => 'submit',
      '#value' => t('Submit'),
      '#submit' => array(
        'lingotek_get_node_settings_form_submit',
      ),
    );
  }
  return $form;
}

/*
 * This is not a hook. This is a helper function to save all lingotek related data.
 */
function lingotek_node_save($node) {
  if (!isset($node->lingotek)) {

    // load default lingotek settings when alternate node creation paths are employed (e.g., commons)
    lingotek_node_load(array(
      $node,
    ), array());
  }
  $has_not_been_uploaded = !isset($node->lingotek['document_id']) || empty($node->lingotek['document_id']);
  $disabled_selected = $node->lingotek['profile'] == LingotekSync::PROFILE_DISABLED;
  $disabled = isset($node->lingotek['node_sync_status']) && $node->lingotek['node_sync_status'] == LingotekSync::STATUS_DISABLED;
  if ($disabled_selected && !$disabled) {
    LingotekSync::setNodeEnabled($node->nid, FALSE);
  }
  elseif (!$disabled_selected && $disabled) {
    LingotekSync::setNodeEnabled($node->nid, TRUE);
  }
  $remove = lingotek_get_profile_fields($has_not_been_uploaded, TRUE);
  $remove[] = 'profile';
  db_delete('lingotek')
    ->condition('nid', $node->nid)
    ->condition('lingokey', $remove, 'IN')
    ->execute();
  if ($node->lingotek['profile'] == LingotekSync::PROFILE_CUSTOM) {
    $ignore = array(
      'et_content',
      'lingotek_push_once',
      'lingotek_disabled',
      'advanced',
      'node_sync_status',
      'document_id',
      'prefill_phases_checkbox',
      'prefill_phase_select',
    );
    $query = db_insert('lingotek')
      ->fields(array(
      'nid',
      'lingokey',
      'lingovalue',
    ));
    foreach ($node->lingotek as $key => $value) {
      if (array_search($key, $ignore) === FALSE) {
        $query
          ->values(array(
          $node->nid,
          $key,
          $value,
        ));
      }
    }
    $query
      ->execute();

    // check for a workflow change
    if (isset($node->lingotek['workflow_id']) && isset($node->lingotek['advanced']['document_id'])) {
      $curr_workflow = $node->lingotek['workflow_id'];
      $document_id = $node->lingotek['advanced']['document_id'];
      if (!isset($node->original->lingotek['workflow_id']) || $node->original->lingotek['workflow_id'] != $curr_workflow) {
        $api = LingotekApi::instance();
        $prefill_checked = isset($node->lingotek['prefill_phases_checkbox']) ? $node->lingotek['prefill_phases_checkbox'] : NULL;
        $prefill_phase = $prefill_checked ? $node->lingotek['prefill_phase_select'] : NULL;
        $result = $api
          ->changeWorkflow(array(
          $document_id,
        ), $curr_workflow, $prefill_phase);
        LingotekSync::setAllTargetStatus($node->nid, LingotekSync::STATUS_PENDING);
      }
    }
  }
  else {

    // Specific Profile
    $node_defaults = lingotek_load_profile_defaults('node');
    $profile_overridden = !isset($node_defaults[$node->type]['profile']) || $node_defaults[$node->type]['profile'] != $node->lingotek['profile'];
    if ($profile_overridden) {
      lingotek_lingonode($node->nid, 'profile', $node->lingotek['profile']);
    }
    else {
      lingotek_lingonode_variable_delete($node->nid, 'profile');
    }
  }

  //This reloads the lingovalues to ensure that the proper defaults and hierarchy are used.

  //We clear out the lingovalues first, then wrap in an array, call the lingotek load function, and then unwrap from array.
  unset($node->lingotek);
  $nodes = array(
    $node->nid => $node,
  );
  $types = array(
    $node->type,
  );
  lingotek_node_load($nodes, $types);
  $node = $nodes[$node->nid];
  return $node;
}
function lingotek_node_save_readonly($node) {
  lingotek_lingonode($node->nid, 'project_id', $node->lingotek['project_id']);
  lingotek_lingonode($node->nid, 'vault_id', $node->lingotek['vault_id']);
  lingotek_lingonode($node->nid, 'workflow_id', $node->lingotek['workflow_id']);
  lingotek_lingonode($node->nid, 'lingotek_nodes_translation_method', $node->lingotek['lingotek_nodes_translation_method']);
}
function lingotek_get_node_settings_form_submit($form, $form_state) {
  if (isset($form_state['nids']) && !empty($form_state['nids'])) {
    $nids = $form_state['nids'];

    // YOU LEFT OFF HERE, SEND UP THE CALL TO UPDATE THE WORKFLOW_ID FOR EACH NODE ALREADY UPLOADED
    foreach ($nids as $nid) {
      $node = node_load($nid);
      $node->lingotek = array_replace($node->lingotek, $form_state['values']['lingotek']);
      node_save($node);
    }
  }
}
function lingotek_get_change_workflow_form_submit($form, $form_state) {
  if (isset($form_state['nids']) && !empty($form_state['nids'])) {
    $nids = $form_state['nids'];
    $workflow_id = $form_state['values']['lingotek']['workflow_id'];
    if (isset($form_state['values']['lingotek']['prefill_phases_checkbox']) && $form_state['values']['lingotek']['prefill_phases_checkbox']) {
      $prefill_phase = $form_state['values']['lingotek']['prefill_phase_select'];
    }
    else {
      $prefill_phase = NULL;
    }

    // SUBMIT THE WORKFLOW CHANGES TO TMS
    $document_ids = LingotekSync::getDocIdsFromNodeIds($nids);
    if (!$document_ids) {
      return;
    }
    $api = LingotekApi::instance();
    $api
      ->changeWorkflow($document_ids, $workflow_id, $prefill_phase);

    // CREATE/UPDATE WORKFLOW ENTRIES IN THE LINGOTEK METADATA TABLE
    foreach ($nids as $nid) {
      lingotek_lingonode($nid, 'workflow_id', $workflow_id);
      LingotekSync::setAllTargetStatus($nid, LingotekSync::STATUS_PENDING);
    }
  }
}
function lingotek_get_global_profile() {
  return array(
    'name' => '',
    'document_id' => NULL,
    'lingotek_nodes_translation_method' => variable_get('lingotek_nodes_translation_method'),
    'create_lingotek_document' => 0,
    'sync_method' => 0,
    'allow_community_translation' => 0,
    'url_alias_translation' => 0,
    'node_sync_status' => LingotekSync::STATUS_EDITED,
    'sync_method_workbench_moderation' => 'no_moderation',
    'create_lingotek_document_workbench_moderation' => 'published',
    'project_id' => variable_get('lingotek_project'),
    'workflow_id' => variable_get('lingotek_workflow'),
    'vault_id' => variable_get('lingotek_vault'),
  );
}
function lingotek_get_profiles() {
  $profiles = variable_get('lingotek_profiles', array());
  if (empty($profiles)) {
    $profiles[] = array(
      'name' => 'Automatic',
      'create_lingotek_document' => 1,
      'sync_method' => 1,
    );
    $profiles[] = array(
      'name' => 'Manual',
      'create_lingotek_document' => 0,
      'sync_method' => 0,
    );
    variable_set('lingotek_profiles', $profiles);
  }
  return $profiles;
}

/**
 * Implements hook_node_view().
 */
function lingotek_node_view($node, $view_mode) {
  global $language, $first_load, $user;
  $lingotek_document_id = isset($node->lingotek['document_id']) ? $node->lingotek['document_id'] : NULL;
  $community_translation_allowed = isset($node->lingotek['allow_community_translation']) ? $node->lingotek['allow_community_translation'] : FALSE;
  if ($view_mode == 'full' && $community_translation_allowed && $lingotek_document_id && lingotek_supported_type($node->type) && Lingotek::isSupportedLanguage($node->language)) {
    if ($language->language != $node->language) {
      $link = lingotek_get_workbench_url($node->lingotek['document_id'], $language->lingotek_locale, t('Help make it better.'));
      if ($link != '') {
        $message = t('The translation of this page is still being worked on.') . " ";
        $message .= $link . '&nbsp;';
        if (lingotek_access($node, 'manage projects')) {
          $message .= '<span style="font-size: 80%">[' . l(t('progress'), 'node/' . $node->nid . '/lingotek_pm', array(
            'html' => TRUE,
          )) . ']</span>';
        }
        drupal_set_message($message, 'warning', FALSE);
      }
    }
  }
  if (user_access('translation')) {
    if (isset($node->lingotek['document_id']) && !empty($node->lingotek['document_id'])) {
      global $language;
      $node->content['lingotek_link']['#markup'] = lingotek_workbench_icon('node', $node->nid, Lingotek::convertDrupal2Lingotek($language->language));
    }
  }
}
function lingotek_node_presave($node) {

  // Make sure the title isn't overwritten with the translation when using the title module.
  if (module_exists('title') && array_key_exists('title_field', $node)) {
    if (isset($node->title_field[$node->language][0]['value'])) {
      $node->title = $node->title_field[$node->language][0]['value'];
    }
  }
}

/**
 * Implements hook_node_insert().
 */
function lingotek_node_insert($node) {
  lingotek_node_update($node);
}

/**
 * Implements hook_node_delete().
 */
function lingotek_node_delete($node) {
  $doc_id = isset($node->lingotek['document_id']) ? $node->lingotek['document_id'] : NULL;
  $api = LingotekApi::instance();
  $api
    ->removeDocument($doc_id, FALSE);
  LingotekSync::removeNodeInfoByNodeId($node->nid);
}

/*
 * The function entity_load_single is not in core but rather in the entity module
 * which we currently do not have as a dependency.
 */
function lingotek_entity_load_single($entity_type, $entity_id) {
  if ($entity_type == 'node') {
    $entity = lingotek_node_load_default($entity_id);
  }
  else {
    $entities = entity_load($entity_type, array(
      $entity_id,
    ));
    $entity = $entities[$entity_id];
  }
  return $entity;
}

/**
 * Implements hook_node_load().
 */
function lingotek_node_load($nodes, $types) {
  if (empty($nodes)) {
    return;
  }
  $global_profile = lingotek_get_global_profile();
  $node_profile_defaults = lingotek_load_profile_defaults('node');

  // return assoc array by node type
  $profiles_list = lingotek_get_profiles();
  $result = db_select('lingotek', 'n')
    ->fields('n', array(
    'nid',
    'lingokey',
    'lingovalue',
  ))
    ->condition('n.nid', array_keys($nodes), 'IN')
    ->condition('n.lingokey', 'target_%', 'NOT LIKE')
    ->execute();
  $node_lingovalues = array();
  foreach ($result as $record) {
    $node_lingovalues[$record->nid][$record->lingokey] = $record->lingovalue;
  }
  foreach ($nodes as &$node) {

    //    if (!lingotek_supported_type($node->type)) {
    //      continue;
    //    }
    // translate node titles for workbench moderation 'View draft' node tab
    if (module_exists('workbench_moderation') && isset($node->workbench_moderation['published']) && $node->workbench_moderation['published']->vid != $node->vid) {

      // Workbench Moderation calls two node saves if viewing a draft, so we have to
      // clear this cache in order for the translated title fields to be loaded
      drupal_static_reset('title_entity_sync');
      title_entity_sync('node', $node);
    }

    // Node profile inheritance heirarchy
    // Step 1: get global profile
    $node->lingotek = $global_profile;

    // Step 2: add default profiles
    if (array_key_exists($node->type, $node_profile_defaults)) {
      $node->lingotek = array_merge($node->lingotek, $node_profile_defaults[$node->type]);
    }

    // Step 3: add node-specific profile
    if (isset($node->nid) && isset($node_lingovalues[$node->nid]['profile']) && is_numeric($node_lingovalues[$node->nid]['profile'])) {
      $node->lingotek = array_merge($node->lingotek, $profiles_list[$node_lingovalues[$node->nid]['profile']]);
    }

    // Step 4: add node-specific overrides
    if (isset($node->nid) && isset($node_lingovalues[$node->nid])) {
      $node->lingotek = array_merge($node->lingotek, $node_lingovalues[$node->nid]);
    }

    // Step 5: if no profile, then disabled.
    if (!isset($node->lingotek['profile']) || !strlen($node->lingotek['profile'])) {
      $node->lingotek['profile'] = LingotekSync::PROFILE_DISABLED;
    }
  }
}

/**
 * Lingotek custom wrapper function for node_load
 *
 * This is needed because the Lingotek module should translate only the most current revision of any node
 *
 * @param $nid entity_id of node to load
 *
 * @return $node most current revision of the node at $nid
 */
function lingotek_node_load_default($nid, $vid = NULL, $reset = FALSE) {
  if ($vid == NULL) {
    $query = db_select('node_revision', 'nr')
      ->condition('nid', $nid, '=');
    $query
      ->addExpression('MAX(vid)', 'max_vid');
    $vid = $query
      ->execute()
      ->fetchField();
  }
  $node = node_load($nid, $vid, $reset);
  return $node;
}

/**
 * Implements hook_node_update().
 */
function lingotek_node_update($node) {

  // check to make sure Lingotek is enabled and supporting the given node type or profile
  if (!lingotek_supported_type($node->type) && (!isset($node->lingotek) || $node->lingotek['profile'] == LingotekSync::PROFILE_DISABLED)) {
    return;
  }

  //check to make sure it is not a target node
  if (!empty($node->tnid) && $node->nid != $node->tnid) {
    return;
  }
  $node = lingotek_node_save($node);
  if (module_exists('workbench_moderation') && isset($node->workbench_moderation)) {
    if (isset($node->is_new) && $node->is_new) {
      lingotek_workbench_moderation_transition($node, $node->workbench_moderation['current']->from_state, $node->workbench_moderation['current']->state);
    }
    else {
      return;
    }
  }
  else {
    lingotek_upload_document($node);
  }
}
function lingotek_upload_document($node) {
  if (!lingotek_supported_type($node->type) && $node->lingotek['profile'] == LingotekSync::PROFILE_DISABLED || !Lingotek::isSupportedLanguage($node->language)) {
    return;
  }
  if (isset($node->lingotek['node_sync_status']) && ($node->lingotek['node_sync_status'] == LingotekSync::STATUS_DISABLED || $node->lingotek['node_sync_status'] == LingotekSync::STATUS_TARGET)) {
    return;
  }
  if (isset($node->lingotek['document_id'])) {
    $xml = lingotek_xml_node_body($node);
    $hash = md5($xml);
    $oldhash = lingotek_lingonode($node->nid, 'hash');
    lingotek_lingonode($node->nid, 'hash', $hash);
    $diff = strcmp($hash, $oldhash);
    if ($diff == 0 && $node->lingotek['node_sync_status'] == 'CURRENT') {
      return;

      //node has no changes.
    }
  }

  // If the user explicitly requested that this item not be pushed to Lingotek, set the Lingonode property so that the
  // edit form default can be set appropriately on subsequent edits.
  $send_lingotek_document = NULL;
  if (isset($node->lingotek_upload_override)) {

    // this variable can force to either send or not send (1 or 0) when set.
    $send_lingotek_document = $node->lingotek_upload_override == 1 ? 1 : 0;
    $node->lingotek_upload_override = 0;
  }
  else {
    $send_lingotek_document = isset($node->lingotek['create_lingotek_document']) ? $node->lingotek['create_lingotek_document'] : FALSE;
  }
  if ($send_lingotek_document) {
    LingotekSync::resetTargetProgress($node->nid);
  }

  // if workbench moderation is enabled for this node and it is being sent, set flag to be moderated
  // prevents moderation from happening for every downloaded language
  if ($node->lingotek['sync_method'] == 1 && module_exists('workbench_moderation') && isset($node->workbench_moderation)) {
    $moderate = $send_lingotek_document == 1 ? 1 : 0;
    if ($moderate) {
      lingotek_lingonode($node->nid, 'workbench_moderate', 0);
    }
  }

  // On node create or edit,  set the node sync status to 'edited' and all the targets to edited.
  $is_syncing = isset($node->skip_status_updates) && $node->skip_status_updates ? TRUE : FALSE;
  $is_syncing = isset($_SESSION['lingotek_sync_in_progress']) ? $_SESSION['lingotek_sync_in_progress'] : $is_syncing;
  if ($is_syncing === FALSE) {
    LingotekSync::setNodeAndTargetsStatus($node, LingotekSync::STATUS_EDITED, LingotekSync::STATUS_EDITED);
  }

  // Items that are only accessible on node add or edit forms for nodes not yet sent to Lingotek.
  if (!isset($node->lingotek['document_id']) || empty($node->lingotek['document_id'])) {
    LingotekLog::trace('lingotek_node_update FIRST RUN ONLY', array(
      "nid" => $node->nid,
      "language" => $node->language,
    ));
    if ($send_lingotek_document) {
      lingotek_node_save_readonly($node);
      $ln = LingotekNode::load($node);
      if (LingotekApi::instance()
        ->addContentDocument($ln, TRUE)) {
        drupal_set_message(t('<em>@node_title</em> sent to Lingotek successfully.', array(
          '@node_title' => $node->title,
        )));
      }
      else {
        drupal_set_message(t('Unable to send <em>@node_title</em> to Lingotek.', array(
          '@node_title' => $node->title,
        )), 'error');
      }
    }
    else {
      lingotek_lingonode($node->nid, 'node_sync_status', LingotekSync::STATUS_EDITED);
    }
  }
  else {

    // Keep source document up to date.
    $router_item = menu_get_item();

    // These are the URLs that should NOT send updates
    $skip_list = array(
      'lingotek/update',
      LINGOTEK_MENU_LANG_BASE_URL . '/notifications',
    );
    $path_match = array_search($router_item['path'], $skip_list);

    // Also, dont upload if this node update was caused by the sync
    $is_syncing = isset($_SESSION['lingotek_sync_in_progress']) ? $_SESSION['lingotek_sync_in_progress'] : FALSE;
    if ($send_lingotek_document && $path_match === FALSE && $is_syncing === FALSE) {
      LingotekApi::instance()
        ->updateContentDocument($node);
    }
    else {
      lingotek_lingonode($node->nid, 'node_sync_status', LingotekSync::STATUS_EDITED);
    }
  }
}

/**
 * Update Lingotek table settings for a node.
 * 
 * @param array $settings
 *    Associative array of lingokey => lingovalue
 *
 * @param array $node
 *    Array of node objects like what is returned by node_load_multiple()
 *
 */
function lingotek_update_node_settings($settings, $nids) {
  foreach ($nids as $nid) {
    foreach ($settings as $setting => $value) {
      lingotek_lingonode($nid, $setting, $value);
    }
  }
}

/*
 * hook_help
 */
function lingotek_help($path, $arg) {

  // contact
  // links to module dependencies (e.g., title)
  switch ($path) {
    case 'admin/help#lingotek':
      $output = '';
      $output .= '<h2>' . t('Lingotek Translation - Help') . '</h2>';
      $support = lingotek_support_footer();
      $output .= drupal_render($support);
      return $output;
  }
}

/**
 * Gets a list of fields with translation enabled.
 *
 * @return array
 *   An array of the machine names for translatable fields in the system.
 */
function lingotek_translatable_node_fields() {
  $fields = field_info_fields();
  $translatable_fields = array();
  foreach ($fields as $field_id => $field) {
    foreach ($field['bundles'] as $type => $instance) {
      if (field_is_translatable($type, $field)) {
        $translatable_fields[] = $field['field_name'];
      }
    }
  }
  return $translatable_fields;
}

/**
 * Processing callback for the advanced parsing update batch operation.
 */
function lingotek_advanced_parsing_update_node($nid, &$context) {
  $process_node = lingotek_node_load_default($nid);
  if (!empty($process_node->nid)) {
    $context['message'] = t('Sending advanced parsing data to Lingotek for node @node_id: @node_title', array(
      '@node_id' => $process_node->nid,
      '@node_title' => $process_node->title,
    ));
    if (LingotekApi::instance()
      ->updateContentDocument(LingotekNode::load($process_node))) {
      $context['results'][] = t('Updated node: @id', array(
        '@id' => $process_node->nid,
      ));
    }
    else {
      LingotekLog::error('lingotek', 'Unable to send advanced XML version of node to Lingotek: @node_id', array(
        '@node_id' => $process_node->nid,
      ));
    }
  }
}

/**
 * "Finished" callback for the XML update batch operation.
 */
function lingotek_advanced_parsing_update_finished($success, $results, $operations) {
  if ($success) {
    drupal_set_message(t('Advanced parsing updates complete.'));
  }
  else {
    drupal_set_message(t('There were errors updating one or more existing Lingotek documents.'), 'error');
  }
}

/**
 * Installs the default advanced XML configuration that ships with the module.
 */
function lingotek_set_default_advanced_xml($force = FALSE) {
  $filepath_stub = DRUPAL_ROOT . '/' . drupal_get_path('module', 'lingotek') . '/fprm/';
  $config_file_primary = $filepath_stub . 'okf_xmlstream@drupal_filter.fprm';
  $config_file_secondary = $filepath_stub . 'okf_html@drupal_subfilter.fprm';
  $current_config = variable_get('lingotek_advanced_xml_config1', '');
  $default_config = file_get_contents($config_file_primary);
  if ($default_config) {
    if (!strlen($current_config) || $force) {
      variable_set('lingotek_advanced_xml_config1', $default_config);
    }
  }
  else {
    LingotekLog::error('Unable to set default primary advanced XML configuration from: @config_file', array(
      '@config_file' => $config_file_primary,
    ));
  }
  $current_config2 = variable_get('lingotek_advanced_xml_config2', '');
  $default_config2 = file_get_contents($config_file_secondary);
  if ($default_config2) {
    if (!strlen($current_config2) || $force) {
      variable_set('lingotek_advanced_xml_config2', $default_config2);
    }
  }
  else {
    LingotekLog::error('Unable to set default secondary advanced XML configuration from: @config_file', array(
      '@config_file' => $config_file_secondary,
    ));
  }
}

/**
 * Checks to make sure the Lingotek Translation module setup completed successfully.  If its not, the user is directed to the setup wizard.
 */
function lingotek_is_module_setup() {
  $redirect = lingotek_is_config_missing();
  if (!is_bool($redirect)) {
    drupal_goto($redirect);

    // If something is missing - Go to the Setup Process
  }
}

/**
 * Checks any required configuration parameters are missing. (more detailed check than lingotek_is_module_setup())
 */
function lingotek_is_config_missing() {
  $required_variables = array(
    'lingotek_login_id',
    'lingotek_community_identifier',
    'lingotek_project',
    'lingotek_workflow',
    'lingotek_vault',
    'lingotek_enabled_fields',
    'lingotek_translate_comments_node_types',
    'lingotek_oauth_consumer_id',
    'lingotek_oauth_consumer_secret',
  );
  foreach ($required_variables as $required_variable) {
    $val = variable_get($required_variable, NULL);
    if (empty($val)) {
      if ($required_variable == 'lingotek_login_id') {
        return 'admin/config/lingotek/new-account';
      }
      elseif ($required_variable == 'lingotek_community_identifier') {
        return 'admin/config/lingotek/community-select';
      }
      elseif ($required_variable == 'lingotek_project' || $required_variable == 'lingotek_workflow' || $required_variable == 'lingotek_vault') {
        return 'admin/config/lingotek/project-vault-select';
      }
      elseif ($required_variable == 'lingotek_enabled_fields') {
        return 'admin/config/lingotek/node-translation-settings';
      }
      elseif ($required_variable == 'lingotek_translate_comments_node_types') {
        return 'admin/config/lingotek/comment-translation-settings';
      }
      elseif ($required_variable == 'lingotek_oauth_consumer_id' || $required_variable == 'lingotek_oauth_consumer_secret') {
        return TRUE;
      }
    }
  }
  return FALSE;

  // all required configuration variables are set
}

/**
 * Form constructor for the Lingotek Content push form.
 */
function lingotek_content_push_form($form, $form_state, $node) {
  $form = array();
  $form['content_push'] = array(
    '#type' => 'fieldset',
    '#title' => t('Push Node Content to Lingotek'),
    '#description' => t("Manually push this node's content to Lingotek"),
    '#collapsible' => TRUE,
    '#collapsed' => FALSE,
  );
  $form['content_push']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Push Content'),
  );
  $form['node_id'] = array(
    '#type' => 'hidden',
    '#value' => $node->nid,
  );
  return $form;
}

/**
 * Submit handler for the lingotek_content_push_form form.
 */
function lingotek_content_push_form_submit($form, $form_state) {
  $node = lingotek_node_load_default($form_state['values']['node_id']);
  $api = LingotekApi::instance();
  if ($existing_document = isset($node->lingotek['document_id']) ? $node->lingotek['document_id'] : NULL) {

    // Update an existing Lingotek Document.
    $api
      ->updateContentDocument(LingotekNode::load($node));
  }
  else {

    // Create a new Lingotek Document.
    $api
      ->addContentDocument(LingotekNode::load($node), TRUE);
  }
  drupal_set_message(t('Pushed content for @node_title to Lingotek for translation.', array(
    '@node_title' => $node->title,
  )));
}

/**
 * Implements hook_comment_insert().
 */
function lingotek_comment_insert($comment) {

  // Currently, the behavior for add/update is the same.
  lingotek_comment_update($comment);
}

/**
 * Implements hook_comment_update().
 */
function lingotek_comment_update($comment) {
  if (LingotekComment::$content_update_in_progress) {
    return;
  }
  $target_types = variable_get('lingotek_translate_comments_node_types', array());
  $comment_node = lingotek_node_load_default($comment->nid);
  $valid_translation_target = !empty($comment_node->type) && (isset($target_types[$comment_node->type]) && $target_types[$comment_node->type]);
  if (class_exists('LingotekComment') && variable_get('lingotek_translate_comments', FALSE) && $valid_translation_target) {
    $lingotek_comment = LingotekComment::load($comment)
      ->contentUpdated();
  }
}

/**
 * Implements hook_comment_delete().
 */
function lingotek_comment_delete($comment) {
  $target_types = variable_get('lingotek_translate_comments_node_types', array());
  $comment_node = lingotek_node_load_default($comment->nid);
  $valid_translation_target = !empty($comment_node->type) && isset($target_types[$comment_node->type]);
  if (class_exists('LingotekComment') && variable_get('lingotek_translate_comments', FALSE) && $valid_translation_target) {
    $lingotek_comment = LingotekComment::load($comment);
    $doc_id = $lingotek_comment
      ->getMetadataValue('document_id');
    if (is_numeric($doc_id)) {
      $api = LingotekApi::instance();
      $api
        ->removeDocument($doc_id, FALSE);
    }
  }
}

/**
 * Implements hook_comment_view().
 */
function lingotek_comment_view($comment, $view_mode, $langcode) {
  if (class_exists('LingotekComment') && user_access('administer comments')) {
    $lingotek_comment = LingotekComment::load($comment);
    $link_token = drupal_get_token();
    if ($document_id = $lingotek_comment
      ->getMetadataValue('document_id')) {

      // This is a Lingotek-associated comment, present the "update translations" link.
      $comment->content['links']['comment']['#links']['comment-lingotek-refresh'] = array(
        'title' => t('refresh translations'),
        'href' => 'lingotek/sync/comment/' . $comment->cid,
        'html' => FALSE,
        'query' => array_merge(array(
          'token' => $link_token,
        ), drupal_get_destination()),
      );
    }
  }
  global $language;
  $comment->content['lingotek_link']['#markup'] = lingotek_workbench_icon('comment', $comment->cid, Lingotek::convertDrupal2Lingotek($language->language));
}

/**
 * Implements hook_entity_info_alter().
 */
function lingotek_entity_info_alter(&$entity_info) {

  // Only alter the entity handling of comment fields
  // if Lingotek translation is enabled.
  if (variable_get('lingotek_translate_comments', FALSE)) {
    $entity_info['comment']['translation']['lingotek'] = TRUE;
  }
  if (isset($entity_info['field_collection_item'])) {
    $entity_info['field_collection_item']['translation']['lingotek'] = TRUE;
  }
}
function lingotek_get_fc_parent($entity) {
  $query = new EntityFieldQuery();
  $query
    ->fieldCondition($entity
    ->fieldInfo(), 'revision_id', $entity->revision_id);
  if (!$entity
    ->isInUse()) {
    $query
      ->age(FIELD_LOAD_REVISION);
  }
  $result = $query
    ->execute();
  $num_results = isset($result) ? sizeof($result) : 0;
  if (!isset($result) || $num_results != 1) {

    // found more or less than we bargained for.  Abort.
    LingotekLog::error('Found @parents parents when querying for field-collection parent: @entity', array(
      '@parents' => $num_results,
      '@entity' => print_r($entity, TRUE),
    ));
    return NULL;
  }
  foreach ($result as $parent_type => $parents) {
    if (!isset($result) || sizeof($result) != 1) {
      LingotekLog::error('Found @parents parents when querying for field-collection parent: @entity', array(
        '@parents' => $num_results,
        '@entity' => print_r($entity, TRUE),
      ));
      return NULL;
    }
    foreach (array_keys($parents) as $parent_id) {
      return entity_load_single($parent_type, $parent_id);
    }
  }
}
function lingotek_normalize_field_collection_language($entity, $from_language_none = TRUE) {
  $host = lingotek_get_fc_parent($entity);
  if (!$host) {

    // unable to locate parent.  Abort.
    return;
  }
  $max_field_collection_depth = 10;

  // bubble up to the true parent to get its language,
  // which adds support for deeply nested entity collections
  for ($i = 0; $i < $max_field_collection_depth; $i++) {
    if ($host instanceof FieldCollectionItemEntity) {
      $host = lingotek_get_fc_parent($entity);
    }
  }
  foreach ($entity as $key => $value) {
    if (is_array($value) && array_key_exists(LANGUAGE_NONE, $value)) {
      if ($from_language_none) {
        $entity->{$key}[$host->language] = $value[LANGUAGE_NONE];
      }
      elseif (isset($value[$host->language])) {
        $entity->{$key}[LANGUAGE_NONE] = $value[$host->language];
      }
    }
  }
}

/**
 * Implements hook_entity_load().
 */
function lingotek_entity_load($entities, $type) {
  if ($type == 'field_collection_item') {
    foreach ($entities as $e) {
      lingotek_normalize_field_collection_language($e);
    }
  }
}

/**
 * Implements hook_entity_presave().
 */
function lingotek_entity_presave($entity, $type) {
  if ($type == 'field_collection_item') {
    lingotek_normalize_field_collection_language($entity, FALSE);
  }
}

/**
 * Implements hook_form_FORMID_alter().
 */
function lingotek_form_comment_form_alter(&$form, $form_state) {
  if (variable_get('lingotek_translate_comments', FALSE)) {

    // Caution.  There be some wackey voodoo in here.
    // The locale module is going to set the comment's language to the user's browsing language
    // but the form_attach_fields call in comment_form() will have already run
    // setting all field language contents to the site's default.
    // Copy the fields to match the language of the comment.
    $default_language = language_default('language');

    // 'es', 'de', 'en', etc.
    $comment_language = $form['language']['#value'];
    $comment_bundle = $form['#bundle'];
    $comment_fields = array_keys(field_info_instances('comment', $comment_bundle));
    if ($comment_language != $default_language) {

      // Loop through each comment field
      foreach ($comment_fields as $comment_field) {

        // IE:  'comment_body', 'subject_field'
        if (isset($form['cid']['#value'])) {

          // Comment Edit
          $form['lingotek_language_notes'] = array(
            '#weight' => -15,
            '#markup' => '<div style="padding: 5px 0px;"><strong>Note:</strong>  When editing a comment you are only allowed to edit the original.</div>',
          );

          // Make sure the comment form is setup so that the form uses the source comment language.
          if (!empty($form[$comment_field][$default_language])) {

            // You have to do this (even for an empty form), or your comment form disappears, because you have no language content to edit.  So its important before the unset.
            $form[$comment_field][$comment_language] = $form[$comment_field][$default_language];
            unset($form[$comment_field][$default_language]);
          }

          // Now, set the field #default_value as our source language comment
          $original_field_text = $form[$comment_field][$comment_language][0]['#entity']->{$comment_field}[$comment_language][0]['value'];

          // The default field value could be set in 1 of 2 places.  So check both.
          if (isset($form[$comment_field][$comment_language][0]['value'])) {
            $form[$comment_field][$comment_language][0]['value']['#default_value'] = $original_field_text;
          }
          else {
            $form[$comment_field][$comment_language][0]['#default_value'] = $original_field_text;
          }

          // These may not be needed.   They just set the langauge to match the comment language everywhere they can.
          $form[$comment_field]['#language'] = $comment_language;
          $form[$comment_field][$comment_language]['#language'] = $comment_language;
          $form[$comment_field][$comment_language][0]['#language'] = $comment_language;
        }
        else {

          // Comment Add
          // This changes the form, from being submitted in the default language, to being submitted in the language of the page you are viewing.
          if (!empty($form[$comment_field][$default_language])) {

            // You have to do this (even for an empty form), or your comment form disappears, because you have no language content to edit.  So its important before the unset.
            $form[$comment_field][$comment_language] = $form[$comment_field][$default_language];
            unset($form[$comment_field][$default_language]);
          }
        }
      }

      // END:  foreach $comment_fields
    }

    // END:  if $comment_language != $default_language
  }
}

/**
 * Implements hook_field_language_alter().
 */
function lingotek_field_language_alter(&$display_language, $context) {

  // If we have no language set on the entity itself, do nothing.
  if (!isset($context['entity']->language) || empty($context['entity']->language)) {
    return;
  }

  // Comments may be in a state where content only exists in the source language
  // because Lingotek translation hasn't finished yet, or synchonization with
  // Lingotek hasn't yet occurred. In this case, fall back to displaying
  // the default language for each field.
  foreach ($display_language as $field => $display_language_code) {
    if (!isset($context['entity']->{$field}[$display_language_code])) {
      $display_language[$field] = $context['entity']->language;
    }
  }
}

/**
 * Implements hook_workbench_moderation_transition()
 *
 * Upload content automatically only if new state corresponds auto-upload workbench_moderation variables
 * Otherwise override upload
 */
function lingotek_workbench_moderation_transition($node, $old_state, $new_state) {

  //Do not run unless this node has been properly loaded.
  if (!isset($node->lingotek) || !array_key_exists('document_id', $node->lingotek)) {
    return;
  }

  // Check global and node-specific settings
  // if this node is managed by lingotek, add lingotek wb variables
  if (isset($node->lingotek_push_once) && $node->lingotek_push_once || lingotek_node_pushed($node) || isset($node->is_new) || !lingotek_managed_by_entity_translation($node->type)) {
    $def_state = $node->lingotek['create_lingotek_document_workbench_moderation'];
    $states = lingotek_get_workbench_moderation_states();

    // do not change status updates if only moderated (no node_save called)
    if (!isset($node->workbench_moderation_state_new)) {
      $node->skip_status_updates = TRUE;
    }

    //    // set lingonode variable for workbench moderation auto-upload
    //    if (isset($node->create_lingotek_document_workbench_moderation)) {
    //      lingotek_lingonode($node->nid, 'create_lingotek_document_workbench_moderation', $node->create_lingotek_document_workbench_moderation);
    //      $def_state = $node->create_lingotek_document_workbench_moderation;
    //    }
    //    // set lingonode variable for workbench moderation auto-download setting
    //    if (isset($node->syncMethod_wb)) {
    //      lingotek_lingonode($node->nid, 'sync_method_workbench_moderation', $node->syncMethod_wb);
    //    }
    if ($old_state == workbench_moderation_state_published() && $new_state != workbench_moderation_state_published()) {

      // if changing from published
      $node->status = 0;

      // unpublish
      node_save($node);
    }

    // Check new state against settings
    if ($new_state != $states[$def_state]) {

      // && !isset($node->is_new)) {
      $node->lingotek_upload_override = 0;
    }
  }
  lingotek_upload_document($node);
}

/*
 * AJAX callback to update transition options
 */
function lingotek_workbench_moderation_node_callback($form, &$form_state) {
  return $form['lingotek']['content']['syncMethod_wb_message'];
}

/*
 * Implements hook_entity_delete()
 * 
 * This removes the lingotek data of an entity.
 */
function lingotek_entity_delete($entity, $type) {
  db_delete('lingotek_entity_metadata')
    ->condition('entity_type', $type)
    ->condition('entity_id', entity_extract_ids($type, $entity))
    ->execute();
}

/**
 * Implements hook_navbar().
 */
function lingotek_navbar() {

  //this integration with the navbar module has depencies on javascript files

  //that Responsive Theme Preview adds. Thus the integration will only show up if

  //the Responsive Theme Preview module is enabled.
  if (!module_exists('responsive_preview') || variable_get('language_count') <= 1 || !variable_get('lingotek_navbar_switcher', TRUE)) {
    return;
  }
  $path = drupal_is_front_page() ? '<front>' : $_GET['q'];
  $links = language_negotiation_get_switch_links('language', $path);
  $buttons = array();
  if (empty($links->links)) {
    return;
  }
  foreach ($links->links as $key => $link) {
    $link['attributes']['class'] = array(
      'device',
      'icon',
      'icon-active',
    );
    $buttons[] = l($link['title'], $link['href'], $link);
  }
  $items['lingotek'] = array(
    '#type' => 'navbar_item',
    'tab' => array(
      'trigger' => array(
        '#theme' => 'html_tag',
        '#tag' => 'button',
        '#value' => t('Layout preview'),
        '#value_prefix' => '<span class="element-invisible">',
        '#value_suffix' => '</span>',
        '#attributes' => array(
          'title' => t('Preview page layout'),
          'class' => array(
            'icon',
            'icon-lingotek',
            'trigger',
          ),
        ),
      ),
      'device_options' => array(
        '#theme' => 'item_list',
        '#items' => $buttons,
        '#attributes' => array(
          'class' => array(
            'options',
          ),
        ),
      ),
    ),
    '#wrapper_attributes' => array(
      'id' => 'lingotek-navbar-tab',
      'class' => array(
        'navbar-tab-lingotek',
      ),
    ),
    '#attached' => array(
      'library' => array(
        array(
          'lingotek',
          'lingotek-navbar-switcher',
        ),
        array(
          'responsive_preview',
          'responsive-preview',
        ),
      ),
    ),
    '#weight' => 300,
  );
  return $items;
}

/**
 * Implements hook_library().
 */
function lingotek_library() {
  $path = drupal_get_path('module', 'lingotek');
  $options = array(
    'scope' => 'footer',
    'attributes' => array(
      'defer' => TRUE,
    ),
  );
  $libraries['lingotek-navbar-switcher'] = array(
    'title' => 'Lingotek Language Switcher',
    'version' => VERSION,
    'css' => array(
      $path . '/style/lingotek.navbar.css',
    ),
    'js' => array(
      $path . '/js/lingotek-navbar-switcher.js' => $options,
    ),
    'dependencies' => array(
      array(
        'system',
        'jquery',
      ),
      array(
        'responsive_preview',
        'debounce',
      ),
      array(
        'responsive_preview',
        'backbone',
      ),
      array(
        'responsive_preview',
        'jquery.ui.position',
      ),
    ),
  );
  return $libraries;
}
function lingotek_form_alter(&$form, &$form_state, $form_id) {
  if (substr($form_id, -9) == 'node_form') {

    //disable the language field if the node is synced with lingotek
    if (isset($form['#entity']->nid)) {
      $document_id = lingotek_lingonode($form['#entity']->nid, 'document_id');
      if (isset($document_id) && $document_id != 0) {
        $form['language']['#disabled'] = TRUE;
      }
    }
    if (isset($form['#entity']->tnid) && $form['#entity']->tnid != 0 && $form['#entity']->nid != $form['#entity']->tnid) {
      $content_type = $form['type']['#value'];
      $lingotek_fields = variable_get('lingotek_enabled_fields');
      foreach ($lingotek_fields['node'][$content_type] as $field) {
        $form[$field]['#disabled'] = TRUE;
      }
      $lingotek_locale = Lingotek::convertDrupal2Lingotek($form['#entity']->language);
      $node = lingotek_node_load_default($form['#entity']->tnid);
      $workbench = 'node/' . $node->nid . '/lingotekworkbench/' . $lingotek_locale;
      $message = t('Some fields are not editable because the translations are managed by lingotek.
        To edit this content in the source language go to the ') . l(t('source node'), 'node/' . $form['#entity']->tnid . '/edit') . '.';
      $link = l(t('Lingotek workbench'), $workbench, array(
        'attributes' => array(
          'target' => '_blank',
        ),
      ));
      $message .= '<br>' . t('To make changes to this translation you can go to the !url.', array(
        '!url' => $link,
      ));
      drupal_set_message($message);
    }
  }
}
function lingotek_get_profile_fields($include_readonly = TRUE, $include_changeable = TRUE) {
  $profile_fields = array();
  if ($include_readonly) {
    $profile_fields = array_merge($profile_fields, array(
      'lingotek_nodes_translation_method',
      'project_id',
      'workflow_id',
      'vault_id',
    ));
  }
  if ($include_changeable) {
    $profile_fields = array_merge($profile_fields, array(
      'profile_id',
      'name',
      'create_lingotek_document',
      'sync_method',
      'allow_community_translation',
      'url_alias_translation',
      'sync_method_workbench_moderation',
      'create_lingotek_document_workbench_moderation',
    ));
  }
  return $profile_fields;
}

/**
 * Implementation of hook_views_api
 */
function lingotek_views_api() {
  return array(
    'api' => 3,
  );
}

/**
 * Implements hook_cron().
 *
 * Updates the local cache of commonly used values.
 */
function lingotek_cron() {

  // remove locally cached values, so they will be refreshed next time they are needed
  variable_del('lingotek_project_defaults');
  variable_del('lingotek_workflow_defaults');
  variable_del('lingotek_vaults_defaults');
  LingotekLog::trace(__METHOD__ . ' ran');
}

/*
 * Implements hook_l10n_update
 */
function lingotek_l10n_update_projects_alter(&$projects) {
  if (variable_get('lingotek_use_translation_from_drupal', 0) != 1) {
    $projects = array();
  }
}

/*
 * Implements hook_node_view_alter
 */
function lingotek_node_view_alter(&$info) {
  if (!variable_get('lingotek_show_language_label', 0)) {
    if (isset($info['language'])) {
      unset($info['language']);
    }
  }
}
function lingotek_get_change_workflow_form($form, &$form_state, $node = NULL) {
  $second_run = isset($form_state['triggering_element']);
  $bulk_grid = FALSE;
  $multiple = FALSE;
  if (isset($form_state['nids'])) {
    $nids = $form_state['nids'];
    $bulk_grid = TRUE;
    $multiple = count($nids) > 1;
    if (!$multiple) {
      $node = lingotek_node_load_default(reset($nids));
    }
    else {
      if (!$second_run) {
        drupal_set_message(t('You will be changing the workflow for @number nodes.', array(
          '@number' => count($nids),
        )), 'warning');
      }
      $node = new stdClass();
      $node->lingotek = lingotek_get_global_profile();
      $node->lingotek['profile'] = LingotekSync::PROFILE_DISABLED;

      // Note: Consider making this default 'Automatic' (after it is a fixed profile; can't be deleted)
    }
  }
  drupal_add_css(drupal_get_path('module', 'lingotek') . '/style/form.css');
  $title = t('Change Workflow');

  // Vertical Tab.
  $form['lingotek'] = array(
    '#title' => t('Change Workflow'),
    '#type' => 'fieldset',
    '#collapsible' => !$bulk_grid,
    '#collapsed' => !$bulk_grid,
    '#group' => 'additional_settings',
    '#attributes' => array(
      'id' => array(
        'lingotek_fieldset',
      ),
    ),
    '#attached' => array(
      'js' => array(
        drupal_get_path('module', 'lingotek') . '/js/lingotek.form.js',
      ),
    ),
    '#modal' => TRUE,
    '#tree' => TRUE,
  );
  if (isset($node->tnid) && $node->tnid != 0 && $node->tnid != $node->nid) {
    $form['lingotek']['note'] = array(
      '#markup' => t('This is a target node for the language code: @lang. To change
        the Lingotek settings please edit the source node.', array(
        '@lang' => $node->language,
      )),
    );
    return $form;

    //this is a target node and thus should not have lingotek settings
  }
  if (isset($nids)) {
    $form['lingotek']['nids'] = array(
      '#type' => 'hidden',
      '#default_value' => json_encode($nids),
    );
  }
  $form['lingotek']['lingotek_note'] = array(
    '#type' => 'item',
    '#title' => $title,
    '#description' => t("This option allows you to replace the current workflow with a new workflow. This will result in the removal of any existing translations. You can optionally restore those translations to a phase in the new workflow."),
  );
  $api = LingotekApi::instance();
  if ($workflows = $api
    ->listWorkflows()) {
    if (!isset($form_state['values']['workflow_id'])) {
      $keys = array_keys($workflows);
      $form_state['values']['workflow_id'] = $keys[0];
    }
    $form['lingotek']['workflow_id'] = array(
      '#type' => 'select',
      '#title' => t('Workflow'),
      '#description' => t('Choose the Workflow'),
      '#options' => $workflows,
      '#empty_option' => '(select one)',
      '#ajax' => array(
        'callback' => 'lingotek_get_change_workflow_form_callback',
        'wrapper' => 'prefill-phases-div',
        'method' => 'replace',
        'effect' => 'fade',
      ),
      '#prefix' => '<div id="prefill-phases-div">',
    );
    if (isset($node)) {
      $form['lingotek']['workflow_id']['#default_value'] = $node->lingotek['workflow_id'];
    }
    $form['lingotek']['prefill_phases_checkbox'] = array(
      '#type' => 'checkbox',
      '#title' => t('Restore to a phase in the new workflow'),
      '#default_value' => TRUE,
      '#description' => t('Prefill the new workflow with translations from the previous workflow'),
    );
    $form['lingotek']['prefill_phase_select'] = array(
      '#title' => t("Desired Prefill Phase"),
      '#description' => t('Please select the highest phase which should be prefilled for the new workflow'),
      '#type' => 'select',
      '#states' => array(
        'invisible' => array(
          ':input[name="lingotek[prefill_phases_checkbox]"]' => array(
            'checked' => FALSE,
          ),
        ),
      ),
      '#suffix' => '</div>',
    );
    if (isset($form_state['values']['lingotek']['workflow_id']) && $form_state['values']['lingotek']['workflow_id'] != NULL) {
      $form['lingotek']['prefill_phase_select']['#options'] = lingotek_get_phases_by_workflow_id($form_state['values']['lingotek']['workflow_id']);
    }
    else {
      $form['lingotek']['prefill_phase_select']['#options'] = array(
        '-1' => '(first choose a workflow)',
      );
      $form['lingotek']['prefill_phase_select']['#disabled'] = TRUE;
    }
  }
  if ($bulk_grid) {
    $form['submit'] = array(
      '#type' => 'submit',
      '#value' => t('Submit'),
      '#submit' => array(
        'lingotek_get_change_workflow_form_submit',
      ),
    );
  }
  return $form;
}
function lingotek_get_change_workflow_form_callback($form, $form_state) {
  if (isset($form_state['values']['lingotek']['prefill_phases_checkbox']) && $form_state['values']['lingotek']['prefill_phases_checkbox']) {
    return array(
      $form['lingotek']['prefill_phase_select'],
      $form['lingotek']['prefill_phases_checkbox'],
      $form['lingotek']['workflow_id'],
    );
  }
  return $form['lingotek']['workflow_id'];
}
function lingotek_get_phases_by_workflow_id($workflow_id) {
  $api = LingotekApi::instance();
  $response = $api
    ->request('getWorkflow', array(
    'id' => $workflow_id,
  ));
  $phases = array();
  $excluded_phases = array(
    'Project Setup',
    'Workflow Completion',
  );
  if (isset($response->workflow->steps)) {
    foreach ($response->workflow->steps as $id => $phase_obj) {
      if (!in_array($phase_obj->name, $excluded_phases)) {
        $phases[$id] = $phase_obj->name;
      }
    }
  }
  return $phases;
}

Functions

Namesort descending Description
lingotek_advanced_parsing_update_finished "Finished" callback for the XML update batch operation.
lingotek_advanced_parsing_update_node Processing callback for the advanced parsing update batch operation.
lingotek_auto_loader Auto-loader function used for upgrades
lingotek_comment_delete Implements hook_comment_delete().
lingotek_comment_insert Implements hook_comment_insert().
lingotek_comment_update Implements hook_comment_update().
lingotek_comment_view Implements hook_comment_view().
lingotek_content_push_form Form constructor for the Lingotek Content push form.
lingotek_content_push_form_submit Submit handler for the lingotek_content_push_form form.
lingotek_cron Implements hook_cron().
lingotek_entity_delete
lingotek_entity_info_alter Implements hook_entity_info_alter().
lingotek_entity_load Implements hook_entity_load().
lingotek_entity_load_single
lingotek_entity_presave Implements hook_entity_presave().
lingotek_entity_translation_edit_access Entity Translation edit access callback when enabled with Lingotek Translation module.
lingotek_entity_translation_node_tab_access Entity Translation access callback when enabled with Lingotek Translation module.
lingotek_field_language_alter Implements hook_field_language_alter().
lingotek_form_alter
lingotek_form_comment_form_alter Implements hook_form_FORMID_alter().
lingotek_form_node_form_alter Implements hook_form_BASE_FORM_ID_alter().
lingotek_get_change_workflow_form
lingotek_get_change_workflow_form_callback
lingotek_get_change_workflow_form_submit
lingotek_get_fc_parent
lingotek_get_global_profile
lingotek_get_node_settings_form
lingotek_get_node_settings_form_submit
lingotek_get_phases_by_workflow_id
lingotek_get_profiles
lingotek_get_profile_fields
lingotek_help
lingotek_is_config_missing Checks any required configuration parameters are missing. (more detailed check than lingotek_is_module_setup())
lingotek_is_module_setup Checks to make sure the Lingotek Translation module setup completed successfully. If its not, the user is directed to the setup wizard.
lingotek_l10n_update_projects_alter
lingotek_library Implements hook_library().
lingotek_menu Implements hook_menu().
lingotek_menu_alter Implements hook_menu_alter().
lingotek_module_implements_alter Implements hook_module_implements_alter().
lingotek_navbar Implements hook_navbar().
lingotek_node_delete Implements hook_node_delete().
lingotek_node_insert Implements hook_node_insert().
lingotek_node_load Implements hook_node_load().
lingotek_node_load_default Lingotek custom wrapper function for node_load
lingotek_node_presave
lingotek_node_save
lingotek_node_save_readonly
lingotek_node_update Implements hook_node_update().
lingotek_node_view Implements hook_node_view().
lingotek_node_view_alter
lingotek_normalize_field_collection_language
lingotek_permission Implements hook_permission().
lingotek_set_default_advanced_xml Installs the default advanced XML configuration that ships with the module.
lingotek_translatable_node_fields Gets a list of fields with translation enabled.
lingotek_update_node_settings Update Lingotek table settings for a node.
lingotek_upload_document
lingotek_views_api Implementation of hook_views_api
lingotek_workbench_moderation_node_callback
lingotek_workbench_moderation_transition Implements hook_workbench_moderation_transition()