Projet

Général

Profil

Paste
Télécharger (7,04 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / views_bulk_operations / actions / archive.action.inc @ 7547bb19

1
<?php
2

    
3
/**
4
 * @file
5
 * Provides an action for creating a zip archive of selected files.
6
 *
7
 * An entry in the {file_managed} table is created for the newly created archive,
8
 * and it is marked as permanent or temporary based on the operation settings.
9
 */
10

    
11
/**
12
 * Implements hook_action_info().
13
 */
14
function views_bulk_operations_archive_action_info() {
15
  $actions = array();
16
  if (function_exists('zip_open')) {
17
    $actions['views_bulk_operations_archive_action'] = array(
18
      'type' => 'file',
19
      'label' => t('Create an archive of selected files'),
20
      // This action only works when invoked through VBO. That's why it's
21
      // declared as non-configurable to prevent it from being shown in the
22
      // "Create an advanced action" dropdown on admin/config/system/actions.
23
      'configurable' => FALSE,
24
      'vbo_configurable' => TRUE,
25
      'behavior' => array('views_property'),
26
      'triggers' => array('any'),
27
    );
28
  }
29
  return $actions;
30
}
31

    
32
/**
33
 * Since Drupal's Archiver doesn't abstract properly the archivers it implements
34
 * (Archive_Tar and ZipArchive), it can't be used here.
35
 */
36
function views_bulk_operations_archive_action($file, $context) {
37
  global $user;
38
  static $archive_contents = array();
39

    
40
  // Adding a non-existent file to the archive crashes ZipArchive on close().
41
  if (file_exists($file->uri)) {
42
    $destination = $context['destination'];
43
    $zip = new ZipArchive();
44
    // If the archive already exists, open it. If not, create it.
45
    if (file_exists($destination)) {
46
      $opened = $zip->open(drupal_realpath($destination));
47
    }
48
    else {
49
      $opened = $zip->open(drupal_realpath($destination), ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE);
50
    }
51

    
52
    if ($opened) {
53
      // Create a list of all files in the archive. Used for duplicate checking.
54
      if (empty($archive_contents)) {
55
        for ($i = 0; $i < $zip->numFiles; $i++) {
56
          $archive_contents[] = $zip->getNameIndex($i);
57
        }
58
      }
59
      // Make sure that the target filename is unique.
60
      $filename = _views_bulk_operations_archive_action_create_filename(basename($file->uri), $archive_contents);
61
      // Note that the actual addition happens on close(), hence the need
62
      // to open / close the archive each time the action runs.
63
      $zip->addFile(drupal_realpath($file->uri), $filename);
64
      $zip->close();
65
      $archive_contents[] = $filename;
66
    }
67
  }
68

    
69
  // The operation is complete, create a file entity and provide a download
70
  // link to the user.
71
  if ($context['progress']['current'] == $context['progress']['total']) {
72
    $archive_file = new stdClass();
73
    $archive_file->uri      = $destination;
74
    $archive_file->filename = basename($destination);
75
    $archive_file->filemime = file_get_mimetype($destination);
76
    $archive_file->uid      = $user->uid;
77
    $archive_file->status   = $context['settings']['temporary'] ? FALSE : FILE_STATUS_PERMANENT;
78
    // Clear filesize() cache to avoid private file system differences in
79
    // filesize.
80
    // @see https://www.drupal.org/node/2743999
81
    clearstatcache();
82
    file_save($archive_file);
83

    
84
    $url = file_create_url($archive_file->uri);
85
    $url = l($url, $url, array('absolute' => TRUE));
86
    _views_bulk_operations_log(t('An archive has been created and can be downloaded from: !url', array('!url' => $url)));
87
  }
88
}
89

    
90
/**
91
 * Configuration form shown to the user before the action gets executed.
92
 */
93
function views_bulk_operations_archive_action_form($context) {
94
  // Pass the scheme as a value, so that the submit callback can access it.
95
  $form['scheme'] = array(
96
    '#type' => 'value',
97
    '#value' => $context['settings']['scheme'],
98
  );
99

    
100
  $form['filename'] = array(
101
    '#type' => 'textfield',
102
    '#title' => t('Filename'),
103
    '#default_value' => 'vbo_archive_' . date('Ymd'),
104
    '#field_suffix' => '.zip',
105
    '#description' => t('The name of the archive file.'),
106
  );
107
  return $form;
108
}
109

    
110
/**
111
 * Assembles a sanitized and unique URI for the archive.
112
 *
113
 * @returns array
114
 *   A URI array used by the action callback
115
 *   (views_bulk_operations_archive_action).
116
 */
117
function views_bulk_operations_archive_action_submit($form, $form_state) {
118
  // Validate the scheme, fallback to public if it's somehow invalid.
119
  $scheme = $form_state['values']['scheme'];
120
  if (!file_stream_wrapper_valid_scheme($scheme)) {
121
    $scheme = 'public';
122
  }
123
  $destination = $scheme . '://' . basename($form_state['values']['filename']) . '.zip';
124
  // If the chosen filename already exists, file_destination() will append
125
  // an integer to it in order to make it unique.
126
  $destination = file_destination($destination, FILE_EXISTS_RENAME);
127

    
128
  return array(
129
    'destination' => $destination,
130
  );
131
}
132

    
133
/**
134
 * Settings form (embedded into the VBO field settings in the Views UI).
135
 */
136
function views_bulk_operations_archive_action_views_bulk_operations_form($options) {
137
  $scheme_options = array();
138
  foreach (file_get_stream_wrappers(STREAM_WRAPPERS_LOCAL_NORMAL) as $scheme => $stream_wrapper) {
139
    $scheme_options[$scheme] = $stream_wrapper['name'];
140
  }
141
  if (count($scheme_options) > 1) {
142
    $form['scheme'] = array(
143
      '#type' => 'radios',
144
      '#title' => t('Storage'),
145
      '#options' => $scheme_options,
146
      '#default_value' => !empty($options['scheme']) ? $options['scheme'] : variable_get('file_default_scheme', 'public'),
147
      '#description' => t('Select where the archive should be stored. Private file storage has significantly more overhead than public files, but allows restricted access.'),
148
    );
149
  }
150
  else {
151
    $scheme_option_keys = array_keys($scheme_options);
152
    $form['scheme'] = array(
153
      '#type' => 'value',
154
      '#value' => reset($scheme_option_keys),
155
    );
156
  }
157

    
158
  $form['temporary'] = array(
159
    '#type' => 'checkbox',
160
    '#title' => t('Temporary'),
161
    '#default_value' => isset($options['temporary']) ? $options['temporary'] : TRUE,
162
    '#description' => t('Temporary files older than 6 hours are removed when cron runs.'),
163
  );
164
  return $form;
165
}
166

    
167
/**
168
 * Create a sanitized and unique version of the provided filename.
169
 *
170
 * @param string $filename
171
 *   The filename to create.
172
 * @param array $archive_list
173
 *   The list of files already in the archive.
174
 *
175
 * @return string
176
 *   The new filename.
177
 */
178
function _views_bulk_operations_archive_action_create_filename($filename, $archive_list) {
179
  // Strip control characters (ASCII value < 32). Though these are allowed in
180
  // some filesystems, not many applications handle them well.
181
  $filename = preg_replace('/[\x00-\x1F]/u', '_', $filename);
182
  if (substr(PHP_OS, 0, 3) == 'WIN') {
183
    // These characters are not allowed in Windows filenames.
184
    $filename = str_replace(array(':', '*', '?', '"', '<', '>', '|'), '_', $filename);
185
  }
186

    
187
  if (in_array($filename, $archive_list)) {
188
    // Destination file already exists, generate an alternative.
189
    $pos = strrpos($filename, '.');
190
    if ($pos !== FALSE) {
191
      $name = substr($filename, 0, $pos);
192
      $ext = substr($filename, $pos);
193
    }
194
    else {
195
      $name = $filename;
196
      $ext = '';
197
    }
198

    
199
    $counter = 0;
200
    do {
201
      $filename = $name . '_' . $counter++ . $ext;
202
    } while (in_array($filename, $archive_list));
203
  }
204

    
205
  return $filename;
206
}