/**
  * Creates new items from the "Bulk Translate" list.
  *
  * @since 2.11
  *
  * @param	array	$item_content	NULL, to indicate no handler.
  * @param	string	$bulk_action	the requested action.
  * @param	integer	$post_id		the affected attachment.
  *
  * @return	object	updated $item_content. NULL if no handler, otherwise
  *					( 'message' => error or status message(s), 'body' => '' )
  */
 public static function mla_list_table_custom_bulk_action($item_content, $bulk_action, $post_id)
 {
     global $polylang;
     MLA::mla_debug_add(__LINE__ . " MLA_Polylang::mla_list_table_bulk_action_item_request( {$bulk_action}, {$post_id} )", MLA::MLA_DEBUG_CATEGORY_LANGUAGE);
     if ('pll-translate' == $bulk_action) {
         $translations = array();
         if (isset($_REQUEST['bulk_tr_languages'])) {
             $bulk_tr_languages = $_REQUEST['bulk_tr_languages'];
             // Expand All Languages selection
             if (isset($bulk_tr_languages['all'])) {
                 foreach ($polylang->model->get_languages_list() as $language) {
                     $bulk_tr_languages[$language->slug] = 'translate';
                 }
                 unset($bulk_tr_languages['all']);
             }
             // Process language selection(s)
             foreach ($bulk_tr_languages as $language => $action) {
                 $new_id = MLA_Polylang::_get_translation($post_id, $language);
                 $translations[] = $new_id;
             }
         }
         // Clear all the "Filter-by" parameters
         if (isset($_REQUEST['bulk_tr_options']['clear_filters'])) {
             MLA::mla_clear_filter_by();
         }
         if (empty($translations)) {
             $item_content = array('message' => "Item {$post_id}, no translations.");
         } else {
             $_REQUEST['heading_suffix'] = __('Bulk Translations', 'media-library-assistant');
             MLA_Polylang::$bulk_action_includes = array_merge(MLA_Polylang::$bulk_action_includes, $translations);
             $translations = implode(',', $translations);
             $item_content = array('message' => "Item {$post_id}, translation(s): {$translations}.");
         }
     }
     return $item_content;
 }
 /**
  * Filter the $tax_input array to a specific language
  *
  * @since 2.11
  * @uses MLA_WPML::$tax_input
  * @uses MLA_WPML::$existing_terms
  *
  * @param	integer	$post_id ID of the post to be updated
  * @param	string	$post_language explicit language_code; optional
  *
  * @return	array	language-specific $tax_inputs
  */
 private static function _apply_tax_input($post_id, $post_language = NULL)
 {
     global $sitepress;
     if (NULL == $post_language) {
         if (isset(self::$existing_terms['element_id']) && $post_id == self::$existing_terms['element_id']) {
             $post_language = self::$existing_terms['language_code'];
         } else {
             $post_language = $sitepress->get_element_language_details($post_id, 'post_attachment');
             $post_language = $post_language->language_code;
         }
     }
     MLA::mla_debug_add("MLA_WPML::_apply_tax_input( {$post_id} ) \$post_language = " . var_export($post_language, true), MLA::MLA_DEBUG_CATEGORY_AJAX);
     MLA::mla_debug_add("MLA_WPML::_apply_tax_input( {$post_id} ) self::\$tax_input[ \$post_language ] = " . var_export(self::$tax_input[$post_language], true), MLA::MLA_DEBUG_CATEGORY_AJAX);
     return self::$tax_input[$post_language];
 }
 /**
  * Retrieve the terms in one or more taxonomies.
  *
  * Alternative to WordPress /wp-includes/taxonomy.php function get_terms() that provides
  * an accurate count of attachments associated with each term.
  *
  * taxonomy - string containing one or more (comma-delimited) taxonomy names
  * or an array of taxonomy names. Default 'post_tag'.
  *
  * post_mime_type - MIME type(s) of the items to include in the term-specific counts. Default 'all'.
  *
  * post_type - The post type(s) of the items to include in the term-specific counts.
  * The default is "attachment". 
  *
  * post_status - The post status value(s) of the items to include in the term-specific counts.
  * The default is "inherit".
  *
  * ids - A comma-separated list of attachment ID values for an item-specific cloud.
  *
  * include - An array, comma- or space-delimited string of term ids to include
  * in the return array.
  *
  * exclude - An array, comma- or space-delimited string of term ids to exclude
  * from the return array. If 'include' is non-empty, 'exclude' is ignored.
  *
  * parent - term_id of the terms' immediate parent; 0 for top-level terms.
  *
  * minimum - minimum number of attachments a term must have to be included. Default 0.
  *
  * no_count - 'true', 'false' (default) to suppress term-specific attachment-counting process.
  *
  * number - maximum number of term objects to return. Terms are ordered by count,
  * descending and then by term_id before this value is applied. Default 0.
  *
  * orderby - 'count', 'id', 'name' (default), 'none', 'random', 'slug'
  *
  * order - 'ASC' (default), 'DESC'
  *
  * no_orderby - 'true', 'false' (default) to suppress ALL sorting clauses else false.
  *
  * preserve_case - 'true', 'false' (default) to make orderby case-sensitive.
  *
  * pad_counts - 'true', 'false' (default) to to include the count of all children in their parents' count.
  *
  * limit - final number of term objects to return, for pagination. Default 0.
  *
  * offset - number of term objects to skip, for pagination. Default 0.
  *
  * @since 1.60
  *
  * @param	array	taxonomies to search and query parameters
  *
  * @return	array	array of term objects, empty if none found
  */
 public static function mla_get_terms($attr)
 {
     global $wpdb;
     /*
      * Make sure $attr is an array, even if it's empty
      */
     if (empty($attr)) {
         $attr = array();
     } elseif (is_string($attr)) {
         $attr = shortcode_parse_atts($attr);
     }
     /*
      * Merge input arguments with defaults
      */
     $attr = apply_filters('mla_get_terms_query_attributes', $attr);
     $arguments = shortcode_atts(self::$mla_get_terms_parameters, $attr);
     $arguments = apply_filters('mla_get_terms_query_arguments', $arguments);
     /*
      * Build an array of individual clauses that can be filtered
      */
     $clauses = array('fields' => '', 'join' => '', 'where' => '', 'order' => '', 'orderby' => '', 'limits' => '');
     /*
      * If we're not counting attachments per term, strip
      * post fields out of list and adjust the orderby  value
      */
     if ($no_count = 'true' == (string) $arguments['no_count']) {
         $field_array = explode(',', $arguments['fields']);
         foreach ($field_array as $index => $field) {
             if (false !== strpos($field, 'p.')) {
                 unset($field_array[$index]);
             }
         }
         $arguments['fields'] = implode(',', $field_array);
         $arguments['minimum'] = 0;
         $arguments['post_mime_type'] = 'all';
         if ('count' == strtolower($arguments['orderby'])) {
             $arguments['orderby'] = 'none';
         }
     }
     $clauses['fields'] = $arguments['fields'];
     $clause = array('INNER JOIN `' . $wpdb->term_taxonomy . '` AS tt ON t.term_id = tt.term_id');
     $clause_parameters = array();
     if (!$no_count) {
         $clause[] = 'LEFT JOIN `' . $wpdb->term_relationships . '` AS tr ON tt.term_taxonomy_id = tr.term_taxonomy_id';
         $clause[] = 'LEFT JOIN `' . $wpdb->posts . '` AS p ON tr.object_id = p.ID';
         /*
          * Add type and status constraints
          */
         if (is_array($arguments['post_type'])) {
             $post_types = $arguments['post_type'];
         } else {
             $post_types = array($arguments['post_type']);
         }
         $placeholders = array();
         foreach ($post_types as $post_type) {
             $placeholders[] = '%s';
             $clause_parameters[] = $post_type;
         }
         $clause[] = 'AND p.post_type IN (' . join(',', $placeholders) . ')';
         if (is_array($arguments['post_status'])) {
             $post_stati = $arguments['post_status'];
         } else {
             $post_stati = array($arguments['post_status']);
         }
         $placeholders = array();
         foreach ($post_stati as $post_status) {
             if ('private' != $post_status || is_user_logged_in()) {
                 $placeholders[] = '%s';
                 $clause_parameters[] = $post_status;
             }
         }
         $clause[] = 'AND p.post_status IN (' . join(',', $placeholders) . ')';
     }
     $clause = join(' ', $clause);
     $clauses['join'] = $wpdb->prepare($clause, $clause_parameters);
     /*
      * Start WHERE clause with a taxonomy constraint
      */
     if (is_array($arguments['taxonomy'])) {
         $taxonomies = $arguments['taxonomy'];
     } else {
         $taxonomies = array($arguments['taxonomy']);
     }
     foreach ($taxonomies as $taxonomy) {
         if (!taxonomy_exists($taxonomy)) {
             $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy', 'media-library-assistant'), $taxonomy);
             return $error;
         }
     }
     $clause_parameters = array();
     $placeholders = array();
     foreach ($taxonomies as $taxonomy) {
         $placeholders[] = '%s';
         $clause_parameters[] = $taxonomy;
     }
     $clause = array('tt.taxonomy IN (' . join(',', $placeholders) . ')');
     /*
      * The "ids" parameter can build an item-specific cloud.
      * Compile a list of all the terms assigned to the items.
      */
     if (!empty($arguments['ids']) && empty($arguments['include'])) {
         $ids = wp_parse_id_list($arguments['ids']);
         $placeholders = implode("','", $ids);
         $clause[] = "AND p.ID IN ( '{$placeholders}' )";
         $includes = array();
         foreach ($ids as $id) {
             foreach ($taxonomies as $taxonomy) {
                 $terms = get_the_terms($id, $taxonomy);
                 if (is_array($terms)) {
                     foreach ($terms as $term) {
                         $includes[$term->term_id] = $term->term_id;
                     }
                     // terms
                 }
             }
             // taxonomies
         }
         // ids
         /*
          * If there are no terms we want an empty cloud
          */
         if (empty($includes)) {
             $arguments['include'] = (string) 0x7fffffff;
         } else {
             ksort($includes);
             $arguments['include'] = implode(',', $includes);
         }
     }
     /*
      * Add include/exclude and parent constraints to WHERE cluse
      */
     if (!empty($arguments['include'])) {
         $placeholders = implode("','", wp_parse_id_list($arguments['include']));
         $clause[] = "AND t.term_id IN ( '{$placeholders}' )";
     } elseif (!empty($arguments['exclude'])) {
         $placeholders = implode("','", wp_parse_id_list($arguments['exclude']));
         $clause[] = "AND t.term_id NOT IN ( '{$placeholders}' )";
     }
     if ('' !== $arguments['parent']) {
         $parent = (int) $arguments['parent'];
         $clause[] = "AND tt.parent = '{$parent}'";
     }
     if ('all' !== strtolower($arguments['post_mime_type'])) {
         $where = str_replace('%', '%%', wp_post_mime_type_where($arguments['post_mime_type'], 'p'));
         if (0 == absint($arguments['minimum'])) {
             $clause[] = ' AND ( p.post_mime_type IS NULL OR ' . substr($where, 6);
         } else {
             $clause[] = $where;
         }
     }
     $clause = join(' ', $clause);
     $clauses['where'] = $wpdb->prepare($clause, $clause_parameters);
     /*
      * For the inner/initial query, always select the most popular terms
      */
     if ($arguments['no_orderby']) {
         $arguments['orderby'] = 'count';
         $arguments['order'] = 'DESC';
     }
     /*
      * Add sort order
      */
     $orderby = strtolower($arguments['orderby']);
     $order = strtoupper($arguments['order']);
     if ('DESC' != $order) {
         $order = 'ASC';
     }
     $clauses['order'] = $order;
     $clauses['orderby'] = "ORDER BY {$orderby}";
     /*
      * Count, Descending, is the default order so no further work
      * is needed unless a different order is specified
      */
     if ('count' != $orderby || 'DESC' != $order) {
         if ('true' == strtolower($arguments['preserve_case'])) {
             $binary_keys = array('name', 'slug');
         } else {
             $binary_keys = array();
         }
         $allowed_keys = array('empty_orderby_default' => 'name', 'count' => 'count', 'id' => 'term_id', 'name' => 'name', 'random' => 'RAND()', 'slug' => 'slug');
         $clause = 'ORDER BY ' . self::_validate_sql_orderby($arguments, '', $allowed_keys, $binary_keys);
         $clauses['orderby'] = substr($clause, 0, strrpos($clause, ' ' . $order));
     }
     // add ORDER BY
     /*
      * Add pagination
      */
     $clauses['limits'] = '';
     $offset = absint($arguments['offset']);
     $limit = absint($arguments['limit']);
     if (0 < $offset && 0 < $limit) {
         $clauses['limits'] = "LIMIT {$offset}, {$limit}";
     } elseif (0 < $limit) {
         $clauses['limits'] = "LIMIT {$limit}";
     } elseif (0 < $offset) {
         $clause_parameters = 0x7fffffff;
         $clauses['limits'] = "LIMIT {$offset}, {$clause_parameters}";
     }
     $clauses = apply_filters('mla_get_terms_clauses', $clauses);
     /*
      * Build the final query
      */
     $query = array('SELECT');
     $query[] = $clauses['fields'];
     $query[] = 'FROM `' . $wpdb->terms . '` AS t';
     $query[] = $clauses['join'];
     $query[] = 'WHERE (';
     $query[] = $clauses['where'];
     $query[] = ') GROUP BY tt.term_taxonomy_id';
     $clause_parameters = absint($arguments['minimum']);
     if (0 < $clause_parameters) {
         $query[] = "HAVING count >= {$clause_parameters}";
     }
     /*
      * If specifically told to omit the ORDER BY clause or the COUNT,
      * supply a sort order for the initial/inner query only
      */
     if (!($arguments['no_orderby'] || $no_count)) {
         $query[] = 'ORDER BY count DESC, t.term_id ASC';
     }
     /*
      * Limit the total number of terms returned
      */
     $terms_limit = absint($arguments['number']);
     if (0 < $terms_limit) {
         $query[] = "LIMIT {$terms_limit}";
     }
     /*
      * $final_clauses, if present, require an SQL subquery
      */
     $final_clauses = array();
     if ('count' != $orderby || 'DESC' != $order) {
         $final_clauses[] = $clauses['orderby'];
         $final_clauses[] = $clauses['order'];
     }
     if ('' !== $clauses['limits']) {
         $final_clauses[] = $clauses['limits'];
     }
     /*
      * If we're limiting the final results, we need to get an accurate total count first
      */
     if (!$no_count && (0 < $offset || 0 < $limit)) {
         $count_query = 'SELECT COUNT(*) as count FROM (' . join(' ', $query) . ' ) as subQuery';
         $count = $wpdb->get_results($count_query);
         $found_rows = $count[0]->count;
     }
     if (!empty($final_clauses)) {
         if (!$no_count) {
             array_unshift($query, 'SELECT * FROM (');
             $query[] = ') AS subQuery';
         }
         $query = array_merge($query, $final_clauses);
     }
     $query = join(' ', $query);
     $tags = $wpdb->get_results($query);
     if (!isset($found_rows)) {
         $found_rows = $wpdb->num_rows;
     }
     if (self::$mla_debug) {
         MLA::mla_debug_add('<strong>' . __('mla_debug query arguments', 'media-library-assistant') . '</strong> = ' . var_export($arguments, true));
         MLA::mla_debug_add('<strong>' . __('mla_debug last_query', 'media-library-assistant') . '</strong> = ' . var_export($wpdb->last_query, true));
         MLA::mla_debug_add('<strong>' . __('mla_debug last_error', 'media-library-assistant') . '</strong> = ' . var_export($wpdb->last_error, true));
         MLA::mla_debug_add('<strong>' . __('mla_debug num_rows', 'media-library-assistant') . '</strong> = ' . var_export($wpdb->num_rows, true));
         MLA::mla_debug_add('<strong>' . __('mla_debug found_rows', 'media-library-assistant') . '</strong> = ' . var_export($found_rows, true));
     }
     if ('true' == strtolower(trim($arguments['pad_counts']))) {
         self::_pad_term_counts($tags, reset($taxonomies), $post_types, $post_stati);
     }
     $tags['found_rows'] = $found_rows;
     $tags = apply_filters('mla_get_terms_query_results', $tags);
     return $tags;
 }
 /**
  * Filters taxonomy updates by language for Bulk Edit during Add New Media
  * and the Media/Edit Media screen
  *
  * @since 2.11
  *
  * @param	integer	ID of the current post
  */
 public static function edit_attachment($post_id)
 {
     static $already_updating = 0;
     MLA::mla_debug_add(__LINE__ . " MLA_Polylang::edit_attachment( {$post_id} ) _REQUEST = " . var_export($_REQUEST, true), MLA::MLA_DEBUG_CATEGORY_LANGUAGE);
     /*
      * mla_update_single_item may call this action again
      */
     if ($already_updating == $post_id) {
         return;
     } else {
         $already_updating = $post_id;
     }
     /*
      * Check for Bulk Edit during Add New Media
      */
     if (!empty($_REQUEST['mlaAddNewBulkEditFormString'])) {
         if (!empty(self::$bulk_edit_request['tax_input'])) {
             $tax_inputs = self::$bulk_edit_request['tax_input'];
             if ('checked' == MLAOptions::mla_get_option('term_assignment', false, false, MLA_WPML::$mla_language_option_definitions)) {
                 self::_build_tax_input($post_id, $tax_inputs, self::$bulk_edit_request['tax_action']);
                 $tax_inputs = self::_apply_tax_input($post_id);
             }
         } else {
             $tax_inputs = NULL;
         }
         $updates = MLA::mla_prepare_bulk_edits($post_id, self::$bulk_edit_request, self::$bulk_edit_map);
         unset($updates['tax_input']);
         unset($updates['tax_action']);
         MLAData::mla_update_single_item($post_id, $updates, $tax_inputs);
         return;
     }
     // Upload New Media Bulk Edit
     /*
      * For the Bulk Edit action on the Media/Assistant screen, only synchronization is needed
      */
     if (!(isset($_REQUEST['bulk_action']) && 'bulk_edit' == $_REQUEST['bulk_action'])) {
         /*
          * This is the Media/Edit Media screen.
          * The category taxonomy (edit screens) is a special case because 
          * post_categories_meta_box() changes the input name
          */
         if (isset($_REQUEST['tax_input'])) {
             $tax_inputs = $_REQUEST['tax_input'];
         } else {
             $tax_inputs = array();
         }
         if (isset($_REQUEST['post_category'])) {
             $tax_inputs['category'] = $_REQUEST['post_category'];
         }
         if (isset($_REQUEST['tax_action'])) {
             $tax_actions = $_REQUEST['tax_action'];
         } else {
             $tax_actions = NULL;
         }
         if (!empty($tax_inputs) && 'checked' == MLAOptions::mla_get_option('term_assignment', false, false, MLA_WPML::$mla_language_option_definitions)) {
             self::_build_tax_input($post_id, $tax_inputs, $tax_actions);
             $tax_inputs = self::_apply_tax_input($post_id);
         }
         if (!empty($tax_inputs)) {
             MLAData::mla_update_single_item($post_id, array(), $tax_inputs);
         }
     }
     // Media/Edit Media screen, NOT Bulk Edit
 }
 /**
  * Get ALL markup templates from $mla_templates, including 'default'
  *
  * @since 0.80
  *
  * @return	array|null	name => value for all markup templates or null if no templates
  */
 public static function mla_get_markup_templates()
 {
     if (!is_array(self::$mla_option_templates)) {
         MLA::mla_debug_add('<strong>mla_debug mla_get_markup_templates()</strong> ' . __('no templates exist', 'media-library-assistant'));
         return null;
     }
     $templates = array();
     foreach (self::$mla_option_templates as $key => $value) {
         // Note order: -row-open must precede -open!
         $tail = strrpos($key, '-row-open-markup');
         if (!(false === $tail)) {
             $name = substr($key, 0, $tail);
             $templates[$name]['row-open'] = $value;
             continue;
         }
         $tail = strrpos($key, '-open-markup');
         if (!(false === $tail)) {
             $name = substr($key, 0, $tail);
             $templates[$name]['open'] = $value;
             continue;
         }
         $tail = strrpos($key, '-item-markup');
         if (!(false === $tail)) {
             $name = substr($key, 0, $tail);
             $templates[$name]['item'] = $value;
             continue;
         }
         $tail = strrpos($key, '-row-close-markup');
         if (!(false === $tail)) {
             $name = substr($key, 0, $tail);
             $templates[$name]['row-close'] = $value;
             continue;
         }
         $tail = strrpos($key, '-close-markup');
         if (!(false === $tail)) {
             $name = substr($key, 0, $tail);
             $templates[$name]['close'] = $value;
         }
     }
     // foreach
     return $templates;
 }
 /**
  * Log debug information
  *
  * @since 2.12
  *
  * @param	string	$message Error message.
  */
 private static function _mla_debug_add($message)
 {
     if (self::$mla_debug) {
         if (class_exists('MLA')) {
             MLA::mla_debug_add($message);
         } else {
             error_log($message, 0);
         }
     }
 }
 /**
  * Filters all clauses for shortcode queries, post caching plugins
  * 
  * This is for debug purposes only.
  * Defined as public because it's a filter.
  *
  * @since 1.80
  *
  * @param	array	query clauses before modification
  *
  * @return	array	query clauses after modification (none)
  */
 public static function mla_query_posts_clauses_request_filter($pieces)
 {
     /* translators: 1: DEBUG tag 2: SQL clauses */
     MLA::mla_debug_add(sprintf(_x('%1$s: mla_query_posts_clauses_request_filter = "%2$s".', 'error_log', 'media-library-assistant'), __('DEBUG', 'media-library-assistant'), var_export($pieces, true)));
     return $pieces;
 }