utilikit-1.0.0/utilikit.install

utilikit.install
<?php

/**
 * @file
 * Installation, update, and uninstall functions for the UtiliKit module.
 *
 * This file handles the complete lifecycle of the UtiliKit module including:
 * - Initial installation with optimal default configurations
 * - File system setup and directory creation
 * - Permission assignment for administrative roles
 * - Complete cleanup during uninstallation
 * - Runtime requirements validation and monitoring
 * - Performance optimization recommendations.
 *
 * The installation process establishes a production-ready configuration
 * with security-focused defaults, while the uninstallation process ensures
 * complete removal of all module data, files, and configuration.
 */

use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Url;
use Drupal\utilikit\Service\UtilikitConstants;
use Drupal\Core\StringTranslation\ByteSizeMarkup;

/**
 * Implements hook_install().
 *
 * Performs initial setup and configuration for the UtiliKit module including:
 * - Creates necessary file system directories with proper permissions
 * - Establishes comprehensive default configuration optimized for production
 * - Sets up security-focused defaults for rendering modes and scope
 * - Configures performance settings for optimal operation
 * - Assigns essential permissions to administrator role
 * - Provides user feedback and guidance for next steps.
 *
 * The default configuration prioritizes security and performance with
 * inline rendering mode, global scope disabled on admin routes, and
 * conservative settings for rate limiting and batch processing.
 */
function utilikit_install() {
  // Create CSS directory using constants.
  $directory = UtilikitConstants::CSS_DIRECTORY;
  $file_system = \Drupal::service('file_system');

  if (!$file_system->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS)) {
    \Drupal::messenger()->addWarning(t('Could not create UtiliKit CSS directory. Please check file permissions.'));
  }

  // Set comprehensive default configuration.
  $config = \Drupal::configFactory()->getEditable('utilikit.settings');

  // Core settings - prioritize security and stability.
  // Safe default for initial setup.
  $config->set('rendering_mode', 'inline');
  // Enable globally for immediate functionality.
  $config->set('scope_global', TRUE);
  // Allow admin route usage for configuration.
  $config->set('disable_admin', FALSE);
  // Global scope by default.
  $config->set('scope_content_types', FALSE);
  // Empty until user configures.
  $config->set('enabled_content_types', []);

  // Performance settings - balanced for development and production.
  // Enhanced UX with CSS transitions.
  $config->set('enable_transitions', TRUE);
  // Responsive debouncing for performance.
  $config->set('debounce', 50);
  // Enable CSS optimization by default.
  $config->set('optimize_css', TRUE);
  // Use Important by default.
  $config->set('use_important', TRUE);
  // Standard responsive breakpoints.
  $config->set('active_breakpoints', UtilikitConstants::DEFAULT_BREAKPOINTS);

  // Developer settings - conservative defaults for production safety.
  // Production-safe default.
  $config->set('dev_mode', FALSE);
  // Disable preview until configured.
  $config->set('admin_preview', FALSE);
  // Hide errors from end users.
  $config->set('show_page_errors', FALSE);
  // Balanced logging for monitoring.
  $config->set('log_level', 'warnings');

  // Update triggers - disabled by default to prevent unexpected behavior.
  // Manual control initially.
  $config->set('update_on_node_save', FALSE);
  // Manual control initially.
  $config->set('update_on_block_save', FALSE);
  // Manual control initially.
  $config->set('update_on_paragraph_save', FALSE);

  // Advanced settings - performance and security focused defaults.
  // Optimal batch processing.
  $config->set('batch_size', UtilikitConstants::BATCH_SIZE_DEFAULT);
  // Rate limiting protection.
  $config->set('max_classes_per_request', UtilikitConstants::MAX_CLASSES_PER_REQUEST);
  // AJAX protection.
  $config->set('rate_limit_requests', UtilikitConstants::RATE_LIMIT_REQUESTS_PER_MINUTE);
  // 1-hour cache TTL for balance
  $config->set('css_cache_ttl', 3600);

  // File management - using centralized constants for consistency.
  $config->set('css_directory', UtilikitConstants::CSS_DIRECTORY);
  $config->set('css_filename', UtilikitConstants::CSS_FILENAME);

  // Migration flags - indicate fresh installation.
  $config->set('legacy_mode', FALSE);
  $config->set('migration_complete', TRUE);

  // Experimental features (all disabled by default for stability).
  $config->set('experimental_features', [
  // Requires additional testing.
    'grid_auto_generation' => FALSE,
  // May impact performance.
    'advanced_selectors' => FALSE,
  // Browser compatibility considerations.
    'css_variables' => FALSE,
  // Complex optimization feature.
    'media_query_optimization' => FALSE,
  ]);

  // Add default scanning entity types.
  $config->set('scanning_entity_types', [
    'node',
    'block_content',
    'paragraph',
  ]);

  $config->save();

  \Drupal::messenger()->addStatus(t('UtiliKit has been installed with optimal defaults. Visit the <a href="@url">settings page</a> to configure it.', [
    '@url' => Url::fromRoute('utilikit.settings')->toString(),
  ]));

  // Grant essential permissions to administrator role.
  $admin_role = \Drupal::entityTypeManager()->getStorage('user_role')->load('administrator');
  if ($admin_role) {
    $admin_role->grantPermission('administer utilikit');
    $admin_role->grantPermission('use utilikit playground');
    $admin_role->grantPermission('run utilikit tests');
    $admin_role->save();
  }

}

/**
 * Implements hook_uninstall().
 *
 * Performs complete cleanup and removal of all UtiliKit module data including:
 * - Deletes all configuration settings and stored preferences
 * - Removes all state data including generated CSS and known classes
 * - Cleans up rate limiting data from the database
 * - Invalidates all related cache tags for immediate effect
 * - Removes all CSS files and directories from the file system
 * - Attempts cleanup of empty parent directories
 * - Provides comprehensive error handling and logging.
 *
 * The uninstall process follows a specific order to prevent race conditions
 * and ensure complete cleanup even if individual steps fail. All operations
 * are logged for troubleshooting and verification.
 */
function utilikit_uninstall() {
  // Step 1: Delete configuration FIRST to prevent any file recreation.
  \Drupal::configFactory()->getEditable('utilikit.settings')->delete();

  // Step 2: Clear state variables using constants.
  $state = \Drupal::state();
  $state->deleteMultiple([
    UtilikitConstants::STATE_GENERATED_CSS,
    UtilikitConstants::STATE_KNOWN_CLASSES,
    UtilikitConstants::STATE_LAST_CLEANUP,
    UtilikitConstants::STATE_CSS_TIMESTAMP,
    'utilikit.initialized',
  ]);

  // Step 3: Remove rate limit keys from database.
  $connection = \Drupal::database();
  try {
    $connection->delete('key_value')
      ->condition('name', 'utilikit_rate_limit:%', 'LIKE')
      ->condition('collection', 'state')
      ->execute();
  }
  catch (\Exception $e) {
    // Database might not be available during uninstall.
  }

  // Step 4: Invalidate cache tags using constants.
  \Drupal::service('cache_tags.invalidator')->invalidateTags([
    UtilikitConstants::CACHE_TAG_CONFIG,
    UtilikitConstants::CACHE_TAG_CSS,
    UtilikitConstants::CACHE_TAG_INLINE_MODE,
    UtilikitConstants::CACHE_TAG_STATIC_MODE,
    UtilikitConstants::CACHE_TAG_HEAD_MODE,
  ]);

  // Step 5: Delete CSS files and directories using constants.
  $file_system = \Drupal::service('file_system');

  try {
    $utilikit_directory = UtilikitConstants::CSS_DIRECTORY;
    if (is_dir($file_system->realpath($utilikit_directory))) {
      $file_system->deleteRecursive($utilikit_directory);
    }

    // Try to remove parent css directory if empty.
    $parent_directory = 'public://css';
    $parent_real = $file_system->realpath($parent_directory);
    if (is_dir($parent_real)) {
      $files = @scandir($parent_real);
      if (is_array($files) && count($files) <= 2) {
        @rmdir($parent_real);
      }
    }
  }
  catch (\Exception $e) {
    \Drupal::logger('utilikit')->error('Failed to delete CSS files during uninstall: @error', [
      '@error' => $e->getMessage(),
    ]);
  }

  \Drupal::logger('utilikit')->notice('UtiliKit uninstalled and all files cleaned up.');
}

/**
 * Implements hook_requirements().
 *
 * Validates system requirements and configuration for UtiliKit at runtime.
 * Checks include file permissions, static-mode configuration, performance
 * recommendations, and security-related configuration.
 *
 * @see hook_requirements()
 */
function utilikit_requirements($phase): array {
  $requirements = [];

  if ($phase === 'runtime') {
    $file_system = \Drupal::service('file_system');
    $config = \Drupal::config('utilikit.settings');
    $rendering_mode = $config->get('rendering_mode') ?? 'inline';
    $directory = UtilikitConstants::CSS_DIRECTORY;
    $real_path = $file_system->realpath($directory);
    $scanning_types = $config->get('scanning_entity_types') ?? [];

    $requirements['utilikit_scanning'] = [
      'title' => t('UtiliKit scanning scope'),
      'value' => t('@count entity types', ['@count' => count($scanning_types)]),
      'severity' => REQUIREMENT_INFO,
      'description' => t('Scanning: @types', ['@types' => implode(', ', $scanning_types)]),
    ];

    // Check CSS directory - severity depends on rendering mode.
    if (!$real_path || !is_dir($real_path)) {
      // Directory doesn't exist.
      if ($rendering_mode === 'static') {
        // ERROR in static mode - directory is required.
        $requirements['utilikit_directory'] = [
          'title' => t('UtiliKit CSS directory'),
          'value' => t('Directory does not exist'),
          'severity' => REQUIREMENT_ERROR,
          'description' => t('The UtiliKit CSS directory at %path does not exist. This is required for static mode. Please create it and ensure it is writable, or switch to inline/head mode.', [
            '%path' => $directory,
          ]),
        ];
      }
      else {
        // WARNING in inline/head mode - directory not needed but should exist.
        $requirements['utilikit_directory'] = [
          'title' => t('UtiliKit CSS directory'),
          'value' => t('Directory does not exist'),
          'severity' => REQUIREMENT_WARNING,
          'description' => t('The UtiliKit CSS directory at %path does not exist. This is normal for inline/head mode. The directory will be created automatically if you switch to static mode.', [
            '%path' => $directory,
          ]),
        ];
      }
    }
    elseif (!is_writable($real_path)) {
      // Directory exists but not writable.
      if ($rendering_mode === 'static') {
        // ERROR in static mode - must be writable.
        $requirements['utilikit_directory'] = [
          'title' => t('UtiliKit CSS directory'),
          'value' => t('Not writable'),
          'severity' => REQUIREMENT_ERROR,
          'description' => t('The UtiliKit CSS directory at %path is not writable. This is required for static mode.', [
            '%path' => $directory,
          ]),
        ];
      }
      else {
        // WARNING in inline/head mode.
        $requirements['utilikit_directory'] = [
          'title' => t('UtiliKit CSS directory'),
          'value' => t('Not writable'),
          'severity' => REQUIREMENT_WARNING,
          'description' => t('The UtiliKit CSS directory at %path exists but is not writable. This will cause issues if you switch to static mode.', [
            '%path' => $directory,
          ]),
        ];
      }
    }
    else {
      // Directory exists and is writable - all good.
      $requirements['utilikit_directory'] = [
        'title' => t('UtiliKit CSS directory'),
        'value' => t('Exists and writable'),
        'severity' => REQUIREMENT_OK,
      ];
    }

    // Check if static mode is enabled and validate CSS file existence.
    if ($rendering_mode === 'static') {
      $css_file = UtilikitConstants::CSS_DIRECTORY . '/' . UtilikitConstants::CSS_FILENAME;
      $css_real_path = $file_system->realpath($css_file);

      if (!$css_real_path || !file_exists($css_real_path)) {
        $requirements['utilikit_static_css'] = [
          'title' => t('UtiliKit static CSS'),
          'value' => t('File missing'),
          'severity' => REQUIREMENT_WARNING,
          'description' => t('Static mode is enabled but the CSS file does not exist. Visit the <a href="@url">settings page</a> to generate it.', [
            '@url' => Url::fromRoute('utilikit.settings')->toString(),
          ]),
        ];
      }
      else {
        // Show file info if it exists.
        $file_size = filesize($css_real_path);
        $known_classes = \Drupal::state()->get(UtilikitConstants::STATE_KNOWN_CLASSES, []);
        $requirements['utilikit_static_css'] = [
          'title' => t('UtiliKit static CSS'),
          'value' => t('File exists (@size, @count classes)', [
            '@size' => (string) ByteSizeMarkup::create((int) $file_size),
            '@count' => count($known_classes),
          ]),
          'severity' => REQUIREMENT_OK,
        ];
      }
    }

    // Validate overall configuration status.
    $mode_labels = [
      'static' => t('Static'),
      'inline' => t('Inline'),
      'head' => t('Head'),
    ];
    $requirements['utilikit_config'] = [
      'title' => t('UtiliKit configuration'),
      'value' => t('Mode: @mode', [
        '@mode' => $mode_labels[$rendering_mode] ?? t('Unknown'),
      ]),
      'severity' => REQUIREMENT_OK,
      'description' => t('UtiliKit is running in @mode mode.', [
        '@mode' => $mode_labels[$rendering_mode] ?? t('unknown'),
      ]),
    ];

    // Monitor performance and provide optimization recommendations.
    $known_classes = \Drupal::state()->get(UtilikitConstants::STATE_KNOWN_CLASSES, []);
    if (count($known_classes) > UtilikitConstants::MAX_CLASSES_WARNING_THRESHOLD) {
      $requirements['utilikit_performance'] = [
        'title' => t('UtiliKit performance'),
        'value' => t('Large number of classes detected'),
        'severity' => REQUIREMENT_WARNING,
        'description' => t('You have @count utility classes tracked. Consider cleaning up unused classes for optimal performance.', [
          '@count' => count($known_classes),
        ]),
      ];
    }
  }

  return $requirements;
}

/**
 * Add scanning entity types configuration.
 */
function utilikit_update_10001(): void {
  $config = \Drupal::configFactory()->getEditable('utilikit.settings');

  // Add scanning entity types if not already set.
  if ($config->get('scanning_entity_types') === NULL) {
    $config->set('scanning_entity_types', [
      'node',
      'block_content',
      'paragraph',
    ]);
    $config->save();
  }
}

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

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