diff options
Diffstat (limited to 'sites/all/modules/views_bulk_operations/actions/archive.action.inc')
-rw-r--r-- | sites/all/modules/views_bulk_operations/actions/archive.action.inc | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/sites/all/modules/views_bulk_operations/actions/archive.action.inc b/sites/all/modules/views_bulk_operations/actions/archive.action.inc new file mode 100644 index 000000000..f00552743 --- /dev/null +++ b/sites/all/modules/views_bulk_operations/actions/archive.action.inc @@ -0,0 +1,193 @@ +<?php + +/** + * @file + * Provides an action for creating a zip archive of selected files. + * An entry in the {file_managed} table is created for the newly created archive, + * and it is marked as permanent or temporary based on the operation settings. + */ + +function views_bulk_operations_archive_action_info() { + $actions = array(); + if (function_exists('zip_open')) { + $actions['views_bulk_operations_archive_action'] = array( + 'type' => 'file', + 'label' => t('Create an archive of selected files'), + // This action only works when invoked through VBO. That's why it's + // declared as non-configurable to prevent it from being shown in the + // "Create an advanced action" dropdown on admin/config/system/actions. + 'configurable' => FALSE, + 'vbo_configurable' => TRUE, + 'behavior' => array('views_property'), + 'triggers' => array('any'), + ); + } + return $actions; +} + +/** + * Since Drupal's Archiver doesn't abstract properly the archivers it implements + * (Archive_Tar and ZipArchive), it can't be used here. + */ +function views_bulk_operations_archive_action($file, $context) { + global $user; + static $archive_contents = array(); + + // Adding a non-existent file to the archive crashes ZipArchive on close(). + if (file_exists($file->uri)) { + $destination = $context['destination']; + $zip = new ZipArchive(); + // If the archive already exists, open it. If not, create it. + if (file_exists($destination)) { + $opened = $zip->open(drupal_realpath($destination)); + } + else { + $opened = $zip->open(drupal_realpath($destination), ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE); + } + + if ($opened) { + // Create a list of all files in the archive. Used for duplicate checking. + if (empty($archive_contents)) { + for ($i = 0; $i < $zip->numFiles; $i++) { + $archive_contents[] = $zip->getNameIndex($i); + } + } + // Make sure that the target filename is unique. + $filename = _views_bulk_operations_archive_action_create_filename(basename($file->uri), $archive_contents); + // Note that the actual addition happens on close(), hence the need + // to open / close the archive each time the action runs. + $zip->addFile(drupal_realpath($file->uri), $filename); + $zip->close(); + $archive_contents[] = $filename; + } + } + + // The operation is complete, create a file entity and provide a download + // link to the user. + if ($context['progress']['current'] == $context['progress']['total']) { + $archive_file = new stdClass(); + $archive_file->uri = $destination; + $archive_file->filename = basename($destination); + $archive_file->filemime = file_get_mimetype($destination); + $archive_file->uid = $user->uid; + $archive_file->status = $context['settings']['temporary'] ? FALSE : FILE_STATUS_PERMANENT; + file_save($archive_file); + + $url = file_create_url($archive_file->uri); + $url = l($url, $url, array('absolute' => TRUE)); + _views_bulk_operations_log(t('An archive has been created and can be downloaded from: !url', array('!url' => $url))); + } +} + +/** + * Configuration form shown to the user before the action gets executed. + */ +function views_bulk_operations_archive_action_form($context) { + // Pass the scheme as a value, so that the submit callback can access it. + $form['scheme'] = array( + '#type' => 'value', + '#value' => $context['settings']['scheme'], + ); + + $form['filename'] = array( + '#type' => 'textfield', + '#title' => t('Filename'), + '#default_value' => 'vbo_archive_' . date('Ymd'), + '#field_suffix' => '.zip', + '#description' => t('The name of the archive file.'), + ); + return $form; +} + +/** + * Assembles a sanitized and unique URI for the archive, and returns it for + * usage by the action callback (views_bulk_operations_archive_action). + */ +function views_bulk_operations_archive_action_submit($form, $form_state) { + // Validate the scheme, fallback to public if it's somehow invalid. + $scheme = $form_state['values']['scheme']; + if (!file_stream_wrapper_valid_scheme($scheme)) { + $scheme = 'public'; + } + $destination = $scheme . '://' . basename($form_state['values']['filename']) . '.zip'; + // If the chosen filename already exists, file_destination() will append + // an integer to it in order to make it unique. + $destination = file_destination($destination, FILE_EXISTS_RENAME); + + return array( + 'destination' => $destination, + ); +} + +/** + * Settings form (embedded into the VBO field settings in the Views UI). + */ +function views_bulk_operations_archive_action_views_bulk_operations_form($options) { + $scheme_options = array(); + foreach (file_get_stream_wrappers(STREAM_WRAPPERS_LOCAL_NORMAL) as $scheme => $stream_wrapper) { + $scheme_options[$scheme] = $stream_wrapper['name']; + } + if (count($scheme_options) > 1) { + $form['scheme'] = array( + '#type' => 'radios', + '#title' => t('Storage'), + '#options' => $scheme_options, + '#default_value' => !empty($options['scheme']) ? $options['scheme'] : variable_get('file_default_scheme', 'public'), + '#description' => t('Select where the archive should be stored. Private file storage has significantly more overhead than public files, but allows restricted access.'), + ); + } + else { + $scheme_option_keys = array_keys($scheme_options); + $form['scheme'] = array( + '#type' => 'value', + '#value' => reset($scheme_option_keys), + ); + } + + $form['temporary'] = array( + '#type' => 'checkbox', + '#title' => t('Temporary'), + '#default_value' => isset($options['temporary']) ? $options['temporary'] : TRUE, + '#description' => t('Temporary files older than 6 hours are removed when cron runs.'), + ); + return $form; +} + +/** + * Create a sanitized and unique version of the provided filename. + * + * @param $filename + * String filename + * + * @return + * The new filename. + */ +function _views_bulk_operations_archive_action_create_filename($filename, $archive_list) { + // Strip control characters (ASCII value < 32). Though these are allowed in + // some filesystems, not many applications handle them well. + $filename = preg_replace('/[\x00-\x1F]/u', '_', $filename); + if (substr(PHP_OS, 0, 3) == 'WIN') { + // These characters are not allowed in Windows filenames + $filename = str_replace(array(':', '*', '?', '"', '<', '>', '|'), '_', $filename); + } + + if (in_array($filename, $archive_list)) { + // Destination file already exists, generate an alternative. + $pos = strrpos($filename, '.'); + if ($pos !== FALSE) { + $name = substr($filename, 0, $pos); + $ext = substr($filename, $pos); + } + else { + $name = $filename; + $ext = ''; + } + + $counter = 0; + do { + $filename = $name . '_' . $counter++ . $ext; + } while (in_array($filename, $archive_list)); + } + + return $filename; +} |