nextcloud_webdav_client-1.0.x-dev/src/Form/NextCloudWebDavSettingsForm.php

src/Form/NextCloudWebDavSettingsForm.php
<?php

namespace Drupal\nextcloud_webdav_client\Form;

use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\nextcloud_webdav_client\Service\NextCloudWebDavClient;
use Drupal\nextcloud_webdav_client\Service\NextCloudOAuth2Manager;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Configure NextCloud WebDAV settings.
 */
class NextCloudWebDavSettingsForm extends ConfigFormBase {

  /**
   * The NextCloud WebDAV client service.
   *
   * @var \Drupal\nextcloud_webdav_client\Service\NextCloudWebDavClient
   */
  protected $webdavClient;

  /**
   * The OAuth2 manager service.
   *
   * @var \Drupal\nextcloud_webdav_client\Service\NextCloudOAuth2Manager
   */
  protected $oauth2Manager;

  /**
   * Constructs a NextCloudWebDavSettingsForm object.
   *
   * @param \Drupal\nextcloud_webdav_client\Service\NextCloudWebDavClient $webdav_client
   *   The NextCloud WebDAV client service.
   * @param \Drupal\nextcloud_webdav_client\Service\NextCloudOAuth2Manager $oauth2_manager
   *   The OAuth2 manager service.
   */
  public function __construct(NextCloudWebDavClient $webdav_client, NextCloudOAuth2Manager $oauth2_manager) {
    $this->webdavClient = $webdav_client;
    $this->oauth2Manager = $oauth2_manager;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('nextcloud_webdav_client.client'),
      $container->get('nextcloud_webdav_client.oauth2_manager')
    );
  }

  /**
   * {@inheritdoc}
   */
  protected function getEditableConfigNames() {
    return [
      'nextcloud_webdav_client.settings',
      'oauth2_server.oauth',
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'nextcloud_webdav_client_settings';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $config = $this->config('nextcloud_webdav_client.settings');

    // Authentication Mode Selection.
    $form['auth_mode'] = [
      '#type' => 'radios',
      '#title' => $this->t('Authentication Mode'),
      '#options' => [
        'basic' => $this->t('Basic Authentication (Username & Password)'),
        'oauth2' => $this->t('OAuth2 / SSO Authentication'),
      ],
      '#default_value' => $config->get('auth_mode') ?: 'basic',
      '#description' => $this->t('Select how to authenticate with NextCloud. Basic Auth uses username/password, OAuth2 uses token-based SSO.'),
      '#required' => TRUE,
    ];

    // Connection Settings.
    $form['connection'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('Connection Settings'),
      '#collapsible' => FALSE,
    ];

    $form['connection']['server_url'] = [
      '#type' => 'url',
      '#title' => $this->t('Server URL'),
      '#description' => $this->t('The base URL of your NextCloud server (e.g., https://cloud.example.com)'),
      '#default_value' => $config->get('server_url'),
      '#required' => TRUE,
    ];

    $form['connection']['base_path'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Base Path'),
      '#description' => $this->t('The WebDAV base path (usually /remote.php/dav/files/)'),
      '#default_value' => $config->get('base_path') ?: '/remote.php/dav/files/',
      '#required' => TRUE,
    ];

    // Basic Auth Fields (conditional).
    $form['basic_auth'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('Basic Authentication Credentials'),
      '#states' => [
        'visible' => [
          ':input[name="auth_mode"]' => ['value' => 'basic'],
        ],
      ],
    ];

    $form['basic_auth']['username'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Username'),
      '#description' => $this->t('Your NextCloud username'),
      '#default_value' => $config->get('username'),
      '#states' => [
        'required' => [
          ':input[name="auth_mode"]' => ['value' => 'basic'],
        ],
      ],
    ];

    // Check if password already exists.
    $existing_password = $config->get('password');
    $password_description = $this->t('Your NextCloud password or app password (recommended)');

    if (!empty($existing_password)) {
      $password_description = $this->t('Your NextCloud password or app password (recommended). Leave empty to keep the current password.');
      $form['basic_auth']['password_status'] = [
        '#type' => 'markup',
        '#markup' => '<div class="messages messages--status">' .
          $this->t('✓ Password is currently set. Leave the password field empty to keep the existing password.') .
          '</div>',
      ];
    }

    $form['basic_auth']['password'] = [
      '#type' => 'password',
      '#title' => $this->t('Password'),
      '#description' => $password_description,
    ];

    // OAuth2 Configuration Fields (conditional).
    $form['oauth2_config'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('OAuth2 Configuration'),
      '#states' => [
        'visible' => [
          ':input[name="auth_mode"]' => ['value' => 'oauth2'],
        ],
      ],
    ];

    $form['oauth2_config']['oauth2_client_id'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Client ID'),
      '#description' => $this->t('OAuth2 Client ID from your NextCloud OAuth2 app'),
      '#default_value' => $config->get('oauth2_client_id'),
      '#states' => [
        'required' => [
          ':input[name="auth_mode"]' => ['value' => 'oauth2'],
        ],
      ],
    ];

    $form['oauth2_config']['oauth2_client_secret'] = [
      '#type' => 'password',
      '#title' => $this->t('Client Secret'),
      '#description' => $this->t('OAuth2 Client Secret from your NextCloud OAuth2 app'),
      '#default_value' => $config->get('oauth2_client_secret'),
    ];

    if (!empty($config->get('oauth2_client_secret'))) {
      $form['oauth2_config']['oauth2_client_secret']['#description'] = $this->t('OAuth2 Client Secret (leave empty to keep existing secret)');
    }

    $form['oauth2_config']['oauth2_authorize_endpoint'] = [
      '#type' => 'url',
      '#title' => $this->t('Authorization Endpoint'),
      '#description' => $this->t('OAuth2 authorization endpoint URL (e.g., https://cloud.example.com/apps/oauth2/authorize)'),
      '#default_value' => $config->get('oauth2_authorize_endpoint'),
      '#states' => [
        'required' => [
          ':input[name="auth_mode"]' => ['value' => 'oauth2'],
        ],
      ],
    ];

    $form['oauth2_config']['oauth2_token_endpoint'] = [
      '#type' => 'url',
      '#title' => $this->t('Token Endpoint'),
      '#description' => $this->t('OAuth2 token endpoint URL (e.g., https://cloud.example.com/apps/oauth2/api/v1/token)'),
      '#default_value' => $config->get('oauth2_token_endpoint'),
      '#states' => [
        'required' => [
          ':input[name="auth_mode"]' => ['value' => 'oauth2'],
        ],
      ],
    ];

    $form['oauth2_config']['oauth2_userinfo_endpoint'] = [
      '#type' => 'url',
      '#title' => $this->t('User Info Endpoint (Optional)'),
      '#description' => $this->t('OAuth2 user info endpoint URL'),
      '#default_value' => $config->get('oauth2_userinfo_endpoint'),
    ];

    $form['oauth2_config']['oauth2_scopes'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Scopes'),
      '#description' => $this->t('OAuth2 scopes (space-separated)'),
      '#default_value' => $config->get('oauth2_scopes') ?: 'openid profile email',
    ];

    $form['oauth2_config']['oauth2_mode'] = [
      '#type' => 'radios',
      '#title' => $this->t('OAuth2 Mode'),
      '#options' => [
        'site' => $this->t('Site-wide (single NextCloud account for all users)'),
        'per-user' => $this->t('Per-user (each Drupal user links their own NextCloud account)'),
      ],
      '#default_value' => $config->get('oauth2_mode') ?: 'site',
      '#description' => $this->t('Choose how OAuth2 authentication works. Site-wide uses one account for all operations. Per-user allows each user to link their own account.'),
      '#states' => [
        'visible' => [
          ':input[name="auth_mode"]' => ['value' => 'oauth2'],
        ],
      ],
    ];

    $form['oauth2_config']['oauth2_username'] = [
      '#type' => 'textfield',
      '#title' => $this->t('NextCloud Username'),
      '#description' => $this->t('Your NextCloud username (required for WebDAV file paths)'),
      '#default_value' => $config->get('oauth2_username'),
      '#states' => [
        'required' => [
          ':input[name="auth_mode"]' => ['value' => 'oauth2'],
          ':input[name="oauth2_mode"]' => ['value' => 'site'],
        ],
        'visible' => [
          ':input[name="oauth2_mode"]' => ['value' => 'site'],
        ],
      ],
    ];

    $form['oauth2_config']['per_user_info'] = [
      '#type' => 'markup',
      '#markup' => '<div class="messages messages--info">' .
        $this->t('In per-user mode, users link their own NextCloud accounts at /user/{uid}/nextcloud. Configure OAuth2 endpoints above, then grant users the "link nextcloud account" permission.') .
        '</div>',
      '#states' => [
        'visible' => [
          ':input[name="oauth2_mode"]' => ['value' => 'per-user'],
        ],
      ],
    ];

    $form['oauth2_config']['oauth2_use_index_php'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Use /index.php/ prefix in OAuth2 URLs'),
      '#description' => $this->t('Check this if your NextCloud requires /index.php/ in the path (e.g., https://nextcloud.com/index.php/apps/oauth2/authorize)'),
      '#default_value' => $config->get('oauth2_use_index_php'),
    ];

    // Get current OAuth2 Server user_sub_property setting.
    $oauth2_server_config = $this->configFactory->get('oauth2_server.oauth');
    $current_user_sub = $oauth2_server_config->get('user_sub_property') ?: 'uid';

    $form['oauth2_config']['oauth2_user_identifier'] = [
      '#type' => 'radios',
      '#title' => $this->t('User Identifier for OAuth2'),
      '#options' => [
        'uid' => $this->t('User ID (numeric, e.g., 1, 2, 3)'),
        'name' => $this->t('Username (e.g., john_doe, admin)'),
        'mail' => $this->t('Email Address (e.g., user@example.com)'),
      ],
      '#default_value' => $current_user_sub,
      '#description' => $this->t('Choose which Drupal user field to use as the identifier when users log in to NextCloud via OAuth2. This determines what NextCloud will use as the username. <strong>Note:</strong> Changing this after users have already logged in may require them to re-authenticate.'),
      '#states' => [
        'visible' => [
          ':input[name="auth_mode"]' => ['value' => 'oauth2'],
        ],
      ],
    ];

    // OAuth2 Authorization Status.
    $has_tokens = !empty($config->get('oauth2_access_token'));
    $token_expiry = $this->oauth2Manager->getTokenExpiry();

    $form['oauth2_status'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('OAuth2 Authorization Status'),
      '#states' => [
        'visible' => [
          ':input[name="auth_mode"]' => ['value' => 'oauth2'],
        ],
      ],
    ];

    if ($has_tokens) {
      $status_markup = '<div class="messages messages--status">';
      $status_markup .= '<strong>' . $this->t('✓ OAuth2 Authorization Active') . '</strong><br>';

      if ($token_expiry) {
        $expiry_date = \Drupal::service('date.formatter')->format($token_expiry, 'medium');
        $status_markup .= $this->t('Token expires: @date', ['@date' => $expiry_date]);
      }

      $status_markup .= '</div>';

      $form['oauth2_status']['status'] = [
        '#type' => 'markup',
        '#markup' => $status_markup,
      ];

      // Refresh Token Link.
      $form['oauth2_status']['refresh_link'] = [
        '#type' => 'markup',
        '#markup' => '<p>' . $this->t('<a href="@url">Refresh Token</a> | <a href="@clear_url">Clear Authorization</a>', [
          '@url' => Url::fromRoute('nextcloud_webdav_client.oauth2_refresh')->toString(),
          '@clear_url' => Url::fromRoute('nextcloud_webdav_client.oauth2_clear')->toString(),
        ]) . '</p>',
      ];
    }
    else {
      $form['oauth2_status']['no_auth'] = [
        '#type' => 'markup',
        '#markup' => '<div class="messages messages--warning">' .
          $this->t('OAuth2 authorization required. Save your OAuth2 configuration first, then click the "Authorize with NextCloud" button to begin authorization.') .
          '</div>',
      ];

      // Authorization button (only show if config is saved).
      if ($this->oauth2Manager->isConfigured()) {
        $initiate_url = Url::fromRoute('nextcloud_webdav_client.oauth2_initiate')->toString();

        $form['oauth2_status']['authorize_button'] = [
          '#type' => 'markup',
          '#markup' => '<p><a href="' . $initiate_url . '" class="button button--primary">' .
            $this->t('Authorize with NextCloud') .
            '</a></p>',
        ];
      }
    }

    // Advanced Settings.
    $form['advanced'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('Advanced Settings'),
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
    ];

    $form['advanced']['timeout'] = [
      '#type' => 'number',
      '#title' => $this->t('Timeout'),
      '#description' => $this->t('Request timeout in seconds'),
      '#default_value' => $config->get('timeout') ?: 30,
      '#min' => 1,
      '#max' => 300,
    ];

    $form['advanced']['verify_ssl'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Verify SSL certificates'),
      '#description' => $this->t('Uncheck this only for development or self-signed certificates'),
      '#default_value' => $config->get('verify_ssl') !== FALSE,
    ];

    // Security Information.
    $form['security_info'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('Security Information'),
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
    ];

    $form['security_info']['info'] = [
      '#type' => 'markup',
      '#markup' => '<div class="description">' .
        '<div class="messages messages--warning" style="margin-bottom: 1em;">' .
        '<strong>' . $this->t('⚠️ Security Notice:') . '</strong> ' .
        $this->t('Credentials are stored <strong>unencrypted</strong> in Drupal\'s configuration. Ensure config exports and database backups are properly secured. For enhanced security, consider using OAuth2 authentication instead.') .
        '</div>' .
        '<h4>' . $this->t('Authentication Methods') . '</h4>' .
        '<p><strong>' . $this->t('Basic Authentication:') . '</strong> ' .
        $this->t('Credentials are stored in Drupal\'s configuration system. Use NextCloud app passwords for better security.') .
        '</p>' .
        '<p><strong>' . $this->t('OAuth2 Authentication (Recommended):') . '</strong> ' .
        $this->t('Uses secure token-based authentication. Tokens are automatically refreshed. Provides better security than storing passwords.') .
        '</p>' .
        '<h4>' . $this->t('How to create NextCloud App Password (for Basic Auth):') . '</h4>' .
        '<ol>' .
        '<li>' . $this->t('Log into your NextCloud web interface') . '</li>' .
        '<li>' . $this->t('Go to Settings > Personal > Security') . '</li>' .
        '<li>' . $this->t('Scroll to "App passwords" section') . '</li>' .
        '<li>' . $this->t('Enter a name (e.g., "Drupal WebDAV") and click "Create new app password"') . '</li>' .
        '<li>' . $this->t('Copy the generated password and use it here') . '</li>' .
        '</ol>' .
        '</div>',
    ];

    // Connection Test.
    $form['test'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('Connection Test'),
      '#collapsible' => FALSE,
    ];

    $form['test']['test_connection'] = [
      '#type' => 'button',
      '#value' => $this->t('Test Connection'),
      '#ajax' => [
        'callback' => '::testConnectionCallback',
        'wrapper' => 'test-result',
        'method' => 'replace',
        'effect' => 'fade',
      ],
    ];

    $form['test']['test_result'] = [
      '#type' => 'markup',
      '#markup' => '',
      '#prefix' => '<div id="test-result">',
      '#suffix' => '</div>',
    ];

    return parent::buildForm($form, $form_state);
  }

  /**
   * Ajax callback for testing the connection.
   */
  public function testConnectionCallback(array &$form, FormStateInterface $form_state) {
    // Get form values
    $test_password = $form_state->getValue('password');
    $existing_password = $this->config('nextcloud_webdav_client.settings')->get('password');

    // Use existing password if no new password provided
    $password_to_test = !empty($test_password) ? $test_password : $existing_password;

    // Temporarily update config with form values for testing.
    $temp_config = [
      'auth_mode' => $form_state->getValue('auth_mode'),
      'server_url' => $form_state->getValue('server_url'),
      'username' => $form_state->getValue('username'),
      'password' => $password_to_test,
      'base_path' => $form_state->getValue('base_path'),
      'timeout' => $form_state->getValue('timeout'),
      'verify_ssl' => $form_state->getValue('verify_ssl'),
      'oauth2_username' => $form_state->getValue('oauth2_username'),
    ];

    // Save current config.
    $current_config = $this->config('nextcloud_webdav_client.settings');
    $original_values = [];
    foreach ($temp_config as $key => $value) {
      $original_values[$key] = $current_config->get($key);
    }

    // Set temporary values.
    $config = $this->configFactory->getEditable('nextcloud_webdav_client.settings');
    foreach ($temp_config as $key => $value) {
      $config->set($key, $value);
    }
    $config->save();

    // Test connection.
    $success = $this->webdavClient->testConnection();

    // Restore original values.
    foreach ($original_values as $key => $value) {
      $config->set($key, $value);
    }
    $config->save();

    // Prepare result message.
    if ($success) {
      $message = [
        '#type' => 'markup',
        '#markup' => '<div class="messages messages--status">' . $this->t('Connection successful!') . '</div>',
      ];
    }
    else {
      $message = [
        '#type' => 'markup',
        '#markup' => '<div class="messages messages--error">' . $this->t('Connection failed. Please check your settings.') . '</div>',
      ];
    }

    $form['test']['test_result'] = $message + [
      '#prefix' => '<div id="test-result">',
      '#suffix' => '</div>',
    ];

    return $form['test']['test_result'];
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $config = $this->configFactory->getEditable('nextcloud_webdav_client.settings');
    $auth_mode = $form_state->getValue('auth_mode');

    // Save authentication mode.
    $config->set('auth_mode', $auth_mode);

    // Save common settings.
    $config
      ->set('server_url', $form_state->getValue('server_url'))
      ->set('base_path', $form_state->getValue('base_path'))
      ->set('timeout', $form_state->getValue('timeout'))
      ->set('verify_ssl', $form_state->getValue('verify_ssl'));

    // Save authentication-specific settings.
    if ($auth_mode === 'basic') {
      // Handle Basic Auth credentials.
      $new_password = $form_state->getValue('password');
      $existing_password = $config->get('password');
      $password_to_save = !empty($new_password) ? $new_password : $existing_password;

      $config
        ->set('username', $form_state->getValue('username'))
        ->set('password', $password_to_save);

      $config->save();

      // Add message about password handling.
      if (empty($new_password) && !empty($existing_password)) {
        $this->messenger()->addStatus($this->t('Settings saved. The existing password was kept unchanged.'));
      }
      else {
        $this->messenger()->addStatus($this->t('Settings saved successfully.'));
      }
    }
    else {
      // Handle OAuth2 configuration.
      $new_client_secret = $form_state->getValue('oauth2_client_secret');
      $existing_client_secret = $config->get('oauth2_client_secret');
      $client_secret_to_save = !empty($new_client_secret) ? $new_client_secret : $existing_client_secret;

      $config
        ->set('oauth2_client_id', $form_state->getValue('oauth2_client_id'))
        ->set('oauth2_client_secret', $client_secret_to_save)
        ->set('oauth2_authorize_endpoint', $form_state->getValue('oauth2_authorize_endpoint'))
        ->set('oauth2_token_endpoint', $form_state->getValue('oauth2_token_endpoint'))
        ->set('oauth2_userinfo_endpoint', $form_state->getValue('oauth2_userinfo_endpoint'))
        ->set('oauth2_scopes', $form_state->getValue('oauth2_scopes'))
        ->set('oauth2_mode', $form_state->getValue('oauth2_mode'))
        ->set('oauth2_username', $form_state->getValue('oauth2_username'))
        ->set('oauth2_use_index_php', $form_state->getValue('oauth2_use_index_php'));

      $config->save();

      // Save the OAuth2 user identifier setting to the OAuth2 Server config.
      $user_identifier = $form_state->getValue('oauth2_user_identifier');
      if ($user_identifier) {
        $oauth2_server_config = $this->configFactory->getEditable('oauth2_server.oauth');
        $oauth2_server_config->set('user_sub_property', $user_identifier)->save();
      }

      if ($this->oauth2Manager->isConfigured()) {
        $this->messenger()->addStatus($this->t('OAuth2 configuration saved successfully. You can now authorize with NextCloud.'));
      }
      else {
        $this->messenger()->addStatus($this->t('Settings saved successfully.'));
      }
    }
  }

} 

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc