Projet

Général

Profil

Paste
Télécharger (6,72 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / views_bulk_operations / actions / archive.action.inc @ 9df8b457

1
<?php
2

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

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

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

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

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

    
65
  // The operation is complete, create a file entity and provide a download
66
  // link to the user.
67
  if ($context['progress']['current'] == $context['progress']['total']) {
68
    $archive_file = new stdClass();
69
    $archive_file->uri      = $destination;
70
    $archive_file->filename = basename($destination);
71
    $archive_file->filemime = file_get_mimetype($destination);
72
    $archive_file->uid      = $user->uid;
73
    $archive_file->status   = $context['settings']['temporary'] ? FALSE : FILE_STATUS_PERMANENT;
74
    file_save($archive_file);
75

    
76
    $url = file_create_url($archive_file->uri);
77
    $url = l($url, $url, array('absolute' => TRUE));
78
    _views_bulk_operations_log(t('An archive has been created and can be downloaded from: !url', array('!url' => $url)));
79
  }
80
}
81

    
82
/**
83
 * Configuration form shown to the user before the action gets executed.
84
 */
85
function views_bulk_operations_archive_action_form($context) {
86
  // Pass the scheme as a value, so that the submit callback can access it.
87
  $form['scheme'] = array(
88
    '#type' => 'value',
89
    '#value' => $context['settings']['scheme'],
90
  );
91

    
92
  $form['filename'] = array(
93
    '#type' => 'textfield',
94
    '#title' => t('Filename'),
95
    '#default_value' => 'vbo_archive_' . date('Ymd'),
96
    '#field_suffix' => '.zip',
97
    '#description' => t('The name of the archive file.'),
98
  );
99
  return $form;
100
}
101

    
102
/**
103
 * Assembles a sanitized and unique URI for the archive, and returns it for
104
 * usage by the action callback (views_bulk_operations_archive_action).
105
 */
106
function views_bulk_operations_archive_action_submit($form, $form_state) {
107
  // Validate the scheme, fallback to public if it's somehow invalid.
108
  $scheme = $form_state['values']['scheme'];
109
  if (!file_stream_wrapper_valid_scheme($scheme)) {
110
    $scheme = 'public';
111
  }
112
  $destination = $scheme . '://' . basename($form_state['values']['filename']) . '.zip';
113
  // If the chosen filename already exists, file_destination() will append
114
  // an integer to it in order to make it unique.
115
  $destination = file_destination($destination, FILE_EXISTS_RENAME);
116

    
117
  return array(
118
    'destination' => $destination,
119
  );
120
}
121

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

    
147
  $form['temporary'] = array(
148
    '#type' => 'checkbox',
149
    '#title' => t('Temporary'),
150
    '#default_value' => isset($options['temporary']) ? $options['temporary'] : TRUE,
151
    '#description' => t('Temporary files older than 6 hours are removed when cron runs.'),
152
  );
153
  return $form;
154
}
155

    
156
/**
157
 * Create a sanitized and unique version of the provided filename.
158
 *
159
 * @param $filename
160
 *   String filename
161
 *
162
 * @return
163
 *   The new filename.
164
 */
165
function _views_bulk_operations_archive_action_create_filename($filename, $archive_list) {
166
  // Strip control characters (ASCII value < 32). Though these are allowed in
167
  // some filesystems, not many applications handle them well.
168
  $filename = preg_replace('/[\x00-\x1F]/u', '_', $filename);
169
  if (substr(PHP_OS, 0, 3) == 'WIN') {
170
    // These characters are not allowed in Windows filenames
171
    $filename = str_replace(array(':', '*', '?', '"', '<', '>', '|'), '_', $filename);
172
  }
173

    
174
  if (in_array($filename, $archive_list)) {
175
    // Destination file already exists, generate an alternative.
176
    $pos = strrpos($filename, '.');
177
    if ($pos !== FALSE) {
178
      $name = substr($filename, 0, $pos);
179
      $ext = substr($filename, $pos);
180
    }
181
    else {
182
      $name = $filename;
183
      $ext = '';
184
    }
185

    
186
    $counter = 0;
187
    do {
188
      $filename = $name . '_' . $counter++ . $ext;
189
    } while (in_array($filename, $archive_list));
190
  }
191

    
192
  return $filename;
193
}