<?php

/**
 * @file
 * Custom action callbacks and configuration forms.
 */

/**
 * Export a node.
 */
function archibald_export_export_node_action($node, $context = array()) {
  $wrapper = file_stream_wrapper_get_instance_by_uri($context['zip_file']->uri);
  $zip = new ZipArchive();
  $error = $zip->open($wrapper->realpath());

  if ($error === TRUE) {
    // Must we add the files? We must treat this first, as the result will
    // modify the exported data.
    if ($context['export_preview_images']) {
      $export_files = array();
      $vids = array();

      // Must we export revisions as well?
      if ($context['export_revisions']) {
        $result = db_select('node_revision', 'v')
                    ->fields('v', array('vid'))
                    ->condition('nid', $node->nid)
                    ->orderBy('vid', 'ASC')
                    ->execute();
        while ($vid = $result->fetchField()) {
          $vids[] = $vid;
        }
      }
      else {
        // Only the latest revision.
        $vids[] = $node->vid;
      }

      foreach ($vids as $vid) {
        // We can safely load revisions here. They are cached, so the load calls
        // below will not affect performance.
        $node_revision = node_load($node->nid, $vid);

        // Fetch the preview image (if there is any).
        if (!empty($node_revision->lomch_preview_image)) {
          if ($export_file = file_load($node_revision->lomch_preview_image[LANGUAGE_NONE][0]['fid'])) {
            $extension = @end(explode('.', $export_file->uri));
            $export_filename = "file--{$export_file->fid}.{$extension}";

            // Don't add the same file twice.
            if (!in_array($export_filename, $export_files)) {
              $export_wrapper = file_stream_wrapper_get_instance_by_uri($export_file->uri);
              $zip->addFile($export_wrapper->realpath(), $export_filename);
            }

            // Link the exported file to the node revision.
            $export_files[$vid] = $export_filename;
          }
        }
      }
    }

    // Prepare the export data.
    $node_data = archibald_json_export($node);

    // If we must export files, we need to alter the preview image.
    if (
      !empty($node_data->technical->previewImage) &&
      $context['export_preview_images'] &&
      !empty($export_files[$node->vid])
    ) {
      $node_data->technical->previewImage->image = "zip://{$export_files[$node->vid]}";
    }

    $zip->addFromString(
      "description--{$node->nid}.json",
      json_encode($node_data, JSON_PRETTY_PRINT)
    );

    // Must we add revision information?
    if ($context['export_revisions']) {
      $revisions = array();
      $result = db_select('node_revision', 'v')
                  ->fields('v', array('vid'))
                  ->condition('nid', $node->nid)
                  ->orderBy('vid', 'ASC')
                  ->execute();
      while ($vid = $result->fetchField()) {
        $node_revision = node_load($node->nid, $vid);

        // Load the author.
        $author = user_load($node_revision->revision_uid);

        // Prepare the export data.
        $revision_data = archibald_json_export($node_revision);

        // If we must export files, we need to alter the preview image.
        if (
          !empty($revision_data->technical->previewImage) &&
          $context['export_preview_images'] &&
          !empty($export_files[$vid])
        ) {
          $revision_data->technical->previewImage->image = "zip://{$export_files[$vid]}";
        }

        // Add the data.
        $revisions[] = array(
          'author' => $author->mail,
          'date' => date('d/m/Y H:i:s', $node_revision->revision_timestamp),
          // Trick to get a full copy.
          'description' => json_decode(json_encode($revision_data)),
        );
      }

      $zip->addFromString(
        "description--{$node->nid}_revisions.json",
        json_encode($revisions, JSON_PRETTY_PRINT)
      );
    }

    // Must we add the meta-data?
    if ($context['export_metadata']) {
      // Load the partner.
      if (isset($node->archibald_publication_partner[LANGUAGE_NONE][0]['target_id'])) {
        $partner = archibald_partner_load($node->archibald_publication_partner[LANGUAGE_NONE][0]['target_id']);
      }

      // Load the author.
      $author = user_load($node->uid);

      // Add a *_meta.json file with this information.
      $zip->addFromString(
        "description--{$node->nid}_meta.json",
        json_encode(array(
          'lomId' => isset($node->lom_id) ? $node->lom_id : NULL,
          'partner' => isset($partner) ? $partner->partner_username[LANGUAGE_NONE][0]['value'] : NULL,
          'author' => $author->mail,
          'catalogs' => isset($node->archibald_publication_catalogs[LANGUAGE_NONE][0]['value']) ?
            $node->archibald_publication_catalogs[LANGUAGE_NONE][0]['value'] :
            NULL
        ), JSON_PRETTY_PRINT)
      );
    }

    $zip->close();

    // If this is the last batch call, or if this is no batch, we put up a
    // message to download the ZIP archive.
    if ($context['progress']['current'] >= $context['progress']['total'] || !isset($context['progress'])) {
      // Update the file. We need to recompute its size, otherwise the download
      // will not work (will corrupt the file).
      $context['zip_file']->filesize = filesize($wrapper->realpath());
      $context['zip_file'] = file_save($context['zip_file']);

      $filename = @end(explode('/', $wrapper->realpath()));

      // Make sure the message is not repeated, as could be the case with VBO.
      drupal_set_message(t("The ZIP archive was successfully created. You can download it here: !link", array(
        '!link' => l($filename, $wrapper->getExternalUrl()),
      )), 'status', FALSE);
    }
  }
  else {
    switch ($error) {
      case ZipArchive::ER_EXISTS:
        $error_value = t("File exists.");
        break;

      case ZipArchive::ER_INCONS:
        $error_value = t("ZIP archive is inconsistent");
        break;

      case ZipArchive::ER_INVAL:
        $error_value = t("Path to ZIP archive is invalid");
        break;

      case ZipArchive::ER_MEMORY:
        $error_value = t("Memory failure");
        break;

      case ZipArchive::ER_NOENT:
        $error_value = t("ZIP archive doesn't exist");
        break;

      case ZipArchive::ER_NOZIP:
        $error_value = t("File exists, but is not a ZIP archive");
        break;

      case ZipArchive::ER_OPEN:
        $error_value = t("File exists, but could not be opened");
        break;

      case ZipArchive::ER_READ:
        $error_value = t("File exists, but could not be read");
        break;

      case ZipArchive::ER_SEEK:
        $error_value = t("Seek error");
        break;

      default:
        $error_value = t("Unknown error");
        break;
    }

    watchdog('archibald_export', "Could not create a ZIP archive. Error: !error", array(
      '!error' => $error_value,
    ), WATCHDOG_ERROR);

    drupal_set_message(t("Could not create ZIP archive for export! Check site logs for more information, or contact your site administrator."), 'error');
  }
}

/**
 * Configuration form for archibald_export_export_node_action().
 *
 * @see archibald_export_export_node_action()
 * @see archibald_export_export_node_action_submit()
 */
function archibald_export_export_node_action_form($context = array()) {
  $form = array();
  $form['export_revisions'] = array(
    '#type' => 'checkbox',
    '#title' => t("Export revisions"),
    '#description' => t("Do you want to export the revision data as well?"),
  );

  $form['export_metadata'] = array(
    '#type' => 'checkbox',
    '#title' => t("Export meta-data"),
    '#description' => t("Do you want to export the meta-data (author information, partner name, LOM ID, catalog, etc)?"),
  );

  $form['export_preview_images'] = array(
    '#type' => 'checkbox',
    '#title' => t("Export preview images"),
    '#description' => t("Do you want to add the preview images to the export?"),
  );

  return $form;
}

/**
 * Submission callback for archibald_export_export_node_action().
 */
function archibald_export_export_node_action_submit($form, $form_state) {
  global $user;

  // Register a temporary file. This will allow us to use Drupal's garbage
  // collection for files. We create this file now, so we can safely use it in
  // our action batch.
  $filename = 'archibald_export--' . uniqid('u' . $user->uid . '_', TRUE) . '.zip';

  // Get the stream wrapper.
  $stream = 'private://';
  if (!$wrapper = file_stream_wrapper_get_instance_by_uri($stream)) {
    $stream = 'public://';
    $wrapper = file_stream_wrapper_get_instance_by_uri($stream);
  }

  // Create a ZIP archive. We add some dummy data, because Drupal can have
  // trouble with empty files.
  $zip = new ZipArchive();
  $zip->open($wrapper->realpath() . '/' . $filename, ZipArchive::CREATE | ZipArchive::OVERWRITE);
  $zip->addFromString('README.txt', sprintf(
    "Exported by archibald_export on %s.",
    date('Y/m/d')
  ));
  $zip->close();

  $file = (object) array(
    'filename' => $filename,
    'uri' => $stream . $filename,
    'uid' => $user->uid,
    'filemime' => file_get_mimetype($stream . $filename),
  );
  $file = file_save($file);

  return array(
    'zip_file' => $file,
    'export_revisions' => $form_state['values']['export_revisions'],
    'export_metadata' => $form_state['values']['export_metadata'],
    'export_preview_images' => $form_state['values']['export_preview_images'],
  );
}
