<?php

/**
 * @file
 * Install, update and uninstall functions for the image module.
 */

/**
 * Implements hook_install().
 */
function image_install() {
  // Create the styles directory and ensure it's writable.
  $directory = file_default_scheme() . '://styles';
  file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
}

/**
 * Implements hook_uninstall().
 */
function image_uninstall() {
  // Remove the styles directory and generated images.
  file_unmanaged_delete_recursive(file_default_scheme() . '://styles');
}

/**
 * Implements hook_schema().
 */
function image_schema() {
  $schema = array();

  $schema['cache_image'] = drupal_get_schema_unprocessed('system', 'cache');
  $schema['cache_image']['description'] = 'Cache table used to store information about image manipulations that are in-progress.';

  $schema['image_styles'] = array(
    'description' => 'Stores configuration options for image styles.',
    'fields' => array(
      'isid' => array(
        'description' => 'The primary identifier for an image style.',
        'type' => 'serial',
        'unsigned' => TRUE,
        'not null' => TRUE,
      ),
      'name' => array(
        'description' => 'The style name.',
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
      ),
    ),
    'primary key' => array('isid'),
    'unique keys' => array(
      'name' => array('name'),
    ),
  );

  $schema['image_effects'] = array(
    'description' => 'Stores configuration options for image effects.',
    'fields' => array(
      'ieid' => array(
        'description' => 'The primary identifier for an image effect.',
        'type' => 'serial',
        'unsigned' => TRUE,
        'not null' => TRUE,
      ),
      'isid' => array(
        'description' => 'The {image_styles}.isid for an image style.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
      'weight' => array(
        'description' => 'The weight of the effect in the style.',
        'type' => 'int',
        'unsigned' => FALSE,
        'not null' => TRUE,
        'default' => 0,
      ),
      'name' => array(
        'description' => 'The unique name of the effect to be executed.',
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
      ),
      'data' => array(
        'description' => 'The configuration data for the effect.',
        'type' => 'blob',
        'not null' => TRUE,
        'size' => 'big',
        'serialize' => TRUE,
      ),
    ),
    'primary key' => array('ieid'),
    'indexes' => array(
      'isid' => array('isid'),
      'weight' => array('weight'),
    ),
    'foreign keys' => array(
      'image_style' => array(
        'table' => 'image_styles',
        'columns' => array('isid' => 'isid'),
      ),
    ),
  );

  return $schema;
}

/**
 * Implements hook_field_schema().
 */
function image_field_schema($field) {
  return array(
    'columns' => array(
      'fid' => array(
        'description' => 'The {file_managed}.fid being referenced in this field.',
        'type' => 'int',
        'not null' => FALSE,
        'unsigned' => TRUE,
      ),
      'alt' => array(
        'description' => "Alternative image text, for the image's 'alt' attribute.",
        'type' => 'varchar',
        'length' => 512,
        'not null' => FALSE,
      ),
      'title' => array(
        'description' => "Image title text, for the image's 'title' attribute.",
        'type' => 'varchar',
        'length' => 1024,
        'not null' => FALSE,
      ),
      'width' => array(
        'description' => 'The width of the image in pixels.',
        'type' => 'int',
        'unsigned' => TRUE,
      ),
      'height' => array(
        'description' => 'The height of the image in pixels.',
        'type' => 'int',
        'unsigned' => TRUE,
      ),
    ),
    'indexes' => array(
      'fid' => array('fid'),
    ),
    'foreign keys' => array(
      'fid' => array(
        'table' => 'file_managed',
        'columns' => array('fid' => 'fid'),
      ),
    ),
  );
}

/**
 * Implements hook_update_dependencies().
 */
function image_update_dependencies() {
  $dependencies['image'][7002] = array(
    // Image update 7002 uses field API functions, so must run after
    // Field API has been enabled.
    'system' => 7020,
  );
  return $dependencies;
}

/**
 * Install the schema for users upgrading from the contributed module.
 */
function image_update_7000() {
  if (!db_table_exists('image_styles')) {
    $schema = array();

    $schema['cache_image'] = system_schema_cache_7054();
    $schema['cache_image']['description'] = 'Cache table used to store information about image manipulations that are in-progress.';

    $schema['image_styles'] = array(
      'description' => 'Stores configuration options for image styles.',
      'fields' => array(
        'isid' => array(
          'description' => 'The primary identifier for an image style.',
          'type' => 'serial',
          'unsigned' => TRUE,
          'not null' => TRUE,
        ),
        'name' => array(
          'description' => 'The style name.',
          'type' => 'varchar',
          'length' => 255,
          'not null' => TRUE,
        ),
      ),
      'primary key' => array('isid'),
      'unique keys' => array(
        'name' => array('name'),
      ),
    );

    $schema['image_effects'] = array(
      'description' => 'Stores configuration options for image effects.',
      'fields' => array(
        'ieid' => array(
          'description' => 'The primary identifier for an image effect.',
          'type' => 'serial',
          'unsigned' => TRUE,
          'not null' => TRUE,
        ),
        'isid' => array(
          'description' => 'The {image_styles}.isid for an image style.',
          'type' => 'int',
          'unsigned' => TRUE,
          'not null' => TRUE,
          'default' => 0,
        ),
        'weight' => array(
          'description' => 'The weight of the effect in the style.',
          'type' => 'int',
          'unsigned' => FALSE,
          'not null' => TRUE,
          'default' => 0,
        ),
        'name' => array(
          'description' => 'The unique name of the effect to be executed.',
          'type' => 'varchar',
          'length' => 255,
          'not null' => TRUE,
        ),
        'data' => array(
          'description' => 'The configuration data for the effect.',
          'type' => 'blob',
          'not null' => TRUE,
          'size' => 'big',
          'serialize' => TRUE,
        ),
      ),
      'primary key' => array('ieid'),
      'indexes' => array(
        'isid' => array('isid'),
        'weight' => array('weight'),
      ),
      'foreign keys' => array(
        'image_style' => array(
          'table' => 'image_styles',
          'columns' => array('isid' => 'isid'),
        ),
      ),
    );

    db_create_table('cache_image', $schema['cache_image']);
    db_create_table('image_styles', $schema['image_styles']);
    db_create_table('image_effects', $schema['image_effects']);
  }
}

/**
 * @addtogroup updates-7.x-extra
 * @{
 */

/**
 * Rename possibly misnamed {image_effect} table to {image_effects}.
 */
function image_update_7001() {
  // Due to a bug in earlier versions of image_update_7000() it is possible
  // to end up with an {image_effect} table where there should be an
  // {image_effects} table.
  if (!db_table_exists('image_effects') && db_table_exists('image_effect')) {
    db_rename_table('image_effect', 'image_effects');
  }
}

/**
 * Add width and height columns to a specific table.
 *
 * @param $table
 *   The name of the database table to be updated.
 * @param $columns
 *   Keyed array of columns this table is supposed to have.
 */
function _image_update_7002_add_columns($table, $field_name) {
  $spec = array(
    'type' => 'int',
    'unsigned' => TRUE,
  );

  $spec['description'] = 'The width of the image in pixels.';
  db_add_field($table, $field_name . '_width', $spec);

  $spec['description'] = 'The height of the image in pixels.';
  db_add_field($table, $field_name . '_height', $spec);
}

/**
 * Populate image dimensions in a specific table.
 *
 * @param $table
 *   The name of the database table to be updated.
 * @param $columns
 *   Keyed array of columns this table is supposed to have.
 * @param $last_fid
 *   The fid of the last image to have been processed.
 *
 * @return
 *   The number of images that were processed.
 */
function _image_update_7002_populate_dimensions($table, $field_name, &$last_fid) {
  // Define how many images to process per pass.
  $images_per_pass = 100;

  // Query the database for fid / URI pairs.
  $query = db_select($table, NULL, array('fetch' => PDO::FETCH_ASSOC));
  $query->join('file_managed', NULL, $table . '.' . $field_name . '_fid = file_managed.fid');

  if ($last_fid) {
    $query->condition('file_managed.fid', $last_fid, '>');
  }

  $result = $query->fields('file_managed', array('fid', 'uri'))
    ->orderBy('file_managed.fid')
    ->range(0, $images_per_pass)
    ->execute();

  $count = 0;
  foreach ($result as $file) {
    $count++;
    $info = image_get_info($file['uri']);

    if (is_array($info)) {
      db_update($table)
        ->fields(array(
          $field_name . '_width' => $info['width'],
          $field_name . '_height' => $info['height'],
        ))
        ->condition($field_name . '_fid', $file['fid'])
        ->execute();
    }
  }

  // If less than the requested number of rows were returned then this table
  // has been fully processed.
  $last_fid = ($count < $images_per_pass) ? NULL : $file['fid'];
  return $count;
}

/**
 * Add width and height columns to image field schema and populate.
 */
function image_update_7002(array &$sandbox) {
  if (empty($sandbox)) {
    // Setup the sandbox.
    $sandbox = array(
      'tables' => array(),
      'total' => 0,
      'processed' => 0,
      'last_fid' => NULL,
    );

    $fields = _update_7000_field_read_fields(array(
      'module' => 'image',
      'storage_type' => 'field_sql_storage',
      'deleted' => 0,
    ));

    foreach ($fields as $field) {
      $tables = array(
        _field_sql_storage_tablename($field),
        _field_sql_storage_revision_tablename($field),
      );
      foreach ($tables as $table) {
        // Add the width and height columns to the table.
        _image_update_7002_add_columns($table, $field['field_name']);

        // How many rows need dimensions populated?
        $count = db_select($table)->countQuery()->execute()->fetchField();

        if (!$count) {
          continue;
        }

        $sandbox['total'] += $count;
        $sandbox['tables'][$table] = $field['field_name'];
      }
    }

    // If no tables need rows populated with dimensions then we are done.
    if (empty($sandbox['tables'])) {
      $sandbox = array();
      return;
    }
  }

  // Process the table at the top of the list.
  $keys = array_keys($sandbox['tables']);
  $table = reset($keys);
  $sandbox['processed'] += _image_update_7002_populate_dimensions($table, $sandbox['tables'][$table], $sandbox['last_fid']);

  // Has the table been fully processed?
  if (!$sandbox['last_fid']) {
    unset($sandbox['tables'][$table]);
  }

  $sandbox['#finished'] = count($sandbox['tables']) ? ($sandbox['processed'] / $sandbox['total']) : 1;
}

/**
 * Remove the variables that set alt and title length since they were not
 * used for database column size and could cause PDO exceptions.
 */
function image_update_7003() {
  variable_del('image_alt_length');
  variable_del('image_title_length');
}

/**
 * Use a large setting (512 and 1024 characters) for the length of the image alt
 * and title fields.
 */
function image_update_7004() {
  $alt_spec = array(
    'type' => 'varchar',
    'length' => 512,
    'not null' => FALSE,
  );

  $title_spec = array(
    'type' => 'varchar',
    'length' => 1024,
    'not null' => FALSE,
  );

  $fields = _update_7000_field_read_fields(array(
    'module' => 'image',
    'storage_type' => 'field_sql_storage',
  ));

  foreach ($fields as $field_name => $field) {
    $tables = array(
      _field_sql_storage_tablename($field),
      _field_sql_storage_revision_tablename($field),
    );
    $alt_column = $field['field_name'] . '_alt';
    $title_column = $field['field_name'] . '_title';
    foreach ($tables as $table) {
      db_change_field($table, $alt_column, $alt_column, $alt_spec);
      db_change_field($table, $title_column, $title_column, $title_spec);
    }
  }
}

/**
 * @} End of "addtogroup updates-7.x-extra".
 */

/**
 * Implements hook_requirements() to check the PHP GD Library.
 *
 * @param $phase
 */
function image_requirements($phase) {
  $requirements = array();

  if ($phase == 'runtime') {
    // Check for the PHP GD library.
    if (function_exists('imagegd2')) {
      $info = gd_info();
      $requirements['image_gd'] = array(
        'value' => $info['GD Version'],
      );

      // Check for filter and rotate support.
      if (function_exists('imagefilter') && function_exists('imagerotate')) {
        $requirements['image_gd']['severity'] = REQUIREMENT_OK;
      }
      else {
        $requirements['image_gd']['severity'] = REQUIREMENT_WARNING;
        $requirements['image_gd']['description'] = t('The GD Library for PHP is enabled, but was compiled without support for functions used by the rotate and desaturate effects. It was probably compiled using the official GD libraries from http://www.libgd.org instead of the GD library bundled with PHP. You should recompile PHP --with-gd using the bundled GD library. See <a href="http://www.php.net/manual/book.image.php">the PHP manual</a>.');
      }
    }
    else {
      $requirements['image_gd'] = array(
        'value' => t('Not installed'),
        'severity' => REQUIREMENT_ERROR,
        'description' => t('The GD library for PHP is missing or outdated. Check the <a href="@url">PHP image documentation</a> for information on how to correct this.', array('@url' => 'http://www.php.net/manual/book.image.php')),
      );
    }
    $requirements['image_gd']['title'] = t('GD library rotate and desaturate effects');
  }

  return $requirements;
}
