File: /volume1/web/wordpress/wp-content/plugins/lingotek-translation/include/model.php
<?php
/*
* Manages interactions with database
* Factory for Lingotek_Group objects
*
* @since 0.1
*/
class Lingotek_Model {
public $pllm; // Polylang model
static public $copying_post;
static public $copying_term;
/*
* constructor
*
* @since 0.1
*/
public function __construct() {
$this->pllm = $GLOBALS['polylang']->model;
register_taxonomy('lingotek_profile', null , array('label' => false, 'public' => false, 'query_var' => false, 'rewrite' => false));
register_taxonomy('lingotek_hash', null , array('label' => false, 'public' => false, 'query_var' => false, 'rewrite' => false));
}
/*
* get the strings groups as well as their count
*
* @since 0.2
*
* @return array
*/
public static function get_strings() {
static $strings = array();
if (empty($strings)) {
PLL_Admin_Strings::init(); // enables sanitization filter
foreach (PLL_Admin_Strings::get_strings() as $string) {
$strings[$string['context']]['context'] = $string['context'];
$strings[$string['context']]['count'] = empty($strings[$string['context']]['count']) ? 1 : $strings[$string['context']]['count'] + 1;
}
$strings = array_values($strings);
}
return $strings;
}
/*
* create a translation group object from a translation term
*
* @since 0.2
*
* @param object $term
* @return object
*/
protected function convert_term($term) {
switch($term->taxonomy) {
case 'term_translations':
return new Lingotek_Group_Term($term, $this->pllm);
case 'post_translations':
$class = $term->name == $term->slug ? 'Lingotek_Group_Post' : 'Lingotek_Group_String';
return new $class($term, $this->pllm);
}
}
/*
* get the translation term of an object
*
* @since 0.2
*
* @param string $type either 'post' or 'term' or 'string'
* @param int|string $id post id or term id or strings translations group name
* @return object translation term
*/
public function get_group($type, $id) {
switch ($type) {
case 'post':
return ($post = PLL()->model->post->get_object_term((int) $id, $type . '_translations')) && !empty($post) ? $this->convert_term($post) : false;
case 'term':
return ($term = PLL()->model->term->get_object_term((int) $id, $type . '_translations')) && !empty($term) ? $this->convert_term($term) : false;
case 'string':
if (is_numeric($id)) {
$strings = self::get_strings();
$id = $strings[$id]['context'];
}
return ($term = get_term_by('name', $id, 'post_translations')) && !empty($term) ? $this->convert_term($term) : false;
default:
return false;
}
}
/*
* get the translation term of an object by its Lingotek document id
*
* @since 0.2
*
* @param string|object $document_id
* @return object translation term
*/
public function get_group_by_id($document_id) {
// we already passed a translation group object
if (is_object($document_id))
return $document_id;
$terms = get_terms(array('post_translations', 'term_translations'), array('slug' => $document_id));
return is_wp_error($terms) || empty($terms) ? false : $this->convert_term(reset($terms));
}
/*
* get a translation profile
*
* @since 0.2
*
* @param string $type post type or taxonomy
* @param object $language
* @return array
*/
static public function get_profile($type, $language, $post_id = null) {
$content_types = get_option('lingotek_content_type');
$profiles = get_option('lingotek_profiles');
// If a profile is set for a specific post/page get that first
if ($post_id) {
$terms = get_terms('lingotek_profile', 'orderby=count&hide_empty=0');
foreach ($terms as $term) {
$extracted_post_id = str_replace('lingotek_profile_', '', $term->name);
if ($extracted_post_id == $post_id) {
return $profiles[$term->description];
}
}
}
// default profile is manual except for post. Custom types are set to disabled by default.
$default = 'post' === $type || 'page' === $type ? 'manual' : 'disabled';
$profile = is_object($language) && isset($content_types[$type]['sources'][$language->slug]) ?
$content_types[$type]['sources'][$language->slug] :
(isset($content_types[$type]['profile']) ? $content_types[$type]['profile'] : $default);
return $profiles[$profile];
}
static public function get_prefs() {
$default = array(
'download_post_status' => Lingotek_Group_Post::SAME_AS_SOURCE,
'auto_upload_post_statuses' => array(
'draft' => 0, // ignore auto-upload
'pending' => 1, // auto-upload
'publish' => 1,
'future' => 1,
'private' => 0,
),
'delete_document_from_tms' => array(
'delete' => 1,
),
'delete_linked_content' => array(
'enabled' => 1,
),
'auto_update_status' => '10'
);
$prefs = array_merge($default, get_option('lingotek_prefs', $default)); // ensure defaults are set for missing keys
return $prefs;
}
/*
* get a profile option
*
* @since 0.2
*
* @param string $item 'project_id' | 'workflow_id' | 'upload' | 'download'
* @param string $type post type or taxonomy
* @param object $source_language
* @param object $target_language optional, needed to get custom target informations 'workflow_id' | 'download'
* @return string | bool either the option or false if the translation is disabled
*/
static public function get_profile_option($item, $type, $source_language, $target_language = false, $post_id = null) {
$profile = self::get_profile($type, $source_language, $post_id);
if ('disabled' === $profile['profile'] || is_object($target_language) && isset($profile['targets'][$target_language->slug]) && 'disabled' === $profile['targets'][$target_language->slug])
return false;
if (!empty($target_language) && isset($profile['targets'][$target_language->slug]) && !empty($profile['custom'][$item][$target_language->slug]))
return $profile['custom'][$item][$target_language->slug];
if (!empty($profile[$item]))
return $profile[$item];
$defaults = get_option('lingotek_defaults');
return $defaults[$item];
}
/*
* find targets that are set to copy in a profile
*
* @since 1.1.1
*
* @param array $profile (use get_profile to retrieve)
* @return array of targets that should be copied. if none exist returns empty array
*/
public function targets_to_be_copied($profile) {
if (isset($profile['targets']) && in_array('copy', $profile['targets'])) {
$targets_to_copy = array_keys($profile['targets'], 'copy');
return $targets_to_copy;
}
else {
return array();
}
}
/*
* copy a post from the source language to a target language
*
* @since 1.1.1
*
* @param object $post
* @param string $target polylang language slug (ex: en, de, fr, etc)
* @return $new_post_id if copy of post is successful, false otherwise
*/
public function copy_post($post, $target) {
self::$copying_post = true;
$document = $this->get_group('post', $post->ID);
$prefs = self::get_prefs();
$cp_lang = $this->pllm->get_language($target);
$cp_post = (array) $post;
$cp_post['post_status'] = ($prefs['download_post_status'] === 'SAME_AS_SOURCE')? $post->post_status : $prefs['download_post_status']; // status
$slug = $cp_post['post_name'];
unset($cp_post['ID']);
unset($cp_post['post_name']);
if (!isset($document->desc_array[$target])) {
$new_post_id = wp_insert_post($cp_post, true);
if (!is_wp_error($new_post_id)) {
PLL()->model->post->set_language($new_post_id, $cp_lang);
wp_set_object_terms($new_post_id, $document->term_id, 'post_translations');
$GLOBALS['polylang']->sync->taxonomies->copy($document->source, $new_post_id, $cp_lang->slug);
$GLOBALS['polylang']->sync->post_metas->copy($document->source, $new_post_id, $cp_lang->slug);
Lingotek_Group_Post::copy_or_ignore_metas($post->ID, $new_post_id);
$document->desc_array[$target] = $new_post_id;
$document->save();
if (class_exists('PLL_Share_Post_Slug', true)) {
wp_update_post(array('ID' => $new_post_id ,'post_name' => $slug));
}
}
}
self::$copying_post = false;
}
public function copy_term($term, $target, $taxonomy) {
self::$copying_term = true;
$document = $this->get_group('term', $term->term_id);
$cp_lang = $this->pllm->get_language($target);
$cp_term = (array) $term;
//unset($cp_term['term_id']);
if (class_exists('PLL_Share_Term_Slug', true)) {
remove_action( 'create_term', array( PLL()->filters_term, 'save_term' ), 999, 3 );
remove_action( 'edit_term', array( PLL()->filters_term, 'save_term' ), 999, 3 );
remove_action( 'pre_post_update', array( PLL()->filters_term, 'pre_post_update' ));
remove_filter( 'pre_term_name', array( PLL()->filters_term, 'pre_term_name' ));
remove_filter( 'pre_term_slug', array( PLL()->filters_term, 'pre_term_slug' ), 10, 2);
add_action( 'pre_post_update', array( PLL()->share_term_slug, 'pre_post_update' ) );
add_filter( 'pre_term_name', array( PLL()->share_term_slug, 'pre_term_name' ) );
add_filter( 'pre_term_slug', array( PLL()->share_term_slug, 'pre_term_slug' ), 10, 2 );
add_action( 'create_term', array( PLL()->share_term_slug, 'save_term' ), 1, 3 );
add_action( 'edit_term', array( PLL()->share_term_slug, 'save_term' ), 1, 3 );
$_POST['term_lang_choice'] = $cp_lang->slug;
}
else {
if (isset($cp_term['slug']) && term_exists($cp_term['slug'])) {
$cp_term['slug'] .= '-' . $cp_lang->slug;
}
}
$new_term = wp_insert_term($cp_term['name'], $taxonomy, $cp_term);
if (!is_wp_error($new_term)) {
PLL()->model->term->set_language($new_term['term_id'], $cp_lang);
wp_set_object_terms($new_term['term_id'], $document->term_id, 'term_translations');
$document->desc_array[$target] = $new_term['term_id'];
$document->save();
}
self::$copying_term = false;
}
/*
* uploads a new post to Lingotek TMS
*
* @since 0.1
*
* @param int $post_id
*/
public function upload_post($post_id) {
$post = get_post($post_id);
$language = PLL()->model->post->get_language($post_id);
if (empty($post) || empty($language))
return;
$profile = self::get_profile($post->post_type, $language, $post_id);
/**
* Customized workflows have the option to do any sort of pre-processing before a document is uploaded to lingotek.
*/
$document = $this->get_group('post', $post_id);
if ($document) {
$document->pre_upload_to_lingotek($post_id, $post->post_type, $language, 'post');
}
if ('disabled' == $profile['profile'])
return;
$client = new Lingotek_API();
$external_url = add_query_arg(array('lingotek' => 1, 'document_id' => '{document_id}', 'locale' => '{locale}', 'type' => 'get'), site_url());
$params = array(
'title' => $post->post_title,
'content' => Lingotek_Group_Post::get_content($post),
'locale_code' => $language->lingotek_locale,
'project_id' => self::get_profile_option('project_id', $post->post_type, $language, false, $post_id),
'workflow_id' => self::get_profile_option('workflow_id', $post->post_type, $language, false, $post_id),
'external_url' => $external_url,
);
foreach (array('author_email', 'author_name','division','unit','campaign_id','channel','contact_email','contact_name','description','domain','style_id','purchase_order','reference_url','region','require_review') as $key) {
if (isset($profile[$key]) )
$params[$key] = $profile[$key];
}
$filter_ids = array();
if (self::get_profile_option('primary_filter_id', $post->post_type, $language, false, $post_id)) {
$filter_ids['fprm_id'] = self::get_profile_option('primary_filter_id',$post->post_type, $language, false, $post_id);
}
if (self::get_profile_option('secondary_filter_id', $post->post_type, $language, false, $post_id)) {
$filter_ids['fprm_subfilter_id'] = self::get_profile_option('secondary_filter_id',$post->post_type, $language, false, $post_id);
}
$params = array_merge($params, $filter_ids);
if (($document = $this->get_group('post', $post_id)) && 'edited' == $document->status) {
$document->patch($post->post_title, $post, $external_url, $filter_ids);
}
elseif (!Lingotek_Group::$creating_translation && !self::$copying_post) {
$document_id = $client->upload_document($params, $post->ID);
if ($document_id) {
Lingotek_Group_Post::create($post->ID , $language, $document_id);
// If a translation profile has targets set to copy then copy them
$targets_to_copy = $this->targets_to_be_copied($profile);
$upload = self::get_profile_option('upload', $post->post_type, $language, false, $post_id);
if (!empty($targets_to_copy) && $upload == 'automatic') {
foreach ($targets_to_copy as $target) {
$this->copy_post($post, $target);
}
}
}
}
}
/*
* uploads a new term to Lingotek TMS
*
* @since 0.2
*
* @param int $term_id
* @param string $taxonomy
*/
public function upload_term($term_id, $taxonomy) {
$term = get_term($term_id, $taxonomy);
$language = PLL()->model->term->get_language($term_id);
if (empty($term) || empty($language))
return;
$profile = self::get_profile($taxonomy, $language);
if ('disabled' == $profile['profile'])
return;
/**
* Customized workflows have the option to do any sort of pre-processing before a document is uploaded to lingotek.
*/
$document = $this->get_group('term', $term_id);
if ($document) {
$document->pre_upload_to_lingotek($term_id, $taxonomy, $language, 'term');
}
$client = new Lingotek_API();
$params = array(
'title' => $term->name,
'content' => Lingotek_Group_Term::get_content($term),
'locale_code' => $language->lingotek_locale,
'project_id' => self::get_profile_option('project_id', $taxonomy, $language),
'workflow_id' => self::get_profile_option('workflow_id', $taxonomy, $language)
);
$filter_ids = array();
if (self::get_profile_option('primary_filter_id', $taxonomy, $language)) {
$filter_ids['fprm_id'] = self::get_profile_option('primary_filter_id', $taxonomy, $language);
}
if (self::get_profile_option('secondary_filter_id', $taxonomy, $language)) {
$filter_ids['fprm_subfilter_id'] = self::get_profile_option('secondary_filter_id', $taxonomy, $language);
}
$params = array_merge($params, $filter_ids);
if (($document = $this->get_group('term', $term_id)) && 'edited' == $document->status) {
$document->patch($term->name, $term, '', $filter_ids);
}
elseif (!Lingotek_Group::$creating_translation && !self::$copying_term) {
$document_id = $client->upload_document($params, $term_id);
if ($document_id) {
Lingotek_Group_Term::create($term_id, $taxonomy , $language, $document_id);
// If a translation profile has targets set to copy then copy them
$targets_to_copy = $this->targets_to_be_copied($profile);
if (!empty($targets_to_copy) && $upload == 'automatic') {
foreach ($targets_to_copy as $target) {
$this->copy_term($term, $target, $taxonomy);
}
}
}
}
}
/*
* uploads a strings group to Lingotek TMS
*
* @since 0.2
*
* @param string $group
*/
public function upload_strings($group) {
$language = $this->pllm->get_language($this->pllm->options['default_lang']);
$profile = self::get_profile('string', $language);
if ('disabled' == $profile['profile'])
return;
if (is_numeric($group)) {
$strings = self::get_strings();
$group = $strings[$group]['context'];
}
// check that we have a valid string group
if (!in_array($group, wp_list_pluck(self::get_strings(), 'context')))
return;
$client = new Lingotek_API();
$params = array(
'title' => $group,
'content' => Lingotek_Group_String::get_content($group),
'locale_code' => $language->lingotek_locale,
'project_id' => self::get_profile_option('project_id', 'string', $language),
'workflow_id' => self::get_profile_option('workflow_id', 'string', $language)
);
$filter_ids = array();
if (self::get_profile_option('primary_filter_id', 'string', $language)) {
$filter_ids['fprm_id'] = self::get_profile_option('primary_filter_id', 'string', $language);
}
if (self::get_profile_option('secondary_filter_id', 'string', $language)) {
$filter_ids['fprm_subfilter_id'] = self::get_profile_option('secondary_filter_id', 'string', $language);
}
$params = array_merge($params, $filter_ids);
if (($document = $this->get_group('string', $group)) && 'edited' == $document->status) {
$document->patch($group);
}
else {
$document_id = $client->upload_document($params, $group);
if ($document_id) {
Lingotek_Group_String::create($group, $language, $document_id);
}
}
}
/*
* checks if the document can be upload to Lingotek
*
* @since 0.1
*
* @param string $type either 'post' or 'term'
* @param int $object_id post id or term id
* @return bool
*/
// FIXME should I check for disabled profile here?
public function can_upload($type, $object_id) {
$document = $this->get_group($type, $object_id);
switch ($type) {
case 'string':
/*
$profile = self::get_profile('string', $this->pllm->get_language($this->pllm->options['default_lang']));
if ('disabled' == $profile['profile'])
return false;
*/
if (empty($document))
return true;
elseif ($document->md5 != md5(Lingotek_Group_String::get_content($object_id))) { // check if source strings have not been modified
$document->source_edited();
return true;
}
return false;
case 'post':
$language = PLL()->model->post->get_language($object_id);
return !empty($language) && (empty($document) ||
(isset($document) && 'edited' == $document->status && $document->source == $object_id));
case 'term':
// first check that a language is associated to the object
$language = PLL()->model->term->get_language($object_id);
// FIXME how to get profile to check if disabled?
return !empty($language) && (empty($document) ||
(empty($document->translations) && empty($document->source)) || // specific for terms as document is never empty
(isset($document) && 'edited' == $document->status && $document->source == $object_id));
}
}
/*
* deletes a post
*
* @since 0.1
*
* @param int $object_id post id
*/
public function delete_post($object_id) {
if ($document = $this->get_group('post', $object_id)) {
$client = new Lingotek_API();
$lingotek_prefs = Lingotek_Model::get_prefs();
if ($document->source == $object_id) {
$client->delete_document($document->document_id, $object_id);
}
else {
PLL()->model->post->delete_translation($object_id);
$lang = PLL()->model->post->get_language($object_id);
if ($lingotek_prefs['delete_document_from_tms']) {
$client->delete_translation($document->document_id, $lang->lingotek_locale, $object_id);
}
}
}
}
/*
* deletes a term
*
* @since 0.2
*
* @param int $object_id term id
*/
public function delete_term($object_id) {
if ($document = $this->get_group('term', $object_id)) {
$client = new Lingotek_API();
if ($document->source == $object_id) {
$client->delete_document($document->document_id, $object_id);
}
else {
$lang = PLL()->model->term->get_language($object_id);
PLL()->model->term->delete_language($object_id);
PLL()->model->term->delete_translation($object_id);
$client->delete_translation($document->document_id, $lang->lingotek_locale, $object_id);
}
}
}
/*
* counts the number of targets per language
*
* @since 0.2
*
* @param array $groups array of serialized 'post_translations' or 'term_translations' description
* @return array number of targets per language
*/
protected function get_target_count($groups) {
$targets = array_fill_keys($this->pllm->get_languages_list(array('fields' => 'slug')), 0);
foreach ($groups as $group) {
$group = unserialize($group);
if (isset($group['lingotek']['translations'])) {
foreach ($group['lingotek']['translations'] as $locale => $status) {
if ('current' == $status && $language = $this->pllm->get_language($locale))
$targets[$language->slug]++;
}
}
}
return $targets;
}
/*
* counts the number of sources and targets per language for a certain post type
*
* @since 0.2
*
* @param string $post_type
* @return array
*/
public function count_posts($post_type) {
global $wpdb;
static $r = array();
if (!empty($r[$post_type]))
return $r[$post_type];
if (!post_type_exists($post_type))
return;
// gets all translations groups for the post type
$groups = $wpdb->get_col($wpdb->prepare("
SELECT DISTINCT tt.description FROM $wpdb->term_taxonomy AS tt
INNER JOIN $wpdb->term_relationships AS tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
INNER JOIN $wpdb->posts AS p ON p.ID = tr.object_id
WHERE tt.taxonomy = %s
AND p.post_type = %s
AND p.post_status NOT IN ('trash', 'auto-draft')",
'post_translations', $post_type
));
$targets = $this->get_target_count($groups);
$group_ids = array();
$disabled = array();
foreach ($this->pllm->get_languages_list() as $language) {
// counts all the posts in one language
$n = $wpdb->get_var($wpdb->prepare("
SELECT COUNT(*) FROM $wpdb->term_relationships AS tr
INNER JOIN $wpdb->posts AS p ON p.ID = tr.object_id
WHERE tr.term_taxonomy_id = %d
AND p.post_type = %s
AND p.post_status NOT IN ('trash', 'auto-draft')",
$language->term_taxonomy_id, $post_type
));
$objects = $wpdb->get_col($wpdb->prepare("
SELECT object_id FROM $wpdb->term_relationships AS tr
INNER JOIN $wpdb->posts AS p ON p.ID = tr.object_id
WHERE tr.term_taxonomy_id = %d
AND p.post_type = %s
AND p.post_status NOT IN ('trash', 'auto-draft')",
$language->term_taxonomy_id, $post_type
));
foreach ($groups as $group) {
$group = unserialize($group);
if (array_key_exists($language->slug, $group)) {
$group_ids[] = $group[$language->slug];
}
}
$count = 0;
foreach ($objects as $object) {
$id = $object;
if (!in_array($id, $group_ids)) {
$profile = self::get_profile($post_type, $language, $id);
if ($profile['profile'] == 'disabled' && in_array($id, $objects)) {
$count += 1;
}
}
}
$disabled[$language->slug] = $count;
// if a post is not a target, then it is source
$sources[$language->slug] = $n - $targets[$language->slug];
// $sources[$language->slug] -= $disabled[$language->slug];
}
// untranslated posts have no associated translation group in DB
// so let's count them indirectly
// counts the number of translated posts
$n_translated = $wpdb->get_var($wpdb->prepare("
SELECT COUNT(*) FROM $wpdb->term_relationships AS tr
INNER JOIN $wpdb->posts AS p ON p.ID = tr.object_id
INNER JOIN $wpdb->term_taxonomy AS tt ON tt.term_taxonomy_id = tr.term_taxonomy_id
WHERE tt.taxonomy = %s
AND p.post_type = %s
AND p.post_status NOT IN ('trash', 'auto-draft')",
'post_translations', $post_type
));
// untranslated = total - translated
// total of posts translations groups = untranslated + number of translation groups stored in DB
$count_posts = (array) wp_count_posts($post_type);
unset($count_posts['trash'], $count_posts['auto-draft']); // don't count trash and auto-draft
$total = array_sum($count_posts) - $n_translated + count($groups);
return $r[$post_type] = compact('sources', 'targets', 'total');
}
/*
* counts the number of sources and targets per language for a certain taxonomy
*
* @since 0.2
*
* @param string $taxonomy
* @return array
*/
public function count_terms($taxonomy) {
global $wpdb;
static $r = array();
if (!empty($r[$taxonomy]))
return $r[$taxonomy];
if (!taxonomy_exists($taxonomy))
return;
// gets all translations groups for the taxonomy
$groups = $wpdb->get_col($wpdb->prepare("
SELECT DISTINCT tt1.description FROM $wpdb->term_taxonomy AS tt1
INNER JOIN $wpdb->term_relationships AS tr ON tt1.term_taxonomy_id = tr.term_taxonomy_id
INNER JOIN $wpdb->term_taxonomy AS tt2 ON tt2.term_id = tr.object_id
WHERE tt1.taxonomy = %s
AND tt2.taxonomy = %s",
'term_translations', $taxonomy
));
$targets = $this->get_target_count($groups);
$group_ids = array();
$disabled = array();
foreach ($this->pllm->get_languages_list() as $language) {
// counts all the terms in one language
$n = $wpdb->get_var($wpdb->prepare("
SELECT COUNT(*) FROM $wpdb->term_relationships AS tr
INNER JOIN $wpdb->term_taxonomy AS tt ON tt.term_id = tr.object_id
WHERE tr.term_taxonomy_id = %d
AND tt.taxonomy = %s",
$language->tl_term_taxonomy_id, $taxonomy
));
$objects = $wpdb->get_col($wpdb->prepare("
SELECT object_id FROM $wpdb->term_relationships AS tr
INNER JOIN $wpdb->term_taxonomy AS tt ON tt.term_id = tr.object_id
WHERE tr.term_taxonomy_id = %d
AND tt.taxonomy = %s",
$language->tl_term_taxonomy_id, $taxonomy
));
$count = 0;
foreach ($groups as $group) {
$group = unserialize($group);
if (array_key_exists($language->slug, $group)) {
$group_ids[] = $group[$language->slug];
$profile = self::get_profile($taxonomy, $language, $group[$language->slug]);
if ($profile['profile'] == 'disabled' && !isset($group['lingotek'])) {
$count += 1;
}
}
}
$disabled[$language->slug] = $count;
// if a term is not a target, then it is a source
$sources[$language->slug] = $n - $targets[$language->slug];
// $sources[$language->slug] -= $disabled[$language->slug];
}
$total = count($groups);
// default categories are created by Polylang in all languages
// don't count them as sources if they are not associated to the TMS
if ('category' === $taxonomy) {
$term_id = get_option('default_category');
$group = $this->get_group('term', $term_id);
foreach($this->pllm->get_languages_list() as $language) {
if (empty($group->source) || ($group->get_source_language()->slug != $language->slug && empty($group->translations[$language->locale]))) {
if ($language->slug != $this->pllm->options['default_lang']) {
$sources[$language->slug]--;
}
}
}
// Remove category targets from being counted until they are downloaded. Fixed target categories being counted as source languages.
foreach ($groups as $group) {
$group = unserialize($group);
if (isset($group['lingotek']['translations'])) {
foreach ($group['lingotek']['translations'] as $locale => $status) {
if (('pending' == $status || 'ready' == $status) && $language = $this->pllm->get_language($locale)) {
if ($sources[$language->slug] > 0) {
$sources[$language->slug]--;
}
}
}
}
}
if (count($sources) == 1 && $total != $sources[$this->pllm->options['default_lang']]) {
$total = $sources[$this->pllm->options['default_lang']];
}
}
// $total -= array_sum($disabled);
return $r[$taxonomy] = compact('sources', 'targets', 'total');
}
}