cloudwords-8.x-1.x-dev/cloudwords.module
cloudwords.module
<?php
/**
* @file
* Allows users to upload and import Drupal content to and from the Cloudwords
* translation management platform.
*/
$base_stg_url = \Drupal::config('cloudwords.settings')->get('cloudwords_app_url');
$base_api_url = \Drupal::config('cloudwords.settings')->get('cloudwords_api_url');
/**
* The base api url for stage.
*/
define('CLOUDWORDS_BASE_API_URL_STAGE', $base_api_url);
/**
* The base ui url for stage.
*/
define('CLOUDWORDS_BASE_URL_STAGE', $base_stg_url);
/**
* The base api url for production.
*/
const CLOUDWORDS_BASE_API_URL_PRODUCTION = 'https://api.cloudwords.com';
/**
* The base ui url for production.
*/
const CLOUDWORDS_BASE_URL_PRODUCTION = 'https://app.cloudwords.com';
/**
* API version.
*/
const CLOUDWORDS_API_VERSION = 1.24;
/**
* A translatable is not in the queue.
*/
const CLOUDWORDS_QUEUE_NOT_IN_QUEUE = 0;
/**
* A translatable is in the queue.
*/
const CLOUDWORDS_QUEUE_QUEUED = 1;
/**
* A translatable has been submitted to a project.
*/
const CLOUDWORDS_QUEUE_IN_PROJECT = 2;
/**
* Status of translation "Not translated".
*/
const CLOUDWORDS_TRANSLATION_STATUS_NOT_TRANSLATED = 0;
/**
* Status of translation "Translation exists".
*/
const CLOUDWORDS_TRANSLATION_STATUS_TRANSLATION_EXISTS = 1;
/**
* Status of translation "Out of date".
*/
const CLOUDWORDS_TRANSLATION_STATUS_OUT_OF_DATE = 2;
/**
* A language has not been imported.
*/
const CLOUDWORDS_LANGUAGE_NOT_IMPORTED = 0;
/**
* A language has been imported.
*/
const CLOUDWORDS_LANGUAGE_IMPORTED = 1;
/**
* A language has not been approved.
*/
const CLOUDWORDS_LANGUAGE_APPROVED = 2;
/**
* A language import has failed.
*/
const CLOUDWORDS_LANGUAGE_FAILED = 3;
/**
* The status code for a delivered cloudwords language.
*/
const CLOUDWORDS_LANGUAGE_DELIVERED = 'delivered';
/**
* String used to delimit flattened array keys.
*/
const CLOUDWORDS_ARRAY_DELIMITER = '][';
/**
* The size in MB to create export files.
*/
const CLOUDWORDS_BATCH_SIZE = 50;
/**
* The file extension to use for XLIFF files.
*/
const CLOUDWORDS_XLIFF_EXTENSION = 'xlf';
/**
* The Drupal core translation types to be set on Cloudwords Translatable to differentiate user interfaces
*/
const CLOUDWORDS_CONTENT_TRANSLATION_TYPE = 'content';
const CLOUDWORDS_INTERFACE_TRANSLATION_TYPE = 'interface';
const CLOUDWORDS_CONFIG_TRANSLATION_TYPE = 'config';
use Drupal\Core\Url;
/**
* Implements hook_help().
*/
function cloudwords_help($path, $arg) {
switch ($path) {
case 'admin/help#cloudwords':
return 'This module can be used to translate your Drupal content using the ' .
Url::fromRoute('Cloudwords', \Drupal\Core\Url::fromUri('http://www.cloudwords.com')) .
' Translation Management platform. This module leverages the ' .
Url::fromRoute('Cloudwords API', \Drupal\Core\Url::fromUri('http://www.cloudwords.com/developers')) .
' to connect to your existing Cloudwords account.<br /><br />' .
'Simply start a new project using your existing Cloudwords account, upload Drupal content for each of your translation projects, wait for your pre-selected vendors to deliver your translated materials, and import your translated materials back into your Drupal instance.<br /><br />' .
'For more information about the ' .
Url::fromRoute('Cloudwords', \Drupal\Core\Url::fromUri('http://www.cloudwords.com')) .
' Translation Management Platform itself, please visit <a href="http://www.cloudwords.com">http://www.cloudwords.com</a>. Or for additional information regarding the ' .
Url::fromRoute('Cloudwords API', \Drupal\Core\Url::fromUri('http://www.cloudwords.com/developers')) .
', please visit <a href="http://www.cloudwords.com/developers">http://www.cloudwords.com/developers</a>.';
case 'admin/structure/cloudwords':
return t('Content currently under translation with Cloudwords will not be selectable.');
case 'admin/structure/cloudwords/queue':
return t('This page allows you to manage content marked for translation by users. When selecting content to send to Cloudwords for translation, you can use the “Marked for Translation” status as a convenient filter.');
}
}
/**
* Implements hook_preprocess().
*/
function cloudwords_preprocess(&$variables, $hook, &$info) {
// @todo test to make sure doesn't interrupt other views
if($hook == 'views_view' && isset($variables['view']) && $variables['view']->id() == 'cloudwords_translatable'){
$info['path'] = drupal_get_path('module', 'cloudwords') . '/templates';
}
}
/**
* Implements hook_permission().
*/
function cloudwords_permission() {
return [
'administer cloudwords' => [
'title' => t('Administer Cloudwords'),
'restrict access' => TRUE,
],
'add to cloudwords queue' => [
'title' => t('Add to Cloudwords queue'),
'description' => t('Allow a user to add content to the translation queue'),
],
'manage cloudwords projects' => [
'title' => t('Manage projects'),
'description' => t('Submit new bid requests'),
],
];
}
/**
* Implements hook_entity_info().
*/
// @todo remove
function cloudwords_entity_info() {
return [
'cloudwords_translatable' => [
'label' => t('Cloudwords translatable'),
'controller class' => 'CloudwordsTranslatableController',
'entity class' => 'CloudwordsTranslatable',
'base table' => 'cloudwords_translatable',
'uri callback' => 'entity_class_uri',
// 'label callback' => 'entity_class_label',
'fieldable' => FALSE,
'entity keys' => [
'id' => 'ctid',
'label' => 'label',
],
'bundles' => [
'cloudwords_translatable' => [
'label' => t('Cloudwords translatable'),
'admin' => [
'path' => 'admin/structure/cloudwords',
'access arguments' => ['manage cloudwords projects'],
],
],
],
'view modes' => [
'full' => [
'label' => t('Cloudwords translatable'),
'custom settings' => FALSE,
],
],
'module' => 'cloudwords',
'metadata controller class' => 'CloudwordsTranslatableMetadataController',
],
];
}
/**
* Menu loader callback for Cloudwords projects.
*
* This gets called multiple times when i18n_menu is on. Static cache to avoid
* multiple rest calls. :/
*
* @todo Evaluate moving the static cache to the client.
*/
function cloudwords_project_load($pid) {
$projects = &drupal_static(__FUNCTION__, []);
try {
if (!isset($projects[$pid])) {
$projects[$pid] = cloudwords_get_api_client()->get_project($pid);
}
return $projects[$pid];
}
catch (CloudwordsApiException $e) {
// Log API error
\Drupal::logger('cloudwords')->error($e->getErrorMessage(), []);
// Project not found, return empty object
return new CloudwordsDrupalProject([]);
}
}
/**
* Menu loader callback for Cloudwords files.
*
* This gets called multiple times when i18n_menu is on. Static cache to avoid
* multiple rest calls. :/
*
* This is named file_object so that it does not conflict with hook_file_load().
*
* @todo Evaluate moving the static cache to the client.
*/
function cloudwords_file_object_load($pid, $fid) {
$files = &drupal_static(__FUNCTION__, []);
try {
if (!isset($files[$pid])) {
$files[$pid] = cloudwords_get_api_client()->get_project_reference($pid, $fid);
}
return $files[$pid];
}
catch (CloudwordsApiException $e) {
// Don't make 2 failed rest calls.
$files[$pid] = TRUE;
return FALSE;
}
}
/**
* Menu loader callback for Cloudwords languages.
*
* This gets called multiple times when i18n_menu is on.
*/
function cloudwords_language_load($language_code) {
module_load_include('inc', 'cloudwords', 'cloudwords.languages');
$map = _cloudwords_map_cloudwords_drupal();
$list = cloudwords_language_list();
if (isset($map[$language_code])) {
return new \Drupal\cloudwords\CloudwordsLanguage([
'languageCode' => $language_code,
'display' => $list[$map[$language_code]],
]);
}
return FALSE;
}
/**
* Title callback for Cloudwords projects.
*/
function cloudwords_project_title(CloudwordsDrupalProject $project) {
return $project->getName();
}
/**
* Title callback for Cloudwords languages.
*/
function cloudwords_language_title(CloudwordsDrupalProject $project, CloudwordsLanguage $language) {
return cloudwords_project_title($project) . ' (' . $language->getDisplay() . ')';
}
/**
* Translates a queue status into human-readable text.
*
* @param int|null $status
* If int, the corresponding text is returned. If null, the entire map is
* returned.
*
* @return array|string
* The map of status values or the status string.
*/
function cloudwords_status_options_list($status = NULL) {
$return = [
CLOUDWORDS_QUEUE_NOT_IN_QUEUE => t('No'),
CLOUDWORDS_QUEUE_QUEUED => t('Yes'),
CLOUDWORDS_QUEUE_IN_PROJECT => t('In Translation'),
];
if ($status !== NULL && $status !== 'status') {
return $return[$status];
}
return $return;
}
/**
* Returns the correct base api url for the configured environment.
*/
function _cloudwords_api_url() {
$mode = \Drupal::config('cloudwords.settings')->get('cloudwords_client_mode');
if ($mode == 'production') {
$url = CLOUDWORDS_BASE_API_URL_PRODUCTION;
}else{
$url = \Drupal::config('cloudwords.settings')->get('cloudwords_api_url');
}
return rtrim($url, '/');
}
/**
* Returns the correct base ui url for the configured environment.
*/
function _cloudwords_ui_url() {
$mode = \Drupal::config('cloudwords.settings')->get('cloudwords_client_mode');
if ($mode == 'production') {
$url = CLOUDWORDS_BASE_URL_PRODUCTION;
}else{
$url = \Drupal::config('cloudwords.settings')->get('cloudwords_app_url');
}
return rtrim($url, '/');
}
/**
* Returns an instance of the cloudwords client.
*
* @return CloudwordsClient
* A cloudwords client instance.
*/
function cloudwords_get_api_client() {
require_once dirname(__FILE__) . '/lib/cloudwords_client.php';
static $client;
if (!$client) {
$auth_token = 'UserToken ' . \Drupal::config('cloudwords.settings')->get('cloudwords_auth_token');
$client = new CloudwordsClient(_cloudwords_api_url(), CLOUDWORDS_API_VERSION, $auth_token);
$client->set_project_class('\Drupal\cloudwords\CloudwordsDrupalProject');
}
return $client;
}
/**
* Options list callback for translation status.
*/
function cloudwords_exists_options_list() {
return [
CLOUDWORDS_TRANSLATION_STATUS_NOT_TRANSLATED => t('Not translated'),
CLOUDWORDS_TRANSLATION_STATUS_TRANSLATION_EXISTS => t('Translation exists'),
CLOUDWORDS_TRANSLATION_STATUS_OUT_OF_DATE => t('Out of date'),
];
}
/**
* Options list callback for translation status.
*/
function cloudwords_project_active_options_list() {
return [
'configured_project_name' => t('Configured Project Name'),
'configured_project_details' => t('Configured Project Details'),
'uploaded_source_materials' => t('Uploaded Source Materials'),
'in_translation' => t('In Translation'),
'in_review' => t('In Review'),
];
}
/**
* Options list callback for translation status.
*/
function cloudwords_project_closed_options_list() {
return [
'drupal_cancelled' => t('Cancelled In Drupal'),
'project_closed' => t('Project Closed'),
'cancelled' => t('Cancelled'),
];
}
/**
* Implements hook_admin_paths().
*/
// @todo check usage and remove
function cloudwords_admin_paths() {
$paths = [
'cloudwords-project/create' => TRUE,
];
return $paths;
}
/**
* Returns all translatable types.
*
* @return array
* A keyed array of type => type label.
*/
function cloudwords_type_options_list() {
$return = [];
foreach (cloudwords_get_all_source_controllers() as $type => $controller) {
$return[$type] = t($controller->typeLabel());
}
return $return;
}
/**
* Returns all translatable text groups.
*
* @return array
* A keyed array of textgroup => textgroup label.
*/
function cloudwords_textgroup_options_list() {
$return = [];
foreach (cloudwords_get_all_source_controllers() as $controller) {
$return[$controller->textGroup()] = t($controller->textGroupLabel());
}
return $return;
}
/**
* Implements hook_views_api().
*/
function cloudwords_views_api() {
return [
'api' => 3,
// 'path' => drupal_get_path('module', 'cloudwords'),
];
}
/**
* Loads arbitrary translatables given property values.
*
* @param array $properties
* An array of values used to limit the query in the form property => value.
* @param string|null $keyed
* If set, the results will be keyed according to the property specified.
*
* @return array
* A array of translatables.
*/
function cloudwords_get_translatables_by_property(array $properties, $keyed = NULL) {
$query = \Drupal::entityQuery('cloudwords_translatable');
foreach ($properties as $property => $value) {
if(!is_array($value)){
$query->condition($property, $value);
}else{
$query->condition($property, $value, 'IN');
}
}
$result = $query->execute();
if ($result) {
$entity_storage = \Drupal::entityTypeManager()->getStorage('cloudwords_translatable');
$entities = $entity_storage->loadMultiple($result);
// @todo allow for various keys options? Maybe not needed.. Need to return consistent arr
// @ todo consistent structure... keyed only returns one language entity...
if (!$keyed) {
return $entities;
}
$return = [];
foreach ($entities as $entity) {
$eArr = $entity->toArray();
$return[$eArr[$keyed][0]['value']] = $entity;
}
return $return;
}
return [];
}
/**
* Loads a Cloudwords translatable.
*
* @param int $ctid
* A cloudwords translatable id.
*
* @return CloudwordsTranslatable|false
* A translatable or false if not found.
*/
function cloudwords_translatable_load($ctid) {
$entities = \Drupal\cloudwords\Entity\CloudwordsTranslatable::loadMultiple([$ctid]);
return reset($entities);
}
/**
* Loads multiple Cloudwords translatables.
*
* @param array $ctids
* A list of cloudwords translatable ids.
*
* @return array|false
* A list of translatables or false if not found.
*/
function cloudwords_translatable_load_multiple($ctids = [], $conditions = [], $reset = FALSE) {
//@todo deal with conditions if necessary, otherwise remove
return \Drupal\cloudwords\Entity\CloudwordsTranslatable::loadMultiple($ctids);
}
/**
* Creates a new Cloudwords translatable.
*
* @param array $values
* An array of values to apply to the newly created object.
*
* @return CloudwordsTranslatable
* A translatable object with the values applied.
*/
function cloudwords_translatable_create(array $values) {
return \Drupal::entityTypeManager()->getStorage('cloudwords_translatable')->create($values);
}
/**
* Deletes a Cloudwords translatable.
*
* @param int $ctid
* The primary id of a translatable.
*
* @return null|false
* Returns false if the translatable does not exist.
*/
function cloudwords_translatable_delete($ctid) {
return $ctid->delete();
}
/**
* Deletes multiple Cloudwords translatables.
*
* @param array $ctids
* The primary id of a translatable.
*
* @return null|false
* Returns false if the translatable does not exist.
*/
function cloudwords_translatable_delete_multiple(array $ctids) {
$storage_handler = \Drupal::entityTypeManager()->getStorage('cloudwords_translatable');
$entities = $storage_handler->loadMultiple($ctids);
return $storage_handler->delete($entities);
}
/**
* Deletes multiple Cloudwords translatables by property.
*
* @param array $ctids
* The primary id of a translatable.
*
* @return null|false
* Returns false if the translatable does not exist.
*/
function cloudwords_translatable_delete_by_property(array $properties) {
$translatables = cloudwords_get_translatables_by_property($properties);
cloudwords_translatable_delete_multiple(array_keys($translatables));
}
/**
* Retrieves information from hook_cloudwords_translatable_info() implementations.
*
* @param string $type
* (Optional) If specified, a single types' information is returned. Otherwise
* all translatable info is returned.
*
* @return array
* The translatable info for a specific type, or an array of all
* translatable info.
*/
function cloudwords_translatable_info($type = NULL) {
$translatables = &drupal_static(__FUNCTION__);
if (!isset($translatables)) {
$translatables = \Drupal::moduleHandler()->invokeAll('cloudwords_translatable_info');
\Drupal::moduleHandler()->alter('cloudwords_translatable_info', $translatables);
}
if ($type) {
if (isset($translatables[$type])) {
return $translatables[$type];
}
return FALSE;
}
return $translatables;
}
/**
* Returns a list of all statuses the represent the 'closed' state.
*
* @return array
* The list of statuses the represent the 'closed' state.
*/
function cloudwords_project_closed_statuses() {
return [
'project_closed',
'cancelled',
'drupal_cancelled',
];
}
/**
* Implements hook_cron().
*/
function cloudwords_cron() {
module_load_include('inc', 'cloudwords', 'cloudwords.cron');
_cloudwords_cron();
}
function cloudwords_form_views_form_cloudwords_translatable_default_alter(&$form, \Drupal\Core\Form\FormStateInterface &$form_state, $form_id){
//dpm($form['cloudwords_translatable_bulk_form']);
//['']
if (isset($form['cloudwords_translatable_bulk_form'])) {
//@todo remove when VBO supports multipage selection
//unset($form['select']['action::cloudwords_translatable_add_to_project_action']);
foreach (\Drupal\Core\Render\Element::children($form['cloudwords_translatable_bulk_form']) as $delta) {
if ($form['cloudwords_translatable_bulk_form'][$delta]['#return_value']) {
// $form['cloudwords_translatable_bulk_form'][$delta]['#disabled'] = TRUE;
}
//@TODO change to callback when working in core
/*
$form['cloudwords_translatable_bulk_form'][$delta]['#ajax'] = array(
'callback' => 'Drupal\cloudwords\Form\CloudwordsFormAlter::ajaxSelectCallback',
'progress' => 'none',
);
*/
// $form_state
/*
if (in_array($form['views_bulk_operations'][$delta]['#return_value'], $in_project)) {
$form['views_bulk_operations'][$delta]['#disabled'] = TRUE;
}
else {
$form_state['cloudwords_exposed'][] = $form['views_bulk_operations'][$delta]['#return_value'];
}
if ($vbo->view->current_display == 'block_1') {
if (in_array($form['views_bulk_operations'][$delta]['#return_value'], $project_store)) {
$form['views_bulk_operations'][$delta]['#default_value'] = TRUE;
}
$form['views_bulk_operations'][$delta]['#ajax'] = array(
'callback' => 'cloudwords_ajax_select_callback',
'progress' => 'none',
);
}
elseif ($vbo->view->current_display == 'block_2') {
if (in_array($form['views_bulk_operations'][$delta]['#return_value'], $queue)) {
$form['views_bulk_operations'][$delta]['#default_value'] = TRUE;
}
$form['views_bulk_operations'][$delta]['#ajax'] = array(
'callback' => 'cloudwords_ajax_select_callback_queue',
'progress' => 'none',
);
}
*/
}
}
}
function cloudwords_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id){
switch ($form_id) {
case 'views_form_cloudwords_translatable_default':
$form['check'] = [
'#type' => 'checkbox',
'#title' => t('Test'),
'#ajax' => [
'callback' => 'Drupal\cloudwords\Form\CloudwordsFormAlter::ajaxSelectCallback',
'event' => 'change',
],
];
break;
case 'fapi_example_ajax_demo':
$form['check'] = [
'#type' => 'checkbox',
'#title' => t('Test'),
'#ajax' => [
'callback' => 'Drupal\cloudwords\Form\CloudwordsFormAlter::ajaxSelectCallback',
'event' => 'change',
],
];
break;
}
}
function cloudwords_views_bulk_operations_form_alter(&$form, &$form_state, $vbo) {
switch ($vbo->view->base_table) {
case 'cloudwords_translatable':
$user = \Drupal::currentUser();
cloudwords_settings_check();
$form['select']['#collapsible'] = FALSE;
unset($form['select']['#title']);
// Disable items that are in a project.
$in_project = \Drupal::database()->query("SELECT ctid FROM {cloudwords_translatable} WHERE status = 2")->fetchCol();
$project_store = cloudwords_project_user_get($user);
$queue = cloudwords_queue_ctids();
if (isset($form['views_bulk_operations'])) {
//@todo remove when VBO supports multipage selection
unset($form['select']['action::cloudwords_translatable_add_to_project_action']);
foreach (\Drupal\Core\Render\Element::children($form['views_bulk_operations']) as $delta) {
if (in_array($form['views_bulk_operations'][$delta]['#return_value'], $in_project)) {
$form['views_bulk_operations'][$delta]['#disabled'] = TRUE;
}
else {
$form_state['cloudwords_exposed'][] = $form['views_bulk_operations'][$delta]['#return_value'];
}
if ($vbo->view->current_display == 'block_1') {
if (in_array($form['views_bulk_operations'][$delta]['#return_value'], $project_store)) {
$form['views_bulk_operations'][$delta]['#default_value'] = TRUE;
}
$form['views_bulk_operations'][$delta]['#ajax'] = [
'callback' => 'cloudwords_ajax_select_callback',
'progress' => 'none',
];
}
elseif ($vbo->view->current_display == 'block_2') {
if (in_array($form['views_bulk_operations'][$delta]['#return_value'], $queue)) {
$form['views_bulk_operations'][$delta]['#default_value'] = TRUE;
}
$form['views_bulk_operations'][$delta]['#ajax'] = [
'callback' => 'cloudwords_ajax_select_callback_queue',
'progress' => 'none',
];
}
}
}
if ($vbo->view->current_display == 'block_1') {
$form['help_text'] = [
'#markup' => t('Checking an item will add it to the project.'),
];
$form['select']['cloudwords_create'] = [
'#type' => 'submit',
'#submit' => ['cloudwords_create_project_submit'],
'#value' => t('Create project'),
'#prefix' => '<div class="cloudwords-create-project">',
'#suffix' => '<div class="cloudwords-item-count">' . cloudwords_project_user_count($user) . ' ' . t('items in project') . '</div></div>',
];
}
elseif ($vbo->view->current_display == 'block_2') {
$form['select']['text']['#markup'] = cloudwords_queue_count() . ' items marked for translation';
$form['select']['text']['#prefix'] = '<div class="cloudwords-item-count">';
$form['select']['text']['#suffix'] = '</div>';
$form['help_text']['#markup'] = t('Checking an item will mark it for translation.');
}
$form['#attached']['css'][] = drupal_get_path('module', 'cloudwords') . '/cloudwords.css';
$form['#attached']['js'][] = drupal_get_path('module', 'cloudwords') . '/cloudwords.js';
// @FIXME
// url() expects a route name or an external URI.
// $form['#attached']['js'][] = array(
// 'data' => array('cloudwords' => array('ajaxUrl' => url('system/cloudwords/ajax'), 'token' => drupal_get_token('cloudwords'))),
// 'type' => 'setting',
// );
break;
}
}
function cloudwords_create_project_submit($form, &$form_state) {
$form_state['redirect'] = 'cloudwords-project/create';
}
function cloudwords_project_user_remove($uid, array $ctids) {
return \Drupal::database()->update('cloudwords_translatable')
->condition('id', $ctids)
->fields([
'user_id' => 0,
])
->execute();
}
function cloudwords_project_user_add($uid, array $ctids) {
\Drupal::database()->update('cloudwords_translatable')
->condition('id', $ctids)
->fields([
'user_id' => $uid,
])
->execute();
}
function cloudwords_project_user_count($uid) {
return \Drupal::database()->query("SELECT count(id) FROM {cloudwords_translatable} WHERE user_id = :uid", ['uid' => $uid])->fetchField();
}
function cloudwords_project_user_get($uid) {
return \Drupal::database()->query("SELECT id FROM {cloudwords_translatable} WHERE user_id = :uid", ['uid' =>$uid])->fetchCol();
}
function cloudwords_ajax_select_callback($form, FormStateInterface &$form_state) {
$user = \Drupal::currentUser();
if (!$form_state['triggering_element']['#value']) {
cloudwords_project_user_remove($user, [$form_state['triggering_element']['#return_value']]);
}
else {
cloudwords_project_user_add($user, [$form_state['triggering_element']['#return_value']]);
}
return [
'#type' => 'ajax',
'#commands' => [
ajax_command_html('.cloudwords-item-count', cloudwords_project_user_count($user) . ' items in project'),
],
];
}
function cloudwords_ajax_select_callback_queue($form, FormStateInterface &$form_state) {
$user = \Drupal::currentUser();
$translatable = cloudwords_translatable_load($form_state['triggering_element']['#return_value']);
if (!$form_state['triggering_element']['#value']) {
$translatable->dequeue();
}
else {
$translatable->queue();
}
return [
'#type' => 'ajax',
'#commands' => [
ajax_command_html('.cloudwords-item-count', cloudwords_queue_count() . ' items marked for translation.'),
],
];
}
/**
* Returns the count of translatables that are "Marked for translation".
*
* @return int
* The number of translatables.
*/
function cloudwords_queue_count() {
return \Drupal::database()->query("SELECT count(ctid) FROM {cloudwords_translatable} WHERE status = 1")->fetchField();
}
/**
* Selects all translatables that are "Marked for translation".
*
* @return array
* A list of ctids.
*/
function cloudwords_queue_ctids() {
return \Drupal::database()->query("SELECT ctid FROM {cloudwords_translatable} WHERE status = 1")->fetchCol();
}
/**
* Ajax callback for adding and removing translatables from a project.
*/
function cloudwords_ajax_select_all($display, $op, $ctids) {
if (empty($_GET['token']) || !drupal_valid_token($_GET['token'], 'cloudwords')) {
return MENU_ACCESS_DENIED;
}
$ctids = explode(',', $ctids);
$user = \Drupal::currentUser();
if ($display == 'block_2') {
$translatables = cloudwords_translatable_load_multiple($ctids);
if ($op == 'add') {
foreach ($translatables as $translatable) {
$translatable->queue();
}
}
elseif ($op == 'remove') {
foreach ($translatables as $translatable) {
$translatable->dequeue();
}
}
print cloudwords_queue_count() . ' items marked for translation.';
return;
}
elseif ($display == 'block_1') {
if ($op == 'remove') {
cloudwords_project_user_remove($user, $ctids);
}
elseif ($op == 'add') {
cloudwords_project_user_add($user, $ctids);
}
elseif ($op == 'all') {
}
print cloudwords_project_user_count($user) . ' items in project.';
return;
}
}
/**
* Implements hook_form_FORM_ID_alter().
*
* @todo Track down bug in Views exposed filters.
*/
function cloudwords_form_views_exposed_form_alter(&$form, &$form_state) {
/* @todo exposed view form */
/*
if (isset($form_state['input']['type']) && $form_state['input']['type'] != 'node_content') {
$form['type_1']['#access'] = FALSE;
unset($form['#info']['filter-type_1']);
}
if (isset($form_state['input']['type']) && $form_state['input']['type'] != 'taxonomy_term') {
$form['vid']['#access'] = FALSE;
unset($form['#info']['filter-vid']);
}
// This is some bug in views exposed filters.
if (isset($form_state['input']['in_translation']) && is_array($form_state['input']['in_translation'])) {
$form_state['input']['in_translation'] = $form_state['input']['in_translation']['value'];
}
*/
}
/**
* Converts a nested data array into a flattened structure with a combined key.
*
* This function can be used by translators to help with the data conversion.
*
* Nested keys will be joined together using a colon, so for example
* $data['key1']['key2']['key3'] will be converted into
* $flattened_data['key1][key2][key3'].
*
* @param array $data
* The nested array structure that should be flattened.
* @param string $prefix
* Internal use only, indicates the current key prefix when recursing into
* the data array.
* @param array $label
* Internal use only.
*
* @return array
* The flattened data array.
*
* @see cloudwords_unflatten_data()
*/
function cloudwords_flatten_data(array $data, $prefix = NULL, $label =[]) {
$flattened_data = [];
if (isset($data['#label'])) {
$label[] = $data['#label'];
}
// Each element is either a text (has #text property defined) or has children,
// not both.
if (!empty($data['#text'])) {
$flattened_data[$prefix] = $data;
$flattened_data[$prefix]['#parent_label'] = $label;
}
else {
$prefix = isset($prefix) ? $prefix . CLOUDWORDS_ARRAY_DELIMITER : '';
foreach (\Drupal\Core\Render\Element::children($data) as $key) {
$flattened_data += cloudwords_flatten_data($data[$key], $prefix . $key, $label);
}
}
return $flattened_data;
}
/**
* Converts a flattened data structure into a nested array.
*
* This function can be used by translators to help with the data conversion.
*
* Nested keys will be created based on the colon, so for example
* $flattened_data['key1][key2][key3'] will be converted into
* $data['key1']['key2']['key3'].
*
* @param array $flattened_data
* The flattened data array.
*
* @return array
* The nested data array.
*
* @see cloudwords_flatten_data()
*/
function cloudwords_unflatten_data($flattened_data) {
$data = [];
foreach ($flattened_data as $key => $flattened_data_entry) {
\Drupal\Component\Utility\NestedArray::setValue($data, explode(CLOUDWORDS_ARRAY_DELIMITER, $key), $flattened_data_entry);
}
return $data;
}
/**
* Array filter callback for filtering untranslatable source data elements.
*/
function _cloudwords_filter_data($value) {
return !(empty($value['#text']) || (isset($value['#translate']) && $value['#translate'] === FALSE));
}
/**
* String filter to strip out xml control characters prior to constructing or loading xml objects
*/
function _cloudwords_filter_xml_control_characters($string) {
return preg_replace ('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $string);
}
/**
* Converts string keys to array keys.
*
* There are three conventions for data keys in use. This function accepts each
* and ensures an array of keys.
*
* @param array|string $key
* The key can be either be an array containing the keys of a nested array
* hierarchy path or a string with '][' or '|' as delimiter.
*
* @return array
* Array of keys.
*/
function cloudwords_ensure_keys_array($key) {
if (empty($key)) {
return [];
}
if (!is_array($key)) {
if (strstr($key, '|')) {
$key = str_replace('|', CLOUDWORDS_ARRAY_DELIMITER, $key);
}
$key = explode(CLOUDWORDS_ARRAY_DELIMITER, $key);
}
return $key;
}
/**
* Implements hook_multilingual_settings_changed().
*
* Keeps our supported languages in sync. This will also invoke
* hook_cloudwords_language_added() and hook_cloudwords_language_removed().
*/
function cloudwords_multilingual_settings_changed() {
drupal_static_reset('language_list');
// @FIXME
// Could not extract the default value because it is either indeterminate, or
// not scalar. You'll need to provide a default value in
// config/install/cloudwords.settings.yml and config/schema/cloudwords.schema.yml.
$current_lang_codes = \Drupal::config('cloudwords.settings')->get('cloudwords_lang_codes');
require_once dirname(__FILE__) . '/cloudwords.languages.inc';
$new_lang_codes = array_intersect(array_keys(\Drupal::languageManager()->getLanguages()), _cloudwords_map_cloudwords_drupal());
$removed = array_diff($current_lang_codes, $new_lang_codes);
$added = array_diff($new_lang_codes, $current_lang_codes);
\Drupal::configFactory()->getEditable('cloudwords.settings')->set('cloudwords_lang_codes', $new_lang_codes)->save();
if ($added) {
foreach ($added as $langcode) {
\Drupal::moduleHandler()->invokeAll('cloudwords_language_added', [$langcode]);
}
}
if ($removed) {
foreach ($removed as $langcode) {
cloudwords_translatable_delete_by_property(['language' => $langcode]);
\Drupal::moduleHandler()->invokeAll('cloudwords_language_removed', [$langcode]);
}
}
}
/**
* Refreshes all Cloudwords translatables.
*
* Individual implementations of hook_cloudwords_refresh_translatables() can
* batch as well. This allows for large amounts of content to be updated.
*
* @see cloudwords_translation_cloudwords_refresh_translatables()
* @see cloudwords_i18n_cloudwords_refresh_translatables()
*/
function cloudwords_refresh_translatables() {
$batch = [
'title' => t('Refreshing Cloudwords translatables ...'),
'operations' => [],
'init_message' => t('Loading modules to be processed'),
'progress_message' => t('Processed @current out of @total modules.'),
'error_message' => t('An error occurred during processing.'),
'finished' => 'cloudwords_refresh_translatables_finished',
];
foreach (\Drupal::moduleHandler()->getImplementations('cloudwords_refresh_translatables') as $module) {
$batch['operations'][] = [$module . '_cloudwords_refresh_translatables', []];
}
batch_set($batch);
}
/**
* Finished callback for cloudwords_refresh_translatables().
*
* @todo Implement.
*/
function cloudwords_refresh_translatables_finished() {
}
/**
* Wrapper around language_list().
*
* Filters out custom languages and languages unsupported by Cloudwords.
*/
function cloudwords_language_list() {
// @FIXME
// Could not extract the default value because it is either indeterminate, or
// not scalar. You'll need to provide a default value in
// config/install/cloudwords.settings.yml and config/schema/cloudwords.schema.yml.
//@TODO need to limit to CW approved language codes only
// $cloudwords = \Drupal::config('cloudwords.settings')->get('cloudwords_lang_codes');
require_once dirname(__FILE__) . '/cloudwords.languages.inc';
$languages = [];
foreach(\Drupal::languageManager()->getlanguages() as $k => $v){
$languages[$k] = $v->getName();
}
// We keep language_list() here so that the array values are populated.
return array_intersect_key($languages, _cloudwords_map_drupal_cloudwords());
}
/**
* Options callback with our own language list.
*/
function cloudwords_metadata_language_list() {
$return = [];
foreach (cloudwords_language_list() as $key => $language) {
$return[$key] = $language->name;
}
return $return;
}
/**
* Gets the controller for a source.
*
* @param string $type
* The object type.
*
* @return CloudwordsSourceControllerInterface|FALSE
* A source controller object or FALSE if the controller does not exist.
*/
function cloudwords_get_source_controller($type) {
$controllers = &drupal_static(__FUNCTION__, []);
if (!isset($controllers[$type])) {
$info = cloudwords_translatable_info($type);
if (is_array($info) && !empty($info['controller class']) && class_exists($info['controller class'])) {
$controllers[$type] = new $info['controller class']($type);
}
else {
$controllers[$type] = FALSE;
\Drupal::logger('cloudwords')->notice(t('Source controller not found for @type.'), ['@type' => $type]);
}
}
return $controllers[$type];
}
/**
* Gets all of the existing source controllers.
*
* @return array
* A list of CloudwordsSourceControllerInterface objects keyed by type.
*/
function cloudwords_get_all_source_controllers() {
$return = [];
foreach (array_keys(cloudwords_translatable_info()) as $type) {
if ($controller = cloudwords_get_source_controller($type)) {
$return[$type] = $controller;
}
}
return $return;
}
/**
* Helper form for marking items' translations as outdated.
*/
function cloudwords_outdated_form(&$form, &$form_state, array $args) {
$form_state['cloudwords_translatables'] = cloudwords_get_translatables_by_property($args);
$form['cloudwords_outdated'] = [
'#type' => 'checkbox',
'#title' => t('Set translations as outdated.'),
'#default_value' => FALSE,
];
$form['#submit'][] = 'cloudwords_outdated_form_submit';
$form_state->loadInclude('cloudwords', 'inc', 'cloudwords.pages');
}
/**
* Implements hook_modules_enabled().
*
* If a module being enabled implements hook_cloudwords_translatable_info() we
* run the batch command, cloudwords_refresh_translatables() only once as it
* will run for all modules.
*/
function cloudwords_modules_enabled($modules) {
foreach ($modules as $module) {
if (function_exists($module . '_cloudwords_translatable_info')) {
cloudwords_refresh_translatables();
break;
}
}
}
/**
* Maps a Cloudwords language code to a Drupal language code.
*
* @param string $cloudwords_langcode
* A Cloudwords language code.
*
* @return string
* The corresponding Drupal language code.
*/
function cloudwords_map_cloudwords_drupal($cloudwords_langcode) {
require_once dirname(__FILE__) . '/cloudwords.languages.inc';
$map = _cloudwords_map_cloudwords_drupal();
return $map[$cloudwords_langcode];
}
/**
* Maps a Drupal language code to a Cloudwords language code.
*
* @param string $drupal_langcode
* A Drupal language code.
*
* @return string
* The corresponding Cloudwords language code.
*/
function cloudwords_map_drupal_cloudwords($drupal_langcode) {
require_once dirname(__FILE__) . '/cloudwords.languages.inc';
$map = _cloudwords_map_drupal_cloudwords();
return $map[$drupal_langcode];
}
/**
* Implements hook_node_operations().
*/
function cloudwords_node_operations() {
$operations = [
'set_language' => [
'label' => t('Set language for selected content'),
'callback' => 'cloudwords_nodes_set_language',
],
'set_cloudwords_translation_status' => [
'label' => t('Set Cloudwords translation status'),
'callback' => 'cloudwords_translation_status_mass_update',
]
];
return $operations;
}
function cloudwords_nodes_set_language($nodes) {
drupal_goto('admin/cloudwords/set-language/' . implode(',', $nodes));
}
function cloudwords_translation_status_mass_update($nodes){
drupal_goto('admin/cloudwords/set-translation-status/' . implode(',', $nodes));
}
/**
* Implements hook_action_info().
*/
function cloudwords_action_info() {
return [
'cloudwords_nodes_set_language_action' => [
'type' => 'node',
'label' => t('Set language for selected content'),
'configurable' => TRUE,
'behavior' => ['any'],
'triggers' => ['any'],
],
'cloudwords_translation_status_mass_update_action' => [
'type' => 'node',
'label' => t('Set Cloudwords translation status'),
'configurable' => TRUE,
'behavior' => ['any'],
'triggers' => ['any'],
],
'cloudwords_translatable_add_to_project_action' => [
'type' => 'cloudwords_translatable',
'label' => t('Add translatable to Cloudwords project'),
'configurable' => FALSE,
'behavior' => ['any'],
'triggers' => ['any'],
],
];
}
/**
* Implementation of an Drupal Action Configuration Form.
*/
// @TODO needs analoque or remove?
//function cloudwords_nodes_set_language_action_form($context, $node) {
//
// $languages = language_list();
// $lang_opts = array(\Drupal\Core\Language\Language::LANGCODE_NOT_SPECIFIED => 'Language neutral');
//
// foreach ($languages as $key => $lang) {
// $lang_opts[$key] = $lang->name;
// }
//
// $form['language'] = array(
// '#title' => t('Language'),
// '#description' => t('Select the language for the selected nodes.'),
// '#type' => 'select',
// '#options' => $lang_opts,
// );
//
// $question = t('Select a language.');
// $path = 'admin/content';
//
// return confirm_form($form, $question, $path);
//}
/**
* Implementation of an Drupal Action Configuration Form Submit.
*/
function cloudwords_nodes_set_language_action_submit($form, &$form_state) {
return [
'language' => $form_state['values']['language'],
];
}
/**
* Implementation of an Drupal Action.
*/
function cloudwords_nodes_set_language_action(&$node, $context) {
module_load_include('inc', 'cloudwords', 'cloudwords.pages');
_cloudwords_nodes_set_language_single_node_operation($node, $context['language']);
}
/**
* Implementation of an Drupal Action Configuration Form.
*/
function cloudwords_translation_status_mass_update_action_form($context, $node) {
$statuses = cloudwords_exists_options_list();
$status_opts = [null => t('Select one')];
foreach ($statuses as $key => $value) {
$status_opts[$key] = $value;
}
$form['status'] = [
'#title' => t('status'),
'#description' => t('Select the status for selected nodes'),
'#type' => 'select',
'#options' => $status_opts,
];
$question = t('Select a status.');
$path = 'admin/content';
return confirm_form($form, $question, $path);
}
/**
* Implementation of an Drupal Action Configuration Form Submit.
*/
function cloudwords_translation_status_mass_update_action_submit($form, &$form_state) {
return [
'status' => $form_state['values']['status'],
];
}
/**
* Implementation of an Drupal Action.
*/
function cloudwords_translation_status_mass_update_action(&$node, $context) {
module_load_include('inc', 'cloudwords', 'cloudwords.pages');
_cloudwords_translation_status_mass_update_single_node_operation($node, $context['status']);
}
/**
* Displays a warning message if the api token is not set.
*/
function cloudwords_settings_check() {
// @FIXME
// Could not extract the default value because it is either indeterminate, or
// not scalar. You'll need to provide a default value in
// config/install/cloudwords.settings.yml and config/schema/cloudwords.schema.yml.
if (!\Drupal::config('cloudwords.settings')->get('cloudwords_auth_token')) {
// @FIXME
// l() expects a Url object, created from a route name or external URI.
// drupal_set_message(t('API Authorization Token is missing. Click !here to set up your Cloudwords environment.', array('!here' => l(t('here'), 'admin/config/services/cloudwords'))), 'warning');
}
}
/**
* Returns the path to the Cloudwords temporary directory.
*
* @return string
* Path to the Cloudwords temp directory.
*/
function cloudwords_temp_directory() {
// @FIXME
// Could not extract the default value because it is either indeterminate, or
// not scalar. You'll need to provide a default value in
// config/install/cloudwords.settings.yml and config/schema/cloudwords.schema.yml.
$temp_dir = \Drupal::config('cloudwords.settings')->get('cloudwords_temp_directory');
if(empty($temp_dir)){
$temp_dir = file_directory_temp();
}
return $temp_dir;
}
/**
* Returns whether the given content type has support for translations.
*
* @return
* TRUE if translation is supported, and FALSE if not.
*/
function cloudwords_translation_supported_type($type) {
// @FIXME
// // @FIXME
// // The correct configuration object could not be determined. You'll need to
// // rewrite this call manually.
// if(variable_get('language_content_type_' . $type, 0) == TRANSLATION_ENABLED){
// return TRANSLATION_ENABLED;
// }elseif(defined('ENTITY_TRANSLATION_ENABLED') && variable_get('language_content_type_' . $type, 0) == ENTITY_TRANSLATION_ENABLED){
// return ENTITY_TRANSLATION_ENABLED;
// }
}
/**
* Fetches the translated bundle object by the filename
*/
function _cloudwords_get_bundle_by_filename($file_name, $bundles){
foreach($bundles as $bundle){
if($bundle['xliff']['filename'] == $file_name){
return $bundle;
}
}
}
/**
* Implements hook_node_access().
*/
function cloudwords_node_access(\Drupal\node\NodeInterface $node, $op, \Drupal\Core\Session\AccountInterface $account) {
if (($op == 'view') && isset($_GET['cwAccessToken'])){
// @FIXME
// Could not extract the default value because it is either indeterminate, or
// not scalar. You'll need to provide a default value in
// config/install/cloudwords.settings.yml and config/schema/cloudwords.schema.yml.
$tokens = \Drupal::config('cloudwords.settings')->get('cloudwords_node_view_access_bypass');
if(isset($tokens[$node->id()]) && ($_GET['cwAccessToken'] == $tokens[$node->id()])) {
return NODE_ACCESS_ALLOW;
}
}
}
/**
* Implements hook_toolbar().
*/
function cloudwords_toolbar() {
$items = [];
$items['cloudwords'] = [
// '#type' => 'toolbar_item',
'#attached' => [
'library' => [
'cloudwords/cloudwords.icons',
],
],
];
return $items;
}
/**
* Formats a Cloudwords date string.
*
* @param string $date
* A date string from the Cloudwords REST API.
*
* @return string
* A date string formatted m/d/Y.
*/
function _cloudwords_format_display_date($date) {
try {
$datetime = new DateTime($date);
$date = format_date($datetime->format('U'), 'custom', 'm/d/Y', 'UTC');
}
catch (Exception $exc) {
$date = '';
}
return $date;
}
/**
* Implements hook_views_query_alter().
*/
function cloudwords_views_query_alter(\Drupal\views\ViewExecutable $view, \Drupal\views\Plugin\views\query\QueryPluginBase $query){
if($view->id() == 'cloudwords_projects'){
switch($view->current_display){
case 'block_1':
$query->addWhere('', 'status', array_keys(cloudwords_project_closed_options_list()), 'NOT IN');
break;
case 'block_2':
$query->addWhere('', 'status', array_keys(cloudwords_project_closed_options_list()), 'IN');
break;
}
}
}