/**
  * Add a document to the Lingotek platform.
  *
  * Uploads the translatable object's content in the selected language.
  *
  * @param object $translatable_object
  *   A Drupal node object or lingotek ConfigChunk object
  */
 public function addContentDocument(LingotekTranslatableEntity $translatable_object, $with_targets = FALSE)
 {
     $success = FALSE;
     $project_id = $translatable_object->getProjectId();
     $source_language = $translatable_object->getSourceLocale();
     if (empty($source_language)) {
         drupal_set_message('Some entities not uploaded because the source language was language neutral.', 'warning', FALSE);
         LingotekLog::warning('Document @docname not uploaded. Language was language neutral.', array('@docname' => $translatable_object->getDocumentName()));
         return FALSE;
     }
     if ($project_id) {
         $parameters = array('projectId' => $project_id, 'format' => $this->xmlFormat(), 'sourceLanguage' => $source_language, 'tmVaultId' => $translatable_object->getVaultId());
         $parameters['documentName'] = $translatable_object->getDocumentName();
         $parameters['documentDesc'] = $translatable_object->getDescription();
         $parameters['content'] = $translatable_object->documentLingotekXML();
         $parameters['url'] = $translatable_object->getUrl();
         $parameters['workflowId'] = $translatable_object->getWorkflowId();
         $this->addAdvancedParameters($parameters, $translatable_object);
         if ($with_targets) {
             $parameters['targetAsJSON'] = Lingotek::getLanguagesWithoutSourceAsJSON($source_language);
             $parameters['applyWorkflow'] = 'true';
             // API expects a 'true' string
             $result = $this->request('addContentDocumentWithTargetsAsync', $parameters);
         } else {
             $result = $this->request('addContentDocumentAsync', $parameters);
         }
         if ($result) {
             if (isset($result->errors) && $result->errors) {
                 LingotekLog::error(t('Request to send document to Lingotek failed: ') . print_r($result->errors, TRUE), array());
                 $translatable_object->setStatus(LingotekSync::STATUS_FAILED);
                 $translatable_object->setLastError(is_array($result->errors) ? array_shift($result->errors) : $result->errors);
                 return FALSE;
             }
             if (get_class($translatable_object) == 'LingotekConfigSet') {
                 $translatable_object->setDocumentId($result->id);
                 $translatable_object->setProjectId($project_id);
                 $translatable_object->setStatus(LingotekSync::STATUS_CURRENT);
                 $translatable_object->setTargetsStatus(LingotekSync::STATUS_PENDING);
                 // WTD: there is a race condition here where a user could modify a locales-
                 // source entry between the time the dirty segments are pulled and the time
                 // they are set to current at this point.  This same race condition exists
                 // for nodes as well; however, the odds may be lower due to number of entries.
                 LingotekConfigSet::setSegmentStatusToCurrentById($translatable_object->getId());
             } else {
                 // node assumed (based on two functions below...
                 $entity_type = $translatable_object->getEntityType();
                 lingotek_keystore($entity_type, $translatable_object->getId(), 'document_id', $result->id);
                 lingotek_keystore($entity_type, $translatable_object->getId(), 'last_uploaded', time());
             }
             $success = TRUE;
         }
     }
     return $success;
 }
 /**
  * Gets the translation targets associated with this document.
  *
  * @return array
  *   An array of Translation Target, as returned by a getDocument
  *   Lingotek API call
  */
 public function translationTargets()
 {
     $targets = array();
     if ($document = LingotekApi::instance()->getDocument($this->document_id)) {
         if (!empty($document->translationTargets)) {
             foreach ($document->translationTargets as $target) {
                 $targets[Lingotek::convertLingotek2Drupal($target->language)] = $target;
             }
         }
     }
     return $targets;
 }
Exemple #3
0
 public function get_token_details($access_token)
 {
     $url = $this->base_url . "/auth/oauth2/access_token_info?access_token=" . $access_token;
     Lingotek::log("GET " . $url . " (" . __METHOD__ . ")");
     $response = wp_remote_get($url);
     $response_code = wp_remote_retrieve_response_code($response);
     if ($response_code == 200) {
         $response_body = json_decode(wp_remote_retrieve_body($response));
         $token_details = $response_body;
     } else {
         $token_details = FALSE;
     }
     return $token_details;
 }
Exemple #4
0
 public function request($query_vars)
 {
     if (empty($query_vars['lingotek'])) {
         return $query_vars;
     }
     if (isset($_GET['type'], $_GET['document_id']) && ($document = $this->lgtm->get_group_by_id($_GET['document_id']))) {
         // url for in context review
         if (isset($_GET['locale']) && 'get' == $_GET['type']) {
             $locale = Lingotek::map_to_wp_locale($_GET['locale']);
             // map to WP locale
             // posts
             if (post_type_exists($document->type)) {
                 if ($id = $document->pllm->get_post($document->source, $locale)) {
                     wp_redirect(get_permalink($id), 301);
                     exit;
                 } else {
                     wp_redirect(get_permalink($document->source), 302);
                     exit;
                 }
             } elseif (taxonomy_exists($document->type) && ($id = $document->pllm->get_term($document->source, $locale))) {
                 wp_redirect(get_term_link($id, $document->type), 301);
                 exit;
             }
             status_header(404);
             // no document found
             die;
         }
         if ('document_uploaded' == $_GET['type']) {
             $document->source_ready();
             if ($document->is_automatic_upload()) {
                 $document->request_translations();
             }
         }
         if (isset($_GET['locale']) && 'target' == $_GET['type']) {
             // We will need access to PLL_Admin_Sync::copy_post_metas
             global $polylang;
             $polylang->sync = new PLL_Admin_Sync($polylang);
             $locale = Lingotek::map_to_wp_locale($_GET['locale']);
             // map to WP locale
             $document->is_automatic_download($locale) ? $document->create_translation($locale, true) : $document->translation_ready($locale);
         }
         status_header(200);
         // useless as it the default value
         die;
     }
     status_header(404);
     // no document found
     die;
 }
Exemple #5
0
 public function __construct()
 {
     $plugin = Lingotek::get_instance();
     $this->plugin_slug = $plugin->get_plugin_slug();
     $this->dashboard = new Lingotek_Dashboard($plugin);
     $this->pllm = $GLOBALS['polylang']->model;
     add_action('admin_enqueue_scripts', array(&$this, 'admin_enqueue_scripts'));
     // adds a 'settings' link in the plugins table
     add_filter('plugin_action_links_' . LINGOTEK_BASENAME, array(&$this, 'plugin_action_links'));
     // adds the link to the languages panel in the wordpress admin menu
     add_action('admin_menu', array(&$this, 'add_menus'));
     add_action('load-translation_page_wp-lingotek_manage', array(&$this, 'load_manage_page'));
     add_filter('set-screen-option', array($this, 'set_screen_option'), 10, 3);
     add_action('wp_ajax_' . $this->ajax_dashboard_language_endpoint, array(&$this->dashboard, 'ajax_language_dashboard'));
     add_action('wp_ajax_get_current_status', array($this, 'ajax_get_current_status'));
     //Network admin menu
     add_action('network_admin_menu', array($this, 'add_network_admin_menu'));
 }
Exemple #6
0
 public function add_profile_column_data($column_name, $post_id)
 {
     if ($column_name == 'profile') {
         $document = $this->lgtm->get_group('post', $post_id);
         if (isset($document->source)) {
             $post_id = $document->source;
         }
         $profiles = Lingotek::get_profiles();
         $content_profiles = get_option('lingotek_content_type');
         $post_profile = Lingotek_Post_actions::get_post_profile($post_id);
         $post_language = $this->pllm->get_post_language($post_id);
         $post_type = 'post';
         if (isset($_REQUEST['post_type'])) {
             $post_type = $_REQUEST['post_type'];
         }
         if ($post_profile) {
             echo $profiles[$post_profile->description]['name'] . sprintf('<a title="%s">%s</a>', __('Not set to the content default profile', 'wp-lingotek'), '*');
         } else {
             if ($post_language && isset($content_profiles[$post_type]['sources'][$post_language->slug])) {
                 $profile = $content_profiles[$post_type]['sources'][$post_language->slug];
                 echo $profiles[$profile]['name'];
             } else {
                 if (!empty($content_profiles)) {
                     echo $profiles[$content_profiles[$post_type]['profile']]['name'];
                 } else {
                     if ($post_type == 'post') {
                         _e('Automatic', 'wp-lingotek');
                     } else {
                         if ($post_type == 'page') {
                             _e('Manual', 'wp-lingotek');
                         }
                     }
                 }
             }
         }
     }
 }
 /**
  * Updates the local content with data from a Lingotek Document.
  *
  * @return bool
  *   TRUE if the content updates succeeded, FALSE otherwise.
  */
 public function updateLocalContent()
 {
     $success = TRUE;
     $metadata = $this->metadata();
     if (!empty($metadata['document_id'])) {
         $document_id = $metadata['document_id'];
         $api = LingotekApi::instance();
         $document = $api->getDocument($document_id);
         foreach ($document->translationTargets as $target) {
             $document_xml = $api->downloadDocument($metadata['document_id'], $target->language);
             $target_language = Lingotek::convertLingotek2Drupal($target->language);
             foreach ($document_xml as $drupal_field_name => $content) {
                 // Figure out which subkey of the field data we're targeting.
                 // "value" for standard text fields, or some other key for
                 // compound text fields (text with summary, for example).
                 $target_key = 'value';
                 $subfield_parts = explode('__', $drupal_field_name);
                 if (count($subfield_parts) == 2) {
                     $drupal_field_name = $subfield_parts[0];
                     $target_key = $subfield_parts[1];
                 }
                 $field = field_info_field($drupal_field_name);
                 if (!empty($field['lingotek_translatable'])) {
                     $comment_field =& $this->comment->{$drupal_field_name};
                     $index = 0;
                     foreach ($content as $text) {
                         $comment_field[$target_language][$index][$target_key] = decode_entities(lingotek_xml_decode($text));
                         // Copy filter format from source language field.
                         if (!empty($comment_field[$this->comment->language][0]['format'])) {
                             $comment_field[$target_language][$index]['format'] = $comment_field[$this->comment->language][0]['format'];
                         }
                         $index++;
                     }
                 }
             }
             $comment_node = LingotekNode::loadById($this->comment->nid);
             $comment_fields = array_keys(field_info_instances('comment', 'comment_node_' . $comment_node->type));
             foreach ($comment_fields as $field) {
                 // Copy any untranslated fields from the default language into this target.
                 if (isset($this->comment->{$field}[$this->comment->language]) && !isset($this->comment->{$field}[$target_language])) {
                     $this->comment->{$field}[$target_language] = $this->comment->{$field}[$this->comment->language];
                 }
                 // Ensure that all fields get their LANGUAGE_NONE field data populated with the
                 // comment's default language data, to support toggling off of comment translation
                 // at some point in the future.
                 if (!empty($this->comment->{$field}[$this->comment->language])) {
                     $this->comment->{$field}[LANGUAGE_NONE] = $this->comment->{$field}[$this->comment->language];
                 }
             }
         }
         // This avoids an infitinite loop when hooks resulting from comment_save() are invoked.
         self::$content_update_in_progress = TRUE;
         comment_save($this->comment);
         self::$content_update_in_progress = FALSE;
         $this->comment = comment_load($this->comment->cid);
     } else {
         LingotekLog::error('Unable to refresh local contents for comment @cid. Could not find Lingotek Document ID.', array('@cid' => $this->comment->cid));
         $success = FALSE;
     }
     return $success;
 }
Exemple #8
0
<?php

global $polylang;
$profiles = Lingotek::get_profiles();
$profiles = $this->get_profiles_usage($profiles);
$settings = $this->get_profiles_settings();
if (isset($_GET['lingotek_action']) && 'delete-profile' == $_GET['lingotek_action']) {
    check_admin_referer('delete-profile');
    // check again that usage empty
    if (!empty($profiles[$_GET['profile']]) && empty($profiles[$_GET['profile']]['usage'])) {
        unset($profiles[$_GET['profile']]);
        update_option('lingotek_profiles', $profiles);
        add_settings_error('lingotek_profile', 'default', __('Your translation profile was sucessfully deleted.', 'wp-lingotek'), 'updated');
        set_transient('settings_errors', get_settings_errors(), 30);
        wp_redirect(admin_url('admin.php?page=wp-lingotek_manage&sm=profiles&settings-updated=1'));
        exit;
    }
}
if (!empty($_POST)) {
    check_admin_referer('lingotek-edit-profile', '_wpnonce_lingotek-edit-profile');
    $defaults = get_option('lingotek_defaults');
    if (empty($_POST['name']) && empty($_POST['profile'])) {
        add_settings_error('lingotek_profile', 'default', __('You must provide a name for your translation profile.', 'wp-lingotek'), 'error');
    } else {
        $profile_id = empty($_POST['profile']) ? uniqid(rand()) : $_POST['profile'];
        $profiles[$profile_id]['profile'] = $profile_id;
        if (!empty($_POST['name'])) {
            $profiles[$profile_id]['name'] = strip_tags($_POST['name']);
        }
        foreach (array('upload', 'download', 'project_id', 'workflow_id', 'primary_filter_id', 'secondary_filter_id') as $key) {
            if (isset($_POST[$key]) && in_array($_POST[$key], array_keys($settings[$key]['options']))) {
 /**
  * Set the entity's language to be used by Lingotek, which will
  * sometimes be different from the stated Drupal language.
  */
 public function setLanguage($language = NULL)
 {
     if (empty($language)) {
         $drupal_locale = Lingotek::convertDrupal2Lingotek($this->entity->language);
         if (!empty($this->entity->lingotek['allow_source_overwriting']) && !empty($this->entity->lingotek['source_language_' . $drupal_locale])) {
             $language = $this->entity->lingotek['source_language_' . $drupal_locale];
         } else {
             $language = $this->entity->language;
         }
     }
     $this->language = $language;
     $this->locale = Lingotek::convertDrupal2Lingotek($this->language);
     $this->language_targets = Lingotek::getLanguagesWithoutSource($this->locale);
 }
Exemple #10
0
 public static function getDirtyChunkLids()
 {
     // return the list of all lids from the locale_source table *not* fully translated
     $source_language = language_default();
     if (!isset($source_language->lingotek_locale)) {
         $source_language->lingotek_locale = Lingotek::convertDrupal2Lingotek($source_language->language);
     }
     $lingotek_codes = Lingotek::getLanguagesWithoutSource($source_language->lingotek_locale);
     if (!count($lingotek_codes)) {
         LingotekLog::error('No languages configured for this Lingotek account.', array());
         return array();
     }
     // get the drupal language for each associated lingotek locale
     $drupal_codes = array();
     foreach ($lingotek_codes as $lc) {
         $drupal_codes[] = Lingotek::convertLingotek2Drupal($lc);
     }
     // get the list of all segments that need updating
     // that belong to the textgroups the user wants translated
     $textgroups = array_merge(array(-1), LingotekConfigChunk::getTextgroupsForTranslation());
     $max_length = variable_get('lingotek_config_max_source_length', LINGOTEK_CONFIG_MAX_SOURCE_LENGTH);
     $query = db_select('{locales_source}', 'ls');
     $query->fields('ls', array('lid'))->condition('ls.source', '', '!=')->condition('ls.lid', self::getQueryCompletedConfigTranslations($drupal_codes), 'NOT IN')->where('length(ls.source) < ' . (int) $max_length);
     if (in_array('misc', $textgroups)) {
         $or = db_or();
         $or->condition('ls.textgroup', $textgroups, 'IN');
         $or->where("ls.textgroup NOT IN ('default','menu','taxonomy','views','blocks','field')");
         $query->condition($or);
     } else {
         $query->condition('ls.textgroup', $textgroups, 'IN');
     }
     return $query->execute()->fetchCol();
 }
Exemple #11
0
 public static function lingotek_edit_meta_box_html()
 {
     wp_enqueue_script('lingotek_defaults', LINGOTEK_URL . '/js/defaults.js');
     global $post;
     $post_type = get_post_type($post->ID);
     $lgtm = new Lingotek_Model();
     $group = $lgtm->get_group('post', $post->ID);
     $profiles = Lingotek::get_profiles();
     $content_profiles = get_option('lingotek_content_type');
     $language_profiles = self::retrieve_lang_Profiles($post_type, $profiles, $content_profiles);
     $default_name = empty($content_profiles) == false ? $profiles[$content_profiles[$post->post_type]['profile']]['name'] : ($post_type == 'page' ? __('Manual', 'wp-lingotek') : __('Automatic', 'wp-lingotek'));
     $content_default_profile = array('default' => array('name' => __('Content Default', 'wp-lingotek') . ' (' . $default_name . ')'));
     $language_profiles['defaults'] = array('content_default' => $default_name, 'title' => __('Content Default', 'wp-lingotek'));
     $profiles = array_merge($content_default_profile, $profiles);
     $post_profile = self::get_post_profile($post->ID);
     if (isset($post_profile)) {
         $selected[$post_profile->description] = $profiles[$post_profile->description];
         unset($profiles[$post_profile->description]);
         $profiles = array_merge($selected, $profiles);
     }
     if (isset($group->source)) {
         // Disables selection of a different profile if content has been uploaded to Lingotek
         $args = array('document_id' => $group->document_id, 'action' => 'lingotek-delete', 'noheader' => true);
         if ($post_type == 'page') {
             $args['lingotek_redirect'] = true;
         }
         $site_id = get_current_blog_id();
         $url = $post_type == 'page' ? get_site_url($site_id, '/wp-admin/edit.php?post_type=page') : get_site_url($site_id, '/wp-admin/edit.php');
         $disassociate_url = wp_nonce_url(add_query_arg($args, $url), 'lingotek-delete');
         $remove_post = 'post=' . $post->ID;
         $disassociate_url = str_replace($remove_post, '', $disassociate_url);
         $prefs = Lingotek_Model::get_prefs();
         $confirm_message = isset($prefs['delete_document_from_tms']) === false ? __('Are you sure you want to do this?', 'wp-lingotek') : __('Are you sure you want to do this? The document will be deleted from Lingotek TMS.', 'wp-lingotek');
         $confirm_message = sprintf(' onclick = "return confirm(\'%s\');"', $confirm_message);
         printf('<strong>%s</strong><br><br>', __('Translation Profile', 'wp-lingotek'));
         printf('<em>%s</em><br>', __('Disassociate this content to change the Translation Profile', 'wp-lingotek'));
         printf('<a class="button button-small" href="%s" %s>%s</a><br><br>', esc_url($disassociate_url), $confirm_message, __('Disassociate', 'wp-lingotek'));
         printf('<select disabled class="lingotek-profile-setting" name="%1$s" id="%1$s">', 'lingotek_profile_meta');
     } else {
         printf('<strong>%s</strong><br><br>', __('Translation Profile', 'wp-lingotek'));
         printf('<select class="lingotek-profile-setting" name="%1$s" id="%1$s">', 'lingotek_profile_meta');
     }
     foreach ($profiles as $key => $profile) {
         echo "\n\t<option value=" . esc_attr($key) . ">" . esc_attr($profile['name']) . '</option>';
     }
     echo '</select>';
     echo '<div id="lingotek-language-profiles" style="display: none;">' . json_encode($language_profiles) . '</div>';
 }
Exemple #12
0
 function __construct($content_types)
 {
     parent::__construct(array('plural' => 'lingotek-content', 'ajax' => false));
     $this->profiles = Lingotek::get_profiles();
     $this->content_types = $content_types;
 }
Exemple #13
0
 /**
  * Return an instance of this class.
  *
  * @since     1.0.0
  *
  * @return    object    A single instance of this class.
  */
 public static function get_instance()
 {
     // If the single instance hasn't been set, set it now.
     if (null == self::$instance) {
         self::$instance = new self();
     }
     return self::$instance;
 }
 /**
  * Gets the comment-specific parameters for use in a createContentDocumentWithTargets API call.
  *
  * @param LingotekComment
  *   The comment entity to be translated.
  *
  * @return array
  *   An array of API parameter values.
  */
 protected function getCommentCreateWithTargetsParams(LingotekComment $comment)
 {
     $target_locales = Lingotek::availableLanguageTargets("lingotek_locale");
     $parameters = array('projectId' => variable_get('lingotek_project', NULL), 'documentName' => 'comment - ' . $comment->cid, 'documentDesc' => 'comment ' . $comment->cid . ' on node ' . $comment->nid, 'format' => $this->xmlFormat(), 'applyWorkflow' => 'true', 'workflowId' => variable_get('lingotek_translate_comments_workflow_id', NULL), 'sourceLanguage' => Lingotek::convertDrupal2Lingotek($comment->language), 'tmVaultId' => variable_get('lingotek_vault', 1), 'content' => $comment->documentLingotekXML(), 'targetAsJSON' => drupal_json_encode(array_values($target_locales)), 'note' => url('node/' . $comment->nid, array('absolute' => TRUE, 'alias' => TRUE)));
     $this->addAdvancedParameters($parameters, $comment);
     return $parameters;
 }
 public function getSourceLocale()
 {
     return Lingotek::convertDrupal2Lingotek($this->entity->language);
 }
Exemple #16
0
<?php

$sites = wp_get_sites();
$site_data = array();
foreach ($sites as $site) {
    switch_to_blog($site['blog_id']);
    $details = get_blog_details($site['blog_id'])->blogname;
    $temp = array("blog_id" => $site['blog_id'], "blogname" => $details);
    array_push($site_data, $temp);
    $ltk = new Lingotek();
    $ltk->admin_init();
    restore_current_blog();
}
if (!empty($_POST)) {
    $source_site = $_POST['source'];
    if (isset($_POST['destination'])) {
        $destination_site = $_POST['destination'];
        foreach ($destination_site as $destination) {
            if (!empty($_POST['settings'])) {
                $selected_settings = $_POST['settings'];
                foreach ($selected_settings as $setting) {
                    //Updates account options for access token and the base url to connect to Lingotek
                    if ($setting == 'token') {
                        $lingotek_option = 'lingotek_' . $setting;
                        $source_options = get_blog_option($source_site, $lingotek_option);
                        update_blog_option($destination, $lingotek_option, $source_options);
                        $source_options = get_blog_option($source_site, 'lingotek_base_url');
                        update_blog_option($destination, 'lingotek_base_url', $source_options);
                    }
                    //Updates the chosen option
                    $lingotek_option = 'lingotek_' . $setting;
 public function patch($url, $args = array())
 {
     Lingotek::log("PATCH " . $url);
     if (!empty($args)) {
         Lingotek::log($args);
     }
     return wp_remote_request($url, array('method' => 'PATCH', 'headers' => $this->headers, 'body' => $args));
 }
 /**
  * Updates the local content of $target_code with data from a Lingotek Document
  *
  * @param string $lingotek_locale
  *   The code for the language that needs to be updated.
  * @return bool
  *   TRUE if the content updates succeeded, FALSE otherwise.
  */
 public function downloadTriggered($lingotek_locale)
 {
     $metadata = $this->metadata();
     $document_id = $metadata['document_id'];
     if (empty($document_id)) {
         LingotekLog::error('Unable to refresh local contents for config chunk @cid. Could not find Lingotek Document ID.', array('@cid' => $this->cid));
         return FALSE;
     }
     $api = LingotekApi::instance();
     $document_xml = $api->downloadDocument($document_id, $lingotek_locale);
     $target_language = Lingotek::convertLingotek2Drupal($lingotek_locale);
     /* FAST VERSION (git history for slow version) */
     // 1. save the dirty targets associated with given language
     $dirty_lids = self::getDirtyLidsByChunkIdAndLanguage($this->cid, $target_language);
     // 2. delete all segment targets associated with given language
     self::deleteSegmentTranslationsByChunkIdAndLanguage($this->cid, $target_language);
     // 3. insert all segments for the given language
     self::saveSegmentTranslations($document_xml, $target_language);
     // 4. return the dirty targets' statuses
     self::restoreDirtyLids($dirty_lids);
     /* END FAST */
     // set chunk status to current
     $this->setStatus(LingotekSync::STATUS_CURRENT);
     $this->setTargetsStatus(LingotekSync::STATUS_CURRENT, $lingotek_locale);
     return TRUE;
 }
Exemple #19
0
 /**
  * Lingotek
  */
 function ajax_language_dashboard()
 {
     global $polylang;
     $request_method = isset($_REQUEST['_method']) ? $_REQUEST['_method'] : (isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET');
     $response = array('method' => $request_method);
     switch ($request_method) {
         case 'POST':
             if (isset($_REQUEST['code'], $_REQUEST['native'], $_REQUEST['direction'])) {
                 $name = $_REQUEST['native'];
                 $slug = substr($_REQUEST['code'], 0, strpos($_REQUEST['code'], '_'));
                 // 3rd parameter of strstr needs PHP 5.3
                 $locale = Lingotek::map_to_wp_locale($_REQUEST['code']);
                 // avoid conflicts between language slugs
                 $existing_slugs = $polylang->model->get_languages_list(array('fields' => 'slug'));
                 if (!empty($existing_slugs) && in_array($slug, $existing_slugs)) {
                     $slug = strtolower(str_replace('_', '-', $locale));
                 }
                 $rtl = $_REQUEST['direction'] == 'RTL';
                 $term_group = 0;
                 // adds the language
                 $polylang->model->add_language(compact('name', 'slug', 'locale', 'rtl', 'term_group'));
                 // attempts to install the language pack
                 require_once ABSPATH . 'wp-admin/includes/translation-install.php';
                 wp_download_language_pack($locale);
                 // force checking for themes and plugins translations updates
                 wp_update_themes();
                 wp_update_plugins();
                 $response = array('request' => 'POST: add target language to CMS and Lingotek Project Language', 'locale' => $_REQUEST['code'], 'xcode' => $locale, 'active' => 1, 'enabled' => 1, 'source' => self::get_counts_by_type($locale, 'sources'), 'target' => self::get_counts_by_type($locale, 'targets'));
                 status_header(200);
             }
             break;
         case 'DELETE':
             $body = file_get_contents("php://input");
             $code = str_replace('code=', '', $body);
             $lang = $polylang->model->get_language(Lingotek::map_to_wp_locale($code));
             // map code to WP locales to find the language
             // prevents deleting the last language as it would break the Lingotek dashboard
             if (1 == count($polylang->model->get_languages_list())) {
                 $response = array('request' => sprintf('DELETE: remove language from CMS and project (%s)', $code), 'code' => $code, 'success' => false, 'message' => __('You must keep at least one language.', 'wp-lingotek'));
                 status_header(403);
             } elseif (!self::has_language_content($lang)) {
                 $default_category = pll_get_term(get_option('default_category'), $lang->slug);
                 $polylang->model->delete_language((int) $lang->term_id);
                 wp_delete_term($default_category, 'category');
                 // delete the default category after the language
                 // Deletes the translation status so when re-adding a language the string groups translations won't display as current
                 $lingotek_model = new Lingotek_Model();
                 $strings = $lingotek_model->get_strings();
                 foreach ($strings as $string) {
                     $group = $lingotek_model->get_group('string', $string['context']);
                     unset($group->translations[$lang->locale]);
                     $group->save();
                 }
                 $response = array('request' => sprintf('DELETE: remove language from CMS and project (%s)', $code), 'code' => $code, 'active' => false, 'success' => true);
                 status_header(204);
             } else {
                 $response = array('request' => sprintf('DELETE: remove language from CMS and project (%s)', $code), 'code' => $code, 'success' => false, 'message' => __('The language can only be removed when no existing content is using it.  If you would like to remove this language from the site, then first remove any content assigned to this language.', 'wp-lingotek'));
                 status_header(403);
             }
             break;
         case 'GET':
         default:
             $locale_code = isset($_REQUEST['code']) ? $_REQUEST['code'] : NULL;
             $response = $response + $this->get_language_details($locale_code);
             break;
     }
     wp_send_json($response);
 }