HEX
Server: LiteSpeed
System: Linux server315.web-hosting.com 4.18.0-553.54.1.lve.el8.x86_64 #1 SMP Wed Jun 4 13:01:13 UTC 2025 x86_64
User: globfdxw (6114)
PHP: 8.1.34
Disabled: NONE
Upload Files
File: /home/globfdxw/www/wp-content/plugins/wpforms-entry-automation/src/Export/Provider/Dropbox.php
<?php

namespace WPFormsEntryAutomation\Export\Provider;

use WPForms\Helpers\File;
use WPFormsDropbox\Api\Api;
use WPFormsEntryAutomation\Helpers\DropboxReconnect;
use RuntimeException;

/**
 * Dropbox provider implementation.
 *
 * @since 1.0.0
 */
class Dropbox extends Provider implements AddonProviderInterface {

	/**
	 * Instance of DropboxReconnect class for handling reconnection functionality.
	 * We may use it in the future to disable this logic, when it will be implemented in the Dropbox addon itself.
	 *
	 * @since 1.0.0
	 *
	 * @var DropboxReconnect
	 */
	private $reconnect;

	/**
	 * Constructor method for initializing the object.
	 * Initializes the DropboxReconnect instance.
	 *
	 * @since 1.0.0
	 */
	public function __construct() {

		$this->reconnect = new DropboxReconnect();
	}

	/**
	 * Registers the necessary hooks for the Dropbox reconnect functionality.
	 *
	 * @since 1.0.0
	 */
	public function hooks(): void {

		$this->reconnect->hooks();
	}

	/**
	 * Get a human-readable title of the provider.
	 *
	 * @since 1.0.0
	 *
	 * @return string Provider title.
	 */
	public function get_title(): string {

		return esc_html__( 'Dropbox', 'wpforms-entry-automation' );
	}

	/**
	 * Checks whether the required addon is activated.
	 *
	 * @since 1.0.0
	 *
	 * @return bool True if the addon is activated, false otherwise.
	 */
	public function is_activated(): bool {

		return wpforms_is_addon_initialized( 'dropbox' );
	}

	/**
	 * Retrieve addon data for Dropbox.
	 *
	 * @since 1.0.0
	 *
	 * @return array Addon data for Dropbox.
	 */
	public function get_data(): array {

		return wpforms()->obj( 'addons' )->get_addon( 'dropbox' );
	}

	/**
	 * Retrieve custom templates.
	 *
	 * @since 1.0.0
	 *
	 * @return array List of custom template identifiers.
	 */
	public function get_custom_templates(): array {

		if ( ! $this->is_activated() ) {
			return [];
		}

		return [
			'export-to/dropbox',
			'export-to/dropbox-fields',
		];
	}

	/**
	 * Deliver formatted entries via Dropbox.
	 *
	 * @since 1.0.0
	 *
	 * @param string $entries_file_path Formatted entries.
	 * @param array  $connection        Connection data.
	 * @param array  $form_data         Form data.
	 *
	 * @return bool True on success, false on failure.
	 */
	public function deliver( string $entries_file_path, array $connection, array $form_data ): bool {

		$account_id = $connection['dropbox']['account_id'] ?? '';
		$api        = wpforms_entry_automation()->get( 'dropbox_client' )->new_api( $account_id );

		if ( ! $api ) {
			wpforms_log(
				'Entry Automation - Dropbox API instance is not available',
				[
					'account_id' => $account_id,
				],
				[
					'type'    => 'error',
					'form_id' => absint( $form_data['id'] ),
				]
			);

			return false;
		}

		try {
			$dropbox_path  = $this->get_full_path( $api, $connection, basename( $entries_file_path ) );
			$uploaded_file = $api->upload( $entries_file_path, $dropbox_path );
		} catch ( RuntimeException $exception ) {
			return false;
		}

		if ( empty( $uploaded_file['id'] ) || empty( $uploaded_file['path_display'] ) ) {
			wpforms_log(
				'Entry Automation - Dropbox upload failed',
				[
					'file_path' => $entries_file_path,
					'path'      => $dropbox_path,
					'response'  => $uploaded_file,
				],
				[
					'type'    => 'error',
					'form_id' => absint( $form_data['id'] ),
				]
			);

			return false;
		}

		$shared_link = $api->create_shared_link( $uploaded_file['path_display'] );

		$destination = wp_json_encode(
            [
				'id'  => $uploaded_file['id'],
				'url' => $shared_link['url'] ?? '',
            ]
        );

		wpforms_entry_automation()->get( 'tasks' )->update( $connection['task_id'], [ 'destination' => $destination ] );

		return true;
	}

	/**
	 * Checks if the file with the specified name exists using the provided task data and connection information.
	 *
	 * @since 1.0.0
	 *
	 * @param string $file_name  The name of the file to check.
	 * @param array  $task_data  Task data that may include additional file information.
	 * @param array  $connection An array containing the connection details required to check file existence.
	 *
	 * @return bool True if the file exists, false otherwise.
	 */
	public function is_file_exist( string $file_name, array $task_data, array $connection ): bool {

		$file_id = $this->get_destination_file_id( $task_data );

		if ( empty( $file_id ) ) {
			return false;
		}

		$account_id = $connection['dropbox']['account_id'] ?? '';
		$api        = wpforms_entry_automation()->get( 'dropbox_client' )->new_api( $account_id );

		if ( ! $api ) {
			return false;
		}

		$request = wpforms_entry_automation()->get( 'dropbox_client' )->new_request( $account_id );

		if ( ! $request ) {
			return false;
		}

		// Check if the file exists using the Dropbox API.
		$body = wp_json_encode( [ 'path' => $file_id ] );
		$url  = Api::API_ACCOUNT_URL . 'files/get_metadata';

		try {
			$response = $request->post( $url, $body );
		} catch ( RuntimeException $exception ) {
			return false;
		}

		// If there are no errors in the response, the file exists.
		return ! $response->has_errors();
	}

	/**
	 * Downloads a file using the provided file ID and connection details.
	 *
	 * @since 1.0.0
	 *
	 * @param array $task_data  Task data containing the file ID to be downloaded.
	 * @param array $connection The connection details required to access the file.
	 *
	 * @return string The contents of the downloaded file.
	 */
	public function download_file( array $task_data, array $connection ): string {

		$file_id = $this->get_destination_file_id( $task_data );

		if ( empty( $file_id ) ) {
			return '';
		}

		$account_id        = $connection['dropbox']['account_id'] ?? '';
		$file_content      = wpforms_entry_automation()->get( 'dropbox_client' )->get_file( $account_id, $file_id );
		$formatter_manager = wpforms_entry_automation()->get( 'formatter_manager' );
		// Create a temporary file path. Use basename for safety.
		$tmp_file = $formatter_manager->get_temp_dir() . '/' . $task_data['file_name'];
		// Write the file content to the temporary file.
		$result = File::put_contents( $tmp_file, $file_content );

		return $result === false ? '' : $tmp_file;
	}

	/**
	 * Deletes a file with the specified ID using the provided connection.
	 *
	 * @since 1.0.0
	 *
	 * @param array $task_data  Task data.
	 * @param array $connection An array containing the connection details required to perform the delete operation.
	 */
	public function delete_file( array $task_data, array $connection ): void {

		$file_id = $this->get_destination_file_id( $task_data );

		if ( empty( $file_id ) ) {
			return;
		}

		$account_id = $connection['dropbox']['account_id'] ?? '';
		$request    = wpforms_entry_automation()->get( 'dropbox_client' )->new_request( $account_id );

		if ( ! $request ) {
			return;
		}

		// Prepare the request body for the Dropbox API delete endpoint.
		$body = wp_json_encode( [ 'path' => $file_id ] );
		$url  = Api::API_ACCOUNT_URL . 'files/delete_v2';

		try {
			// Make the POST request to delete the file.
			$response = $request->post( $url, $body );
		} catch ( RuntimeException $exception ) {
			return;
		}

		if ( ! $response->has_errors() ) {
			return;
		}

		// Log errors if any.
		wpforms_log(
			'Request to Dropbox API was failed',
			[
				'message' => $response->get_body(),
				'request' => [
					'method' => 'POST',
					'url'    => $url,
				],
			],
			[
				'type' => [ 'provider', 'error' ],
			]
		);
	}

	/**
	 * Sanitizes the connection data for the specified export service.
	 * Ensures the connection data is properly formatted and sanitized for
	 * Dropbox when the export service is set to 'dropbox'. Removes connection
	 * details for unsupported export services.
	 *
	 * @since 1.0.0
	 *
	 * @param array $connection_data An array of connection data that will be sanitized in place.
	 * @param array $form_data       An array containing form-related data.
	 */
	public function sanitize_connection( array &$connection_data, array $form_data ): void {

		$connection_data['dropbox'] = $connection_data['dropbox'] ?? [];

		$this->sanitize_connection_fields( $connection_data['dropbox'], $form_data );
	}

	/**
	 * Retrieves the destination file ID from the task data.
	 *
	 * @since 1.0.0
	 *
	 * @param array $task_data Task data containing destination details.
	 *
	 * @return string The destination file ID.
	 */
	private function get_destination_file_id( array $task_data ): string {

		$destination = $task_data['destination'] ?? '';
		$destination = wpforms_is_json( $destination ) ? json_decode( $destination, true ) : [];
		$file_id     = $destination['id'] ?? '';

		return (string) $file_id;
	}

	/**
	 * Sanitizes Dropbox connection fields and updates the folder name if not set.
	 *
	 * @since 1.0.0
	 *
	 * @param array $dropbox_data Reference to the Dropbox data array containing connection fields to be sanitized.
	 * @param array $form_data    Array containing form data, used to populate the folder name if it is empty.
	 */
	private function sanitize_connection_fields( array &$dropbox_data, array $form_data ): void {

		$dropbox_data['account_id']  = sanitize_text_field( $dropbox_data['account_id'] ?? '' );
		$dropbox_data['folder_name'] = $this->sanitize_folder_name( $dropbox_data['folder_name'] ?? '', $form_data );
		$dropbox_data['path']        = sanitize_text_field( $dropbox_data['path'] ?? '' );

		if ( empty( $dropbox_data['folder_name'] ) ) {
			$dropbox_data['folder_name'] = $form_data['settings']['form_title'] ?? '';
		}
	}

	/**
	 * Constructs the full path of a file within the specified folder and connection.
	 *
	 * @since 1.0.0
	 *
	 * @param Api    $api        An instance of the Api class used to retrieve necessary data.
	 * @param array  $connection An array containing connection details, including folder path information.
	 * @param string $file_name  The name of the file for which the full path is to be constructed.
	 *
	 * @return string The full path of the file, or an empty string if the main folder is not found.
	 */
	private function get_full_path( Api $api, array $connection, string $file_name ): string {

		$main_folder = $this->get_main_folder( $api, $connection );

		if ( empty( $main_folder ) ) {
			return '';
		}

		$folder = $connection['dropbox']['path'] ?? '';

		if ( $folder === '/' ) {
			$folder = $main_folder;
		}

		return $folder . '/' . $file_name;
	}

	/**
	 * Retrieves the main folder from Dropbox based on the provided connection details.
	 * If the folder does not exist, it creates the folder using the API.
	 *
	 * @since 1.0.0
	 *
	 * @param Api   $api        An instance of the API class used to interact with Dropbox.
	 * @param array $connection An array containing Dropbox connection details, including the target folder name.
	 *
	 * @return string The path of the main folder in Dropbox.
	 */
	private function get_main_folder( Api $api, array $connection ): string {

		$folder_name = sprintf( '/%s', wpforms_dropbox()->get( 'helper' )->prepare_folder_name( $connection['dropbox']['folder_name'] ) );

		return $api->folder_exists( $folder_name ) ? $folder_name : $api->create_folder( $folder_name );
	}

	/**
	 * Sanitizes and prepares a folder name based on the provided input and form data.
	 *
	 * Ensures the folder name is secure, clean, and compliant with naming conventions.
	 * If no folder name is provided, it falls back to the form title.
	 *
	 * @since 1.0.0
	 *
	 * @param string $folder_name The input folder name to be sanitized.
	 * @param array  $form_data   Form data containing settings such as the form title.
	 *
	 * @return string The sanitized folder name.
	 */
	private function sanitize_folder_name( string $folder_name, array $form_data ): string {

		$folder_name = sanitize_text_field( wpforms_dropbox()->get( 'helper' )->prepare_folder_name( $folder_name ) );

		if ( ! isset( $form_data['settings']['form_title'] ) || ! wpforms_is_empty_string( $folder_name ) ) {
			return $folder_name;
		}

		// Form Name is used as a fallback.
		return sanitize_text_field(
			wpforms_dropbox()->get( 'helper' )->prepare_folder_name( $form_data['settings']['form_title'] )
		);
	}
}