<?php
if ( ! defined( 'ABSPATH' ) ) exit();
/**
 * Base class to add row and bulk actions to posts, media and terms list
 * Bulk actions management is inspired by http://www.foxrunsoftware.net/articles/wordpress/add-custom-bulk-action/
 *
 * @since 0.2
 */
abstract class Lingotek_Actions {
	/**
	 * Polylang model.
	 *
	 * @var obj
	 */
	public $pllm;
	/**
	 * Lingotek model.
	 *
	 * @var object
	 */
	public $lgtm;
	/**
	 * Must be defined in child class: 'post' or 'term'.
	 *
	 * @var string
	 */
	public $type;
	/**
	 * Actions
	 *
	 * @var array
	 */
	public static $actions;
	/**
	 * Icons
	 *
	 * @var list
	 */
	public static $icons;
	/**
	 * Confirm_message
	 *
	 * @var string
	 */
	public static $confirm_message;

	/**
	 * Confirm_delete
	 *
	 * @var string
	 */
	public static $confirm_delete;

	/**
	 * Confirm_cancel
	 *
	 * @var string
	 */
	public static $confirm_cancel;

	/**
	 * Confirm_delete_translation
	 *
	 * @var string
	 */
	public static $confirm_delete_translation;

	/**
	 * Confirm_cancel_translation
	 *
	 * @var string
	 */
	public static $confirm_cancel_translation;

	/**
	 * Constructor
	 *
	 * @since 0.2
	 */
	public function __construct( $type ) {
		// confirm message.
		self::$confirm_message            = sprintf( ' onclick = "return confirm(\'%s\');"', __( 'You are about to overwrite existing translations. Are you sure?', 'lingotek-translation' ) );
		self::$confirm_delete             = sprintf( ' onclick = "return confirm(\'%s\');"', __( 'Content will be deleted from WordPress. The associated content in the TMS will be cancelled. Are you sure?', 'lingotek-translation' ) );
		self::$confirm_cancel             = sprintf( ' onclick = "return confirm(\'%s\');"', __( 'You are about to cancel existing translations in your Lingotek community. Are you sure?', 'lingotek-translation' ) );
		self::$confirm_delete_translation = sprintf( ' onclick = "return confirm(\'%s\');"', __( 'Translation will be deleted from WordPress. The associated translation in the TMS will be cancelled. Are you sure?', 'lingotek-translation' ) );
		self::$confirm_cancel_translation = sprintf( ' onclick = "return confirm(\'%s\');"', __( 'You are about to cancel this translation in your Lingotek community. Are you sure?', 'lingotek-translation' ) );
		// row actions.
		self::$actions = array(
			'upload'             => array(
				'action'      => __( 'Upload to Lingotek', 'lingotek-translation' ),
				'progress'    => __( 'Uploading...', 'lingotek-translation' ),
				'description' => __( 'Upload this item to Lingotek TMS', 'lingotek-translation' ),
				'per_locale'  => false,
			),

			'request'            => array(
				'action'      => __( 'Request translations', 'lingotek-translation' ),
				'progress'    => __( 'Requesting translations...', 'lingotek-translation' ),
				'description' => __( 'Request translations of this item to Lingotek TMS', 'lingotek-translation' ),
				'per_locale'  => false,
			),

			'status'             => array(
				'action'      => __( 'Update translations status', 'lingotek-translation' ),
				'progress'    => __( 'Updating translations status...', 'lingotek-translation' ),
				'description' => __( 'Update translations status of this item in Lingotek TMS', 'lingotek-translation' ),
				'per_locale'  => false,
			),

			'download'           => array(
				'action'      => __( 'Download translations', 'lingotek-translation' ),
				'progress'    => __( 'Downloading translations...', 'lingotek-translation' ),
				'description' => __( 'Download translations of this item from Lingotek TMS', 'lingotek-translation' ),
				'per_locale'  => false,
			),

			'cancel'             => array(
				'action'      => __( 'Cancel translations', 'lingotek-translation' ),
				'progress'    => __( 'Cancelling translations...', 'lingotek-translation' ),
				'description' => __( 'Cancel the translations of this item from Lingotek TMS', 'lingotek-translation' ),
				'per_locale'  => false,
			),

			'delete'             => array(
				'action'      => __( 'Delete translations', 'lingotek-translation' ),
				'progress'    => __( 'Deleting translations...', 'lingotek-translation' ),
				'description' => __( 'Delete the translations of this item from Lingotek TMS', 'lingotek-translation' ),
				'per_locale'  => false,
			),

			'cancel-translation' => array(
				'action'      => __( 'Cancel translation', 'lingotek-translation' ),
				'progress'    => __( 'Cancelling translation', 'lingotek-translation' ),
				'description' => __( 'Cancel this translation from Lingotek TMS', 'lingotek-translation' ),
				'per_locale'  => true,
			),

			'delete-translation' => array(
				'action'      => __( 'Delete translation', 'lingotek-translation' ),
				'progress'    => __( 'Deleting translation', 'lingotek-translation' ),
				'description' => __( 'Delete this translation from Lingotek TMS', 'lingotek-translation' ),
				'per_locale'  => false,
			),

			'lock-target' => array(
				'action'      => __( 'Lock/Unlock target', 'lingotek-translation' ),
				'progress'    => __( 'Locking/Unlocking translation', 'lingotek-translation' ),
				'description' => __( 'Lock/Unlock this target', 'lingotek-translation' ),
				'per_locale'  => true,
			),
		);

		// action icons
		self::$icons = array(
			'upload'      => array(
				'title' => __( 'Upload Now', 'lingotek-translation' ),
				'icon'  => 'upload',
			),

			'importing'   => array(
				'title' => __( 'Importing source', 'lingotek-translation' ),
				'icon'  => 'clock',
			),

			'failed'      => array(
				'title' => __( 'Upload Failed. Click to re-upload', 'lingotek-translation' ),
				'icon'  => 'no',
				'class' => 'lingotek-failed-color',
			),

			'uploaded'    => array(
				'title' => __( 'Source uploaded', 'lingotek-translation' ),
				'icon'  => 'yes',
			),

			'request'     => array(
				'title' => __( 'Request a translation', 'lingotek-translation' ),
				'icon'  => 'plus',
			),

			'pending'     => array(
				'title' => __( 'In Progress', 'lingotek-translation' ),
				'icon'  => 'clock',
			),

			'ready'       => array(
				'title' => __( 'Ready to download', 'lingotek-translation' ),
				'icon'  => 'download',
			),

			'interim'     => array(
				'title' => __( 'Interim Translation Downloaded', 'lingotek-translation' ),
				'icon'  => 'edit',
			),

			'ready-interim'     => array(
				'title' => __( 'Interim Translation Ready to be Downloaded', 'lingotek-translation' ),
				'icon'  => 'edit',
			),

			'current'     => array(
				'title' => __( 'Current', 'lingotek-translation' ),
				'icon'  => 'edit',
			),

			'not-current' => array(
				'title' => __( 'The target translation is no longer current as the source content has been updated', 'lingotek-translation' ),
				'icon'  => 'edit',
			),

			'error'       => array(
				'title' => __( 'There was an error contacting Lingotek', 'lingotek-translation' ),
				'icon'  => 'no',
			),

			'copy'        => array(
				'title' => __( 'Copy source language', 'lingotek-translation' ),
				'icon'  => 'welcome-add-page',
			),

			'cancelled'   => array(
				'title' => __( 'Translation has been cancelled', 'lingotek-translation' ),
				'icon'  => 'warning',
			),

			'deleted'     => array(
				'title' => __( 'Translation has been deleted', 'lingotek-translation' ),
				'icon'  => 'remove',
			),

			'archived'    => array(
				'title' => __( 'Document has been archived', 'lingotek-translation' ),
				'icon'  => 'remove',
			),

			'locked'    => array(
				'title' => __( 'The target has been locked', 'lingotek-translation' ),
				'icon'  => 'lock',
			),
		);

		$this->type = $type;
		$this->pllm = $GLOBALS['polylang']->model;
		$this->lgtm = $GLOBALS['wp_lingotek']->model;

		add_action( 'admin_enqueue_scripts', array( &$this, 'admin_enqueue_scripts' ) );

		add_action( 'wp_ajax_estimate_cost', array( &$this, 'ajax_estimate_cost' ) );
		add_action( 'wp_ajax_request_professional_translation', array( &$this, 'ajax_request_professional_translation' ) );
		add_action( 'wp_ajax_get_user_payment_information', array( &$this, 'ajax_get_user_payment_information' ) );
		add_action( 'wp_ajax_get_ltk_terms_and_conditions', array( &$this, 'ajax_get_ltk_terms_and_conditions' ) );

		foreach ( self::$actions as $action => $action_data ) {
			if ( strpos( $action, '-translation' ) && ! $action_data['per_locale'] ) {
				continue;
			}
			add_action( 'wp_ajax_lingotek_progress_' . $this->type . '_' . $action, array( &$this, 'ajax_' . str_replace( '-', '_', $action ?? '') ) );
		}
	}

	/**
	 * Generates a workbench link
	 *
	 * @since 0.1
	 *
	 * @param string $document_id
	 * @param string $locale Lingotek locale.
	 * @return string Workbench link.
	 */
	public static function workbench_link( $document_id, $locale ) {
		$document_id = ! empty( $document_id ) ? $document_id : '';
		$locale      = $locale ? Lingotek::map_to_lingotek_locale( $locale ) : '';
		return Lingotek_API::PRODUCTION_URL . "/workbench/document/$document_id/locale/$locale";
	}

	/**
	 * Generates a request target link
	 *
	 * @since 1.4.14
	 *
	 * @param string $document_id
	 * @param string $wp_locale WordPress locale.
	 * @return string Workbench link.
	 */
	public static function request_target_link( $document_id, $wp_locale ) {
		$link = wp_nonce_url(
			add_query_arg(
				array(
					'document_id' => $document_id,
					'locale'      => $wp_locale,
					'action'      => 'lingotek-request',
					'noheader'    => true,
				),
				defined( 'DOING_AJAX' ) && DOING_AJAX ? wp_get_referer() : wp_get_referer()
			),
			'lingotek-request'
		);
		return $link;
	}

	/**
	 * Outputs an action icon
	 *
	 * @since 0.2
	 *
	 * @param string $name
	 * @param string $link
	 * @param string $additional Parameters to add (js, target).
	 */
	public static function display_icon( $name, $link, $additional = '' ) {
		self::link_to_settings_if_not_connected( $link );
		if ( 'interim' === $name ) {
			return sprintf(
				'<a class="lingotek-interim-color dashicons dashicons-%s dashicons-%s-lingotek" title="%s" href="%s"%s></a>',
				self::$icons[ $name ]['icon'],
				self::$icons[ $name ]['icon'],
				self::$icons[ $name ]['title'],
				esc_url( $link ),
				$additional
			);
		} elseif ( 'ready-interim' === $name) {
			return sprintf(
				'<a class="lingotek-ready-interim-color dashicons dashicons-%s dashicons-%s-lingotek" title="%s" href="%s"%s></a>',
				self::$icons[ $name ]['icon'],
				self::$icons[ $name ]['icon'],
				self::$icons[ $name ]['title'],
				esc_url( $link ),
				$additional
			);
		}
		return sprintf(
			'<a class="lingotek-color dashicons dashicons-%s dashicons-%s-lingotek" title="%s" href="%s"%s></a>',
			self::$icons[ $name ]['icon'],
			self::$icons[ $name ]['icon'],
			self::$icons[ $name ]['title'],
			esc_url( $link ),
			$additional
		);
	}

	/**
	 * Outputs an API error icon
	 *
	 * @since 1.2
	 *
	 * @param string $name
	 * @param string $api_error
	 * @param string $additional Parameters to add (js, target).
	 */
	public static function display_error_icon( $name, $api_error, $additional = '' ) {
		return sprintf(
			'<span class="lingotek-error dashicons dashicons-%s" title="%s"></span>',
			esc_attr( self::$icons[ $name ]['icon'] ),
			esc_attr( self::$icons[ $name ]['title'] )  . "\n" . esc_html( $api_error ),
			esc_html( $additional )
		);
	}

	/**
	 * Outputs an upload icon
	 *
	 * @since 0.2
	 *
	 * @param int|string $object_id
	 * @param bool       $confirm
	 */
	public function upload_icon( $object_id, $confirm = false ) {
		$link = $this->upload_url( $object_id );

		return self::display_icon( 'upload', $link, $confirm ? self::$confirm_message : '' );
	}

	/**
	 * Outputs a cancelled icon
	 *
	 * @since 1.4.13
	 *
	 * @param int|string $object_id
	 *   id of the object
	 * @param bool       $target
	 *   boolean to show if icon is for a target or source document
	 * @param bool       $confirm
	 *   boolean to enable the cancel confirm pop up
	 */
	public function cancelled_icon( $object_id, $target = false, $confirm = false ) {
		$action = $target ? 'edit' : 'lingotek-upload';
		$args   = array(
			$this->type => $object_id,
			'action'    => $action,
			'noheader'  => true,
		);
		if ( isset( $args['string'] ) ) {
			$args['string'] = rawurlencode( $args['string'] );
		}
		$link = wp_nonce_url( defined( 'DOING_AJAX' ) && DOING_AJAX ? add_query_arg( $args, wp_get_referer() ) : add_query_arg( $args ), $action );
		self::link_to_settings_if_not_connected( $link );
		return self::display_icon( 'cancelled', $link, $confirm ? self::$confirm_message : '' );
	}

	/**
	 * Outputs a copy icon
	 *
	 * @param int|string $object_id
	 * @param string     $target
	 * @param bool       $confirm
	 */
	public function copy_icon( $object_id, $target, $confirm = false ) {
		$args = array(
			$this->type => $object_id,
			'target'    => $target,
			'action'    => 'lingotek-copy',
			'noheader'  => true,
		);
		$link = wp_nonce_url( defined( 'DOING_AJAX' ) && DOING_AJAX ? add_query_arg( $args, wp_get_referer() ) : add_query_arg( $args ), 'lingotek-copy' );
		self::link_to_settings_if_not_connected( $link );
		return self::display_icon( 'copy', $link, $confirm ? self::$confirm_message : '' );
	}

	/**
	 * Outputs an importing icon
	 *
	 * @since 0.2
	 *
	 * @param object $document
	 */
	public static function importing_icon( $document ) {
		$args = array(
			'document_id' => $document->document_id,
			'action'      => 'lingotek-status',
			'noheader'    => true,
		);
		$link = wp_nonce_url( defined( 'DOING_AJAX' ) && DOING_AJAX ? add_query_arg( $args, wp_get_referer() ) : add_query_arg( $args ), 'lingotek-status' );
		self::link_to_settings_if_not_connected( $link );
		return self::display_icon( 'importing', $link );
	}

	/**
	 * Outputs icons for translations
	 *
	 * @since 0.2
	 *
	 * @param object $document
	 * @param object $language
	 */
	public static function translation_icon( $document, $language ) {
		if ( isset( $document->translations[ $language->locale ] ) ) {
			if ( 'ready' === $document->translations[ $language->locale ] ) {
				$link = self::request_target_link( $document->document_id, $language->locale );
				self::link_to_settings_if_not_connected( $link );
				return self::display_icon( $document->translations[ $language->locale ], $link );
			} elseif ( 'not-current' === $document->translations[ $language->locale ] ) {
				return '<div class="lingotek-color dashicons dashicons-no"></div>';
				// phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.Found,Squiz.PHP.DisallowMultipleAssignments.FoundInControlStructure
			} elseif ( 'current' !== $document->translations[ $language->locale ] && $custom_icon = $document->get_custom_in_progress_icon( $language ) ) {
				return $custom_icon;
			} else {
				$link         = self::workbench_link( $document->document_id, $language->lingotek_locale );
				$target_blank = ' target="_blank"';
				if ( 'deleted' === $document->translations[ $language->locale ] ) {
					$link         = self::request_target_link( $document->document_id, $language->locale );
					$target_blank = '';
				}
				self::link_to_settings_if_not_connected( $link );
				return self::display_icon( $document->translations[ $language->locale ], $link, $target_blank );
			}//end if
		} else {
			$link = self::request_target_link( $document->document_id, $language->locale );
			self::link_to_settings_if_not_connected( $link );
			return self::display_icon( 'request', $link );
		}//end if
	}

	/**
	 * Outputs icon for failed import callback
	 *
	 * @since 1.4.3
	 */
	public function failed_import_icon( $name, $object_id ) {
		$link = $this->upload_url( $object_id );
		return sprintf(
			'<a class="%s dashicons dashicons-%s" title="%s" href="%s"></a>',
			self::$icons[ $name ]['class'],
			self::$icons[ $name ]['icon'],
			self::$icons[ $name ]['title'],
			esc_url( $link )
		);
	}

	/**
	 * Creates an html action link
	 *
	 * @since 0.2
	 *
	 * @param array $args Parameters to add to the link.
	 * @param bool  $warning Whether to display an alert or not, optional, defaults to false.
	 * @return string
	 */
	protected function get_action_link( $args, $warning = false ) {
		$action           = $args['action'];
		$args['action']   = 'lingotek-' . $action;
		$args['noheader'] = true;
		if ( isset( $args['string'] ) ) {
			$args['string'] = rawurlencode( $args['string'] );
		}
		$link = wp_nonce_url( defined( 'DOING_AJAX' ) && DOING_AJAX ? add_query_arg( $args, wp_get_referer() ) : add_query_arg( $args ), 'lingotek-' . $action );
		self::link_to_settings_if_not_connected( $link );

		switch ( $action ) {
			case 'delete-translation':
				$message = self::$confirm_delete_translation;
				break;
			case 'delete':
				$message = self::$confirm_delete;
				break;
			case 'cancel-translation':
				$message = self::$confirm_cancel_translation;
				break;
			case 'cancel':
				$message = self::$confirm_cancel;
				break;
			default:
				$message = self::$confirm_message;
				break;
		}

		return sprintf(
			'<a class="lingotek-color" title="%s" href="%s"%s>%s</a>',
			self::$actions[ $action ]['description'],
			$link,
			empty( $warning ) ? '' : $message,
			self::$actions[ $action ]['action']
		);
	}

	private static function link_to_settings_if_not_connected( &$link ) {
		if ( ! get_option( 'lingotek_token' ) || ! get_option( 'lingotek_community' ) ) {
			$link = get_site_url( null, '/wp-admin/admin.php?page=lingotek-translation_settings' );
		}
	}

	/**
	 * Adds a row action link
	 *
	 * @since 0.2
	 *
	 * @param array $actions List of action links.
	 * @param mixed $id Object id.
	 * @return array
	 */
	protected function _row_actions( $actions, $id ) {
		// first check that a language is associated to this object
		if ( ! $this->get_language( $id ) ) {
			return $actions;
		}
		$document = $this->lgtm->get_group( $this->type, $id );

		$target_id = $id;
		if ( 'string' !== $this->type && isset( $document->desc_array['lingotek']['source'] ) ) {
			$id = $document->desc_array['lingotek']['source'];
		}

		if ( $this->lgtm->can_upload( $this->type, $id ) || ( isset( $document->source ) && 'string' !== $this->type && $this->lgtm->can_upload( $this->type, $document->source ) ) ) {
			if ( $document ) {
				$desc_array = $document->desc_array;
				unset( $desc_array['lingotek'] );
				$data = array(
					$this->type => $id,
					'action'    => 'upload',
				);
				if ( count( $desc_array ) >= 2 ) {
					$actions['lingotek-upload'] = $this->get_action_link( $data, true );
				} else {
					$actions['lingotek-upload'] = $this->get_action_link( $data );
				}

				/**
				* If a document has been changed but still has translations or is importing we still want to have the
				* update translation status option.
				*/
				if ( 'importing' === $document->status || $document->has_translation_status( 'pending' ) ) {
					$actions['lingotek-status'] = $this->get_action_link(
						array(
							'document_id' => $document->document_id,
							'action'      => 'status',
						)
					);
				}

				if ( $document->has_translation_status( 'ready' ) ) {
					$actions['lingotek-download'] = $this->get_action_link(
						array(
							'document_id' => $document->document_id,
							'action'      => 'download',
						)
					);
				}

				if ( $document->has_translation_status( 'ready-interim' ) ) {
					$actions['lingotek-status'] = $this->get_action_link(
						array(
							'document_id' => $document->document_id,
							'action'      => 'download',
						)
					);
				}

			} else {
				$actions['lingotek-upload'] = $this->get_action_link(
					array(
						$this->type => $id,
						'action'    => 'upload',
					)
				);
			}//end if
		} elseif ( isset( $document->translations ) ) {
			// translations to download ?
			if ( $document->has_translation_status( 'ready' ) ) {
				$actions['lingotek-download'] = $this->get_action_link(
					array(
						'document_id' => $document->document_id,
						'action'      => 'download',
					)
				);
			}

			if ( $document->has_translation_status( 'interim' ) ) {
				$actions['lingotek-status'] = $this->get_action_link(
					array(
						'document_id' => $document->document_id,
						'action'      => 'status',
					)
				);
			}

			if ( $document->has_translation_status( 'ready-interim' ) ) {
				$actions['lingotek-status'] = $this->get_action_link(
					array(
						'document_id' => $document->document_id,
						'action'      => 'download',
					)
				);
			}

			//need to request translations?
			$language    = $this->get_language( $document->source );
			$all_locales = array_flip( $this->pllm->get_languages_list( array( 'fields' => 'locale' ) ) );
			// In case a language has been deleted.
			if ( ! empty( $language ) ) {
				unset( $all_locales[ $language->locale ] );
			}
			$untranslated = array_diff_key( $all_locales, $document->translations );

			// remove disabled target language from untranslated languages list
			foreach ( $untranslated as $k => $v ) {
				if ( $document->is_disabled_target( $language, $this->pllm->get_language( $k ) ) ) {
					unset( $untranslated[ $k ] );
				}
			}

			if ( 'current' === $document->status && ! empty( $untranslated ) ) {
				$actions['lingotek-request'] = $this->get_action_link(
					array(
						'document_id' => $document->document_id,
						'action'      => 'request',
					)
				);
			}

			// offers to update translations status
			if ( 'importing' === $document->status || $document->has_translation_status( 'pending' ) ) {
				$actions['lingotek-status'] = $this->get_action_link(
					array(
						'document_id' => $document->document_id,
						'action'      => 'status',
					)
				);
			}
		} elseif ( empty( $document->source ) ) {
			$actions['lingotek-upload'] = $this->get_action_link(
				array(
					$this->type => $id,
					'action'    => 'upload',
				),
				true
			);
		}//end if

		$target_locale = $this->get_language( $target_id )->locale;
		// phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
		if ( $id == $target_id && isset( $document->source ) && 'cancelled' !== $document->status ) {
			$actions['lingotek-cancel'] = $this->get_action_link(
				array(
					'document_id' => $document->document_id,
					'action'      => 'cancel',
				),
				true
			);
			$actions['lingotek-delete'] = $this->get_action_link(
				array(
					'document_id' => $document->document_id,
					'action'      => 'delete',
				),
				true
			);
		} elseif ( isset( $document->source ) && isset( $document->translations[ $target_locale ] ) && 'cancelled' !== $document->translations[ $target_locale ] ) {
			$actions['lingotek-cancel-translation'] = $this->get_action_link(
				array(
					'document_id'   => $document->document_id,
					'target_id'     => $id,
					'target_locale' => $target_locale,
					'action'        => 'cancel-translation',
				),
				true
			);
			$actions['lingotek-delete-translation'] = $this->get_action_link(
				array(
					'document_id'   => $document->document_id,
					'target_id'     => $target_id,
					'target_locale' => $target_locale,
					'action'        => 'delete-translation',
				),
				true
			);
		}//end if

		return $actions;
	}

	/**
	 * Adds actions to bulk dropdown list table using a javascript hack
	 * as the existing filter does not allow to *add* actions
	 * also displays the progress dialog placeholder
	 *
	 * @since 0.2
	 */
	protected function _add_bulk_actions( $bulk_actions ) {
		foreach ( self::$actions as $action => $action_data ) {
			if ( strpos( $action, '-translation' ) && ! $action_data['per_locale'] ) {
				continue;
			}
			if ( !$action_data['per_locale'] ) {
				// phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain
                $bulk_actions[ 'bulk-lingotek-' . $action ] = sprintf(
                /* translators: %s: Action data */
                    esc_html__( 'Bulk Lingotek %s', 'lingotek-translation' ),
                    esc_html( $action_data['action'] )
                );
			}
			if ( null !== filter_input( INPUT_GET, 'bulk-lingotek-' . $action ) ) {
				$text = $action_data['progress'];
			}
			if ( isset( $action_data['per_locale'] ) && true === $action_data['per_locale'] ) {
				foreach ( PLL()->model->get_languages_list() as $language ) {
                    $bulk_actions[ 'bulk-lingotek-' . $action . ':' . $language->locale ] = sprintf(
                        __( '%1$s for %2$s', 'lingotek-translation' ),
                        $action_data['action'],
                        $language->lingotek_locale
                    );
				}
			}
		}
		if ( ! empty( $text ) ) {
			printf( '<div id="lingotek-progressdialog" style="display:none" title="%s"><div id="lingotek-progressbar"></div></div>', esc_html( $text ) );
		}

		return $bulk_actions;
	}

	/**
	 * Outputs javascript data for progress.js
	 * wp_localize_script($handle, $name, $data) $handle needs to be lingotek_myhandle
	 * where myhandle is handle of script you loaded previously with wp_enqueue_script (probably in admin.php)
	 *
	 * @since 0.1
	 */
	public function admin_enqueue_scripts() {
		foreach ( array_keys( self::$actions ) as $action ) {
			if ( null !== filter_input( INPUT_GET, "bulk-lingotek-$action" ) ) {
				$action_message = 'delete' === $action ? 'cancel' : $action;
				$data           = array(
					'action'   => null === filter_input( INPUT_GET, 'page' ) ? ( null === filter_input( INPUT_GET, 'taxonomy' ) ? "post_$action" : "term_$action" ) : "string_$action",
					'taxonomy' => null === filter_input( INPUT_GET, 'taxonomy' ) || ! taxonomy_exists( wp_unslash( filter_input( INPUT_GET, 'taxonomy' ) ) ) ? '' : filter_input( INPUT_GET, 'taxonomy' ),
					'sendback' => remove_query_arg( array( "bulk-lingotek-$action", 'ids', 'lingotek_warning', 'locales' ), wp_get_referer() ),
					'ids'      => array_map( 'intval', array_map('sanitize_text_field', explode(',', filter_input(INPUT_GET, 'ids'))) ),
					// TODO: Fix this based on the possible $action_message values.
					// phpcs:ignore WordPress.WP.I18n.InterpolatedVariableText
					'warning'  => null === filter_input( INPUT_GET, 'lingotek_warning' ) ? ( null === filter_input( INPUT_GET, 'lingotek_remove' ) ? '' : __( "You are about to $action_message existing translations from your Lingotek community. Are you sure?", 'lingotek-translation' ) ) : __( 'You are about to overwrite existing translations. Are you sure?', 'lingotek-translation' ),
					'nonce'    => wp_create_nonce( 'lingotek_progress' ),
				);
				if ( null !== filter_input( INPUT_GET, 'target_locale' ) ) {
					$data['target_locale'] = sanitize_lingotek_locale( filter_input( INPUT_GET, 'target_locale' ) );
				}
				if ( null !== filter_input( INPUT_GET, 'locales' ) ) {
					$data['locales'] = explode( ',', filter_input( INPUT_GET, 'locales' ) );
					array_map( 'sanitize_lingotek_locale', explode( ',', filter_input( INPUT_GET, 'locales' ) ) );
				}
				wp_localize_script( 'lingotek_progress', 'lingotek_data', $data );
				return;
			}//end if
		}//end foreach
	}

	/**
	 * Manages actions driven by document_id
	 *
	 * @since 0.2
	 *
	 * @param string $action Action name.
	 * @return bool True if the action was managed, false otherwise.
	 */
	protected function _manage_actions( $action ) {
		if ( null !== filter_input( INPUT_GET, 'document_id' ) ) {
			$document_id = sanitize_key(filter_input( INPUT_GET, 'document_id' ));
			$document    = $this->lgtm->get_group_by_id( $document_id );
		}

		switch ( $action ) {
			case 'lingotek-status':
				check_admin_referer( 'lingotek-status' );
				$document->source_status();
				$document->translations_status();
				break;

			case 'lingotek-request':
				check_admin_referer( 'lingotek-request' );
				$locale = filter_input( INPUT_GET, 'locale' );
        		$sanitized_locale = null !== $locale ? sanitize_lingotek_locale( $locale ) : null;
				Lingotek_Logger::info(
					'User requested to translate an item',
					array(
						'document_id' => isset( $document_id ) ? $document_id : '',
						'locale'      => $sanitized_locale,
					)
				);
				null !== $sanitized_locale ? $document->request_translation( $sanitized_locale ) : $document->request_translations();
				break;

			case 'lingotek-download':
				check_admin_referer( 'lingotek-download' );
				$locale = filter_input( INPUT_GET, 'locale' );
        		$sanitized_locale = null !== $locale ? sanitize_lingotek_locale( $locale ) : null;
				Lingotek_Logger::info(
					'User requested to download translation',
					array(
						'document_id' => isset( $document_id ) ? $document_id : '',
						'locale'      => $sanitized_locale,
					)
				);
				null !== $sanitized_locale ? $document->create_translation( $sanitized_locale) : $document->create_translations();
				break;
			
			case 'lock-target':
				check_admin_referer( 'lock-target' );
				$locale = sanitize_lingotek_locale( filter_input( INPUT_GET, 'locale') );
				$ltk_locale = Lingotek::map_to_wp_locale( $locale );
				$status = $document->translations[ $ltk_locale];
				if ( $status === 'current') {
					$document->update_translation_status( $ltk_locale, 'locked' );
				} else {
					$document->update_translation_status( $ltk_locale, 'current' );
				}
				break;

			case 'lingotek-delete':
				check_admin_referer( 'lingotek-delete' );
				/**it was left as disassociate so that the delete functionality wouldn't break as it
				* did when changing disassociate to delete/cancel in other connectors
				*/
				$document->delete();
				if ( null !== filter_input( INPUT_GET, 'lingotek_redirect' ) && filter_var(filter_input(INPUT_GET, 'lingotek_redirect'), FILTER_VALIDATE_BOOLEAN) === true ) {
					$site_id = get_current_blog_id();
					wp_safe_redirect( get_site_url( $site_id, '/wp-admin/edit.php?post_type=page' ) );
					exit();
				}
				break;

			case 'lingotek-cancel':
				check_admin_referer( 'lingotek-cancel' );
				$document->cancel();
				if ( null !== filter_input( INPUT_GET, 'lingotek_redirect' ) && filter_var(filter_input(INPUT_GET, 'lingotek_redirect'), FILTER_VALIDATE_BOOLEAN) === true ) {
					$site_id = get_current_blog_id();
					wp_safe_redirect( get_site_url( $site_id, '/wp-admin/edit.php?post_type=page' ) );
					exit();
				}
				break;

			case 'lingotek-delete-translation':
				check_admin_referer( 'lingotek-delete-translation' );
				$target_id     = absint(filter_input(INPUT_GET, 'target_id'));
				$language      = PLL()->model->post->get_language( $target_id );
				$document->delete_translation( $language, $target_id );
				if ( null !== filter_input( INPUT_GET, 'lingotek_redirect' ) && filter_var(filter_input(INPUT_GET, 'lingotek_redirect'), FILTER_VALIDATE_BOOLEAN) === true ) {
					$site_id = get_current_blog_id();
					wp_safe_redirect( get_site_url( $site_id, '/wp-admin/edit.php?post_type=page' ) );
					exit();
				}
				break;

			case 'lingotek-cancel-translation':
				check_admin_referer( 'lingotek-cancel-translation' );
				$target_locale = sanitize_lingotek_locale( filter_input( INPUT_GET, 'target_locale' ) );
				$document_id   = sanitize_key( filter_input( INPUT_GET, 'document_id' ) );
				$document->cancel_translation( $document_id, $target_locale );
				if ( null !== filter_input( INPUT_GET, 'lingotek_redirect' ) && filter_var(filter_input(INPUT_GET, 'lingotek_redirect'), FILTER_VALIDATE_BOOLEAN) === true ) {
					$site_id = get_current_blog_id();
					wp_safe_redirect( get_site_url( $site_id, '/wp-admin/edit.php?post_type=page' ) );
					exit();
				}
				break;

			default:
				return false;
		}//end switch

		return true;
	}

	/**
	 * Ajax response to download translations and showing progress
	 *
	 * @since 0.1
	 */
	public function ajax_download() {
		check_ajax_referer( 'lingotek_progress', '_lingotek_nonce' );
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_die( __( 'You do not have sufficient permissions to access this page.', 'lingotek-translation' ) );
        }
		$document = $this->lgtm->get_group( $this->type, sanitize_key( filter_input( INPUT_POST, 'id' ) ) );
		if ( $document ) {
			foreach ( $document->translations as $locale => $status ) {
				if ( 'pending' === $status || 'ready' === $status || 'interim' === $status || 'current' === $status || 'ready-interim' === $status ) {
					$document->create_translation( $locale );
				}
			}
		}
		die();
	}

	/**
	 * Ajax response to request translations and showing progress
	 *
	 * @since 0.2
	 */
	public function ajax_request() {
		check_ajax_referer( 'lingotek_progress', '_lingotek_nonce' );
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_die( __( 'You do not have sufficient permissions to access this page.', 'lingotek-translation' ) );
        }
		$document = $this->lgtm->get_group( $this->type, sanitize_text_field( filter_input( INPUT_POST, 'id' ) ) );
		if ( $document ) {
			$document->request_translations();
		}
		die();
	}

	/**
	 * Ajax response to check translation status and showing progress
	 *
	 * @since 0.1
	 */
	public function ajax_status() {
		check_ajax_referer( 'lingotek_progress', '_lingotek_nonce' );
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_die( __( 'You do not have sufficient permissions to access this page.', 'lingotek-translation' ) );
        }
		$document = $this->lgtm->get_group( $this->type, sanitize_text_field( filter_input( INPUT_POST, 'id' ) ) );
		if ( $document ) {
			$document->source_status();
			$document->translations_status();
		}
		die();
	}

	/**
	 * Ajax response delete translations and showing progress
	 *
	 * @since 0.2
	 */
	public function ajax_delete() {
		check_ajax_referer( 'lingotek_progress', '_lingotek_nonce' );
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_die( __( 'You do not have sufficient permissions to access this page.', 'lingotek-translation' ) );
        }
		$id       = sanitize_key( filter_input( INPUT_POST, 'id' ) );
		$language = PLL()->model->post->get_language( $id );
		$document = $this->lgtm->get_group( $this->type, $id );
		// phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
		if ( $document && $document->source == $id ) {
			/**it was left as disassociate so that the delete functionality wouldn't break as it
			* did when changing disassociate to delete/cancel in other connectors
			*/
			$document->delete();
			// phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
		} elseif ( $document && $document->source != $id && $language ) {
			$document->delete_translation( $language, $id );
		}
		die();
	}

	/**
	 * Ajax response cancel translations and showing progress
	 */
	public function ajax_cancel() {
		check_ajax_referer( 'lingotek_progress', '_lingotek_nonce' );
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_die( __( 'You do not have sufficient permissions to access this page.', 'lingotek-translation' ) );
        }
		$id       = sanitize_key( filter_input( INPUT_POST, 'id' ) );
		$language = PLL()->model->post->get_language( $id );
		$document = $this->lgtm->get_group( $this->type, $id );
		// phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
		if ( $document && $document->source == $id ) {
			$document->cancel();
			// phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
		} elseif ( $document && $document->source != $id && $language ) {
			$document->cancel_translation( $document->document_id, $language->lingotek_locale );
		}
		die();
	}

	/**
	 * Ajax response cancel only one target and showing progress
	 *
	 * @since 1.5.0
	 */
	public function ajax_cancel_translation() {
		check_ajax_referer( 'lingotek_progress', '_lingotek_nonce' );
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_die( __( 'You do not have sufficient permissions to access this page.', 'lingotek-translation' ) );
        }
		$id            = sanitize_key( filter_input( INPUT_POST, 'id' ) );
		$target_locale = sanitize_lingotek_locale( filter_input( INPUT_POST, 'target_locale' ) );
		$document      = $this->lgtm->get_group( $this->type, $id );
		// phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
		if ( $document && $target_locale ) {
			$document->cancel_translation( $document->document_id, $target_locale );
		}
		die();
	}
	/**
	 * Ajax call to get the price estimation of a given document.
	 */
	public function ajax_estimate_cost() {
		check_ajax_referer( 'lingotek_professional', '_lingotek_nonce' );
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_die( __( 'You do not have sufficient permissions to access this page.', 'lingotek-translation' ) );
        }
		$document_id   = sanitize_key( filter_input( INPUT_GET, 'document_id' ) );
		$locale        = sanitize_lingotek_locale( filter_input( INPUT_GET, 'locale' ) );
		$lingotek_auth = sanitize_text_field( filter_input( INPUT_GET, 'Authorization-Lingotek' ) );
		$client        = new Lingotek_API();
		$response      = $client->get_cost_estimate( $lingotek_auth, $document_id, $locale );
		echo wp_json_encode( $response );
		die();
	}

	/**
	 * Ajax call to request professional translation of a document through bridge.
	 */
	public function ajax_request_professional_translation() {
		check_ajax_referer( 'lingotek_professional', '_lingotek_nonce' );
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_die( __( 'You do not have sufficient permissions to access this page.', 'lingotek-translation' ) );
        }
		$post_vars = filter_input_array( INPUT_POST );
		$client    = new Lingotek_API();
		$response  = $client->request_professional_translation_bulk( $post_vars['workflow_id'], $post_vars['translations'], $post_vars['total_estimate'], $post_vars['summary'] );
		if ( true === $response['data']['transaction_approved'] ) {
			foreach ( $post_vars['translations'] as $document_id => $locales ) {
				$document = $this->lgtm->get_group( $post_vars['type'], $post_vars['ids'][ $document_id ] );
				if ( $document ) {
					foreach ( $locales as $locale ) {
						$locale = $post_vars['lingotek_locale_to_wp_locale'][ $locale ];
						$document->update_translation_status( $locale, 'pending' );
					}
				} else {
					// TODO: what if a document doesn't exists? T_T
				}
			}
		}

		echo wp_json_encode( $response );
		die();
	}

	public function ajax_get_ltk_terms_and_conditions() {
		check_ajax_referer( 'lingotek_professional', '_lingotek_nonce' );
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_die( __( 'You do not have sufficient permissions to access this page.', 'lingotek-translation' ) );
        }
		$client = new Lingotek_API();
		echo wp_json_encode( $client->get_lingotek_terms_and_conditions() );
		die();
	}

	public function ajax_get_user_payment_information() {
		check_ajax_referer( 'lingotek_professional', '_lingotek_nonce' );
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_die( __( 'You do not have sufficient permissions to access this page.', 'lingotek-translation' ) );
        }
		$client   = new Lingotek_API();
		$response = $client->get_user_payment_information();
		echo wp_json_encode( $response );
		die();
	}

	/**
	 * Collects and returns all API errors
	 *
	 * @since 1.1
	 *
	 * @param string $errors
	 */
	public static function retrieve_api_error( $errors ) {
		$api_error = "\n";

		foreach ( $errors as $error => $error_message ) {
			if ( is_array( $error_message ) ) {
				if ( ! empty( $error_message ) ) {
					foreach ( $error_message as $locale => $message ) {
						$api_error = $api_error . $message . "\n";
					}
				}
			} else {
				$api_error = $api_error . $error_message . "\n";
			}
		}

		return $api_error;
	}

	/**
	 * Gets the upload url.
	 *
	 * @param int         $object_id
	 * @param string|null $locale
	 *
	 * @return string
	 *
	 * @since 1.5.0
	 */
	public function upload_url( $object_id, $locale = null ) {
		$args = array(
			$this->type => $object_id,
			'action'    => 'lingotek-upload',
			'noheader'  => true,
		);
		if ( null !== $locale ) {
			$args['locale'] = $locale;
		}
		if ( isset( $args['string'] ) ) {
			$args['string'] = rawurlencode( $args['string'] );
		}
		$link = wp_nonce_url( defined( 'DOING_AJAX' ) && DOING_AJAX ? add_query_arg( $args, wp_get_referer() ) : add_query_arg( $args ), 'lingotek-upload' );
		self::link_to_settings_if_not_connected( $link );

		return $link;
	}

	/**
	 * Gets the request translation url.
	 *
	 * @param int    $object_id
	 * @param object $document
	 * @param string $ltk_locale
	 *
	 * @return string
	 *
	 * @since 1.5.0
	 */
	public function request_translation_url( $object_id, $document, $ltk_locale ) {
		$link = wp_nonce_url(
			add_query_arg(
				array(
					'document_id' => $document->document_id,
					'locale'      => $ltk_locale,
					'action'      => 'lingotek-request',
					'noheader'    => true,
				),
				defined( 'DOING_AJAX' ) && DOING_AJAX ? wp_get_referer() : wp_get_referer()
			),
			'lingotek-request'
		);
		return $link;
	}

	/**
	 * Gets the check source status url.
	 *
	 * @param int    $object_id
	 * @param object $document
	 * @param string $ltk_locale
	 *
	 * @return string
	 *
	 * @since 1.5.0
	 */
	public function check_source_url( $object_id, $document, $ltk_locale ) {
		$link = wp_nonce_url(
			add_query_arg(
				array(
					'document_id' => $document->document_id,
					'action'      => 'lingotek-status',
					'noheader'    => true,
				),
				defined( 'DOING_AJAX' ) && DOING_AJAX ? wp_get_referer() : wp_get_referer()
			),
			'lingotek-status'
		);
		return $link;
	}

	/**
	 * Gets the check translation status url.
	 *
	 * @param int    $object_id
	 * @param object $document
	 * @param string $ltk_locale
	 *
	 * @return string
	 *
	 * @since 1.5.0
	 */
	public function check_translation_url( $object_id, $document, $ltk_locale ) {
		$link = wp_nonce_url(
			add_query_arg(
				array(
					'document_id' => $document->document_id,
					'action'      => 'lingotek-status',
					'noheader'    => true,
				),
				defined( 'DOING_AJAX' ) && DOING_AJAX ? wp_get_referer() : wp_get_referer()
			),
			'lingotek-status'
		);
		return $link;
	}

	/**
	 * Gets the cancel translation target url.
	 *
	 * @param int    $object_id
	 * @param object $document
	 * @param string $ltk_locale
	 *
	 * @return string
	 *
	 * @since 1.5.0
	 */
	public function cancel_translation_url( $object_id, $document, $ltk_locale ) {
		$link = wp_nonce_url(
			add_query_arg(
				array(
					'document_id'   => $document->document_id,
					'id'            => $object_id,
					'target_locale' => $ltk_locale,
					'action'        => 'lingotek-cancel-translation',
					'noheader'      => true,
				),
				defined( 'DOING_AJAX' ) && DOING_AJAX ? wp_get_referer() : wp_get_referer()
			),
			'lingotek-cancel-translation'
		);

		return $link;
	}

	/**
	 * Gets the download translation url.
	 *
	 * @param int    $object_id
	 * @param object $document
	 * @param string $ltk_locale
	 *
	 * @return string
	 *
	 * @since 1.5.0
	 */
	public function download_translation_url( $object_id, $document, $ltk_locale ) {
		$link = wp_nonce_url(
			add_query_arg(
				array(
					'document_id' => $document->document_id,
					'locale'      => $ltk_locale,
					'action'      => 'lingotek-download',
					'noheader'    => true,
				),
				defined( 'DOING_AJAX' ) && DOING_AJAX ? wp_get_referer() : wp_get_referer()
			),
			'lingotek-download'
		);
		return $link;
	}

	public function toggle_lock ( $object_id, $document, $ltk_locale ) {
		$link = wp_nonce_url(
			add_query_arg(
				array(
					'document_id' => $document->document_id,
					'locale'      => $ltk_locale,
					'action'      => 'lock-target',
					'noheader'    => true,
				),
				defined( 'DOING_AJAX' ) && DOING_AJAX ? wp_get_referer() : wp_get_referer()
			),
			'lock-target'
		);
		return $link;
	}

	/**
	 * Gets the workbench access url.
	 *
	 * @param int    $object_id
	 * @param object $document
	 * @param string $ltk_locale
	 *
	 * @return string
	 *
	 * @since 1.5.0
	 */
	public function workbench_url( $object_id, $document, $ltk_locale ) {
		$link = self::workbench_link( $document->document_id, $ltk_locale );
		self::link_to_settings_if_not_connected( $link );
		return $link;
	}
}
