1 |
85ad3d82
|
Assos Assos
|
<?php
|
2 |
|
|
|
3 |
|
|
/**
|
4 |
|
|
* @file
|
5 |
|
|
* Home of the FeedsFileFetcher and related classes.
|
6 |
|
|
*/
|
7 |
|
|
|
8 |
|
|
/**
|
9 |
|
|
* Definition of the import batch object created on the fetching stage by
|
10 |
|
|
* FeedsFileFetcher.
|
11 |
|
|
*/
|
12 |
|
|
class FeedsFileFetcherResult extends FeedsFetcherResult {
|
13 |
|
|
/**
|
14 |
|
|
* Constructor.
|
15 |
|
|
*/
|
16 |
|
|
public function __construct($file_path) {
|
17 |
|
|
parent::__construct('');
|
18 |
|
|
$this->file_path = $file_path;
|
19 |
|
|
}
|
20 |
|
|
|
21 |
|
|
/**
|
22 |
|
|
* Overrides parent::getRaw().
|
23 |
|
|
*/
|
24 |
|
|
public function getRaw() {
|
25 |
|
|
return $this->sanitizeRaw(file_get_contents($this->file_path));
|
26 |
|
|
}
|
27 |
|
|
|
28 |
|
|
/**
|
29 |
|
|
* Overrides parent::getFilePath().
|
30 |
|
|
*/
|
31 |
|
|
public function getFilePath() {
|
32 |
41cc1b08
|
Assos Assos
|
if (!is_readable($this->file_path)) {
|
33 |
85ad3d82
|
Assos Assos
|
throw new Exception(t('File @filepath is not accessible.', array('@filepath' => $this->file_path)));
|
34 |
|
|
}
|
35 |
|
|
return $this->sanitizeFile($this->file_path);
|
36 |
|
|
}
|
37 |
|
|
}
|
38 |
|
|
|
39 |
|
|
/**
|
40 |
|
|
* Fetches data via HTTP.
|
41 |
|
|
*/
|
42 |
|
|
class FeedsFileFetcher extends FeedsFetcher {
|
43 |
|
|
|
44 |
|
|
/**
|
45 |
|
|
* Implements FeedsFetcher::fetch().
|
46 |
|
|
*/
|
47 |
|
|
public function fetch(FeedsSource $source) {
|
48 |
|
|
$source_config = $source->getConfigFor($this);
|
49 |
|
|
|
50 |
|
|
// Just return a file fetcher result if this is a file.
|
51 |
|
|
if (is_file($source_config['source'])) {
|
52 |
|
|
return new FeedsFileFetcherResult($source_config['source']);
|
53 |
|
|
}
|
54 |
|
|
|
55 |
|
|
// Batch if this is a directory.
|
56 |
|
|
$state = $source->state(FEEDS_FETCH);
|
57 |
|
|
$files = array();
|
58 |
|
|
if (!isset($state->files)) {
|
59 |
|
|
$state->files = $this->listFiles($source_config['source']);
|
60 |
|
|
$state->total = count($state->files);
|
61 |
|
|
}
|
62 |
|
|
if (count($state->files)) {
|
63 |
|
|
$file = array_shift($state->files);
|
64 |
|
|
$state->progress($state->total, $state->total - count($state->files));
|
65 |
|
|
return new FeedsFileFetcherResult($file);
|
66 |
|
|
}
|
67 |
|
|
|
68 |
|
|
throw new Exception(t('Resource is not a file or it is an empty directory: %source', array('%source' => $source_config['source'])));
|
69 |
|
|
}
|
70 |
|
|
|
71 |
|
|
/**
|
72 |
|
|
* Returns an array of files in a directory.
|
73 |
|
|
*
|
74 |
|
|
* @param string $dir
|
75 |
|
|
* A stream wreapper URI that is a directory.
|
76 |
|
|
*
|
77 |
|
|
* @return array
|
78 |
|
|
* An array of stream wrapper URIs pointing to files. The array is empty if
|
79 |
|
|
* no files could be found. Never contains directories.
|
80 |
|
|
*/
|
81 |
|
|
protected function listFiles($dir) {
|
82 |
|
|
$dir = file_stream_wrapper_uri_normalize($dir);
|
83 |
|
|
$files = array();
|
84 |
|
|
if ($items = @scandir($dir)) {
|
85 |
|
|
foreach ($items as $item) {
|
86 |
|
|
if (is_file("$dir/$item") && strpos($item, '.') !== 0) {
|
87 |
|
|
$files[] = "$dir/$item";
|
88 |
|
|
}
|
89 |
|
|
}
|
90 |
|
|
}
|
91 |
|
|
return $files;
|
92 |
|
|
}
|
93 |
|
|
|
94 |
|
|
/**
|
95 |
|
|
* Source form.
|
96 |
|
|
*/
|
97 |
|
|
public function sourceForm($source_config) {
|
98 |
|
|
$form = array();
|
99 |
|
|
$form['fid'] = array(
|
100 |
|
|
'#type' => 'value',
|
101 |
|
|
'#value' => empty($source_config['fid']) ? 0 : $source_config['fid'],
|
102 |
|
|
);
|
103 |
|
|
if (empty($this->config['direct'])) {
|
104 |
|
|
$form['source'] = array(
|
105 |
|
|
'#type' => 'value',
|
106 |
|
|
'#value' => empty($source_config['source']) ? '' : $source_config['source'],
|
107 |
|
|
);
|
108 |
|
|
$form['upload'] = array(
|
109 |
|
|
'#type' => 'file',
|
110 |
|
|
'#title' => empty($this->config['direct']) ? t('File') : NULL,
|
111 |
|
|
'#description' => empty($source_config['source']) ? t('Select a file from your local system.') : t('Select a different file from your local system.'),
|
112 |
41cc1b08
|
Assos Assos
|
'#theme_wrappers' => array('feeds_upload'),
|
113 |
85ad3d82
|
Assos Assos
|
'#file_info' => empty($source_config['fid']) ? NULL : file_load($source_config['fid']),
|
114 |
|
|
'#size' => 10,
|
115 |
|
|
);
|
116 |
|
|
}
|
117 |
|
|
else {
|
118 |
|
|
$form['source'] = array(
|
119 |
|
|
'#type' => 'textfield',
|
120 |
|
|
'#title' => t('File'),
|
121 |
|
|
'#description' => t('Specify a path to a file or a directory. Prefix the path with a scheme. Available schemes: @schemes.', array('@schemes' => implode(', ', $this->config['allowed_schemes']))),
|
122 |
|
|
'#default_value' => empty($source_config['source']) ? '' : $source_config['source'],
|
123 |
|
|
);
|
124 |
|
|
}
|
125 |
|
|
return $form;
|
126 |
|
|
}
|
127 |
|
|
|
128 |
|
|
/**
|
129 |
|
|
* Overrides parent::sourceFormValidate().
|
130 |
|
|
*/
|
131 |
|
|
public function sourceFormValidate(&$values) {
|
132 |
|
|
$values['source'] = trim($values['source']);
|
133 |
|
|
|
134 |
|
|
if (empty($this->config['direct'])) {
|
135 |
|
|
|
136 |
|
|
$feed_dir = $this->config['directory'];
|
137 |
|
|
|
138 |
|
|
if (!file_prepare_directory($feed_dir, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
|
139 |
|
|
if (user_access('administer feeds')) {
|
140 |
|
|
$plugin_key = feeds_importer($this->id)->config[$this->pluginType()]['plugin_key'];
|
141 |
|
|
$link = url('admin/structure/feeds/' . $this->id . '/settings/' . $plugin_key);
|
142 |
|
|
form_set_error('feeds][FeedsFileFetcher][source', t('Upload failed. Please check the upload <a href="@link">settings.</a>', array('@link' => $link)));
|
143 |
|
|
}
|
144 |
|
|
else {
|
145 |
|
|
form_set_error('feeds][FeedsFileFetcher][source', t('Upload failed. Please contact your site administrator.'));
|
146 |
|
|
}
|
147 |
|
|
watchdog('feeds', 'The upload directory %directory required by a feed could not be created or is not accessible. A newly uploaded file could not be saved in this directory as a consequence, and the upload was canceled.', array('%directory' => $feed_dir));
|
148 |
|
|
}
|
149 |
|
|
// Validate and save uploaded file.
|
150 |
|
|
elseif ($file = file_save_upload('feeds', array('file_validate_extensions' => array(0 => $this->config['allowed_extensions'])), $feed_dir)) {
|
151 |
|
|
$values['source'] = $file->uri;
|
152 |
|
|
$values['file'] = $file;
|
153 |
|
|
}
|
154 |
|
|
elseif (empty($values['source'])) {
|
155 |
|
|
form_set_error('feeds][FeedsFileFetcher][source', t('Please upload a file.'));
|
156 |
|
|
}
|
157 |
|
|
else {
|
158 |
|
|
// File present from previous upload. Nothing to validate.
|
159 |
|
|
}
|
160 |
|
|
}
|
161 |
|
|
else {
|
162 |
|
|
// Check if chosen url scheme is allowed.
|
163 |
|
|
$scheme = file_uri_scheme($values['source']);
|
164 |
|
|
if (!$scheme || !in_array($scheme, $this->config['allowed_schemes'])) {
|
165 |
|
|
form_set_error('feeds][FeedsFileFetcher][source', t("The file needs to reside within the site's files directory, its path needs to start with scheme://. Available schemes: @schemes.", array('@schemes' => implode(', ', $this->config['allowed_schemes']))));
|
166 |
|
|
}
|
167 |
41cc1b08
|
Assos Assos
|
// Check whether the given path is readable.
|
168 |
|
|
elseif (!is_readable($values['source'])) {
|
169 |
85ad3d82
|
Assos Assos
|
form_set_error('feeds][FeedsFileFetcher][source', t('The specified file or directory does not exist.'));
|
170 |
|
|
}
|
171 |
|
|
}
|
172 |
|
|
}
|
173 |
|
|
|
174 |
|
|
/**
|
175 |
|
|
* Overrides parent::sourceSave().
|
176 |
|
|
*/
|
177 |
|
|
public function sourceSave(FeedsSource $source) {
|
178 |
|
|
$source_config = $source->getConfigFor($this);
|
179 |
|
|
|
180 |
|
|
// If a new file is present, delete the old one and replace it with the new
|
181 |
|
|
// one.
|
182 |
|
|
if (isset($source_config['file'])) {
|
183 |
|
|
$file = $source_config['file'];
|
184 |
|
|
if (isset($source_config['fid'])) {
|
185 |
|
|
$this->deleteFile($source_config['fid'], $source->feed_nid);
|
186 |
|
|
}
|
187 |
|
|
$file->status = FILE_STATUS_PERMANENT;
|
188 |
|
|
file_save($file);
|
189 |
|
|
file_usage_add($file, 'feeds', get_class($this), $source->feed_nid);
|
190 |
|
|
|
191 |
|
|
$source_config['fid'] = $file->fid;
|
192 |
|
|
unset($source_config['file']);
|
193 |
|
|
$source->setConfigFor($this, $source_config);
|
194 |
|
|
}
|
195 |
|
|
}
|
196 |
|
|
|
197 |
|
|
/**
|
198 |
|
|
* Overrides parent::sourceDelete().
|
199 |
|
|
*/
|
200 |
|
|
public function sourceDelete(FeedsSource $source) {
|
201 |
|
|
$source_config = $source->getConfigFor($this);
|
202 |
|
|
if (isset($source_config['fid'])) {
|
203 |
|
|
$this->deleteFile($source_config['fid'], $source->feed_nid);
|
204 |
|
|
}
|
205 |
|
|
}
|
206 |
|
|
|
207 |
|
|
/**
|
208 |
|
|
* Overrides parent::configDefaults().
|
209 |
|
|
*/
|
210 |
|
|
public function configDefaults() {
|
211 |
|
|
$schemes = $this->getSchemes();
|
212 |
|
|
$scheme = in_array('private', $schemes) ? 'private' : 'public';
|
213 |
|
|
|
214 |
|
|
return array(
|
215 |
|
|
'allowed_extensions' => 'txt csv tsv xml opml',
|
216 |
|
|
'direct' => FALSE,
|
217 |
|
|
'directory' => $scheme . '://feeds',
|
218 |
|
|
'allowed_schemes' => $schemes,
|
219 |
|
|
);
|
220 |
|
|
}
|
221 |
|
|
|
222 |
|
|
/**
|
223 |
|
|
* Overrides parent::configForm().
|
224 |
|
|
*/
|
225 |
|
|
public function configForm(&$form_state) {
|
226 |
|
|
$form = array();
|
227 |
|
|
$form['allowed_extensions'] = array(
|
228 |
|
|
'#type' => 'textfield',
|
229 |
|
|
'#title' => t('Allowed file extensions'),
|
230 |
|
|
'#description' => t('Allowed file extensions for upload.'),
|
231 |
|
|
'#default_value' => $this->config['allowed_extensions'],
|
232 |
|
|
);
|
233 |
|
|
$form['direct'] = array(
|
234 |
|
|
'#type' => 'checkbox',
|
235 |
|
|
'#title' => t('Supply path to file or directory directly'),
|
236 |
|
|
'#description' => t('For experts. Lets users specify a path to a file <em>or a directory of files</em> directly,
|
237 |
|
|
instead of a file upload through the browser. This is useful when the files that need to be imported
|
238 |
|
|
are already on the server.'),
|
239 |
|
|
'#default_value' => $this->config['direct'],
|
240 |
|
|
);
|
241 |
|
|
$form['directory'] = array(
|
242 |
|
|
'#type' => 'textfield',
|
243 |
|
|
'#title' => t('Upload directory'),
|
244 |
|
|
'#description' => t('Directory where uploaded files get stored. Prefix the path with a scheme. Available schemes: @schemes.', array('@schemes' => implode(', ', $this->getSchemes()))),
|
245 |
|
|
'#default_value' => $this->config['directory'],
|
246 |
|
|
'#states' => array(
|
247 |
|
|
'visible' => array(':input[name="direct"]' => array('checked' => FALSE)),
|
248 |
|
|
'required' => array(':input[name="direct"]' => array('checked' => FALSE)),
|
249 |
|
|
),
|
250 |
|
|
);
|
251 |
|
|
if ($options = $this->getSchemeOptions()) {
|
252 |
|
|
$form['allowed_schemes'] = array(
|
253 |
|
|
'#type' => 'checkboxes',
|
254 |
|
|
'#title' => t('Allowed schemes'),
|
255 |
|
|
'#default_value' => $this->config['allowed_schemes'],
|
256 |
|
|
'#options' => $options,
|
257 |
|
|
'#description' => t('Select the schemes you want to allow for direct upload.'),
|
258 |
|
|
'#states' => array(
|
259 |
|
|
'visible' => array(':input[name="direct"]' => array('checked' => TRUE)),
|
260 |
|
|
),
|
261 |
|
|
);
|
262 |
|
|
}
|
263 |
|
|
|
264 |
|
|
return $form;
|
265 |
|
|
}
|
266 |
|
|
|
267 |
|
|
/**
|
268 |
|
|
* Overrides parent::configFormValidate().
|
269 |
|
|
*
|
270 |
|
|
* Ensure that the chosen directory is accessible.
|
271 |
|
|
*/
|
272 |
|
|
public function configFormValidate(&$values) {
|
273 |
|
|
|
274 |
|
|
$values['directory'] = trim($values['directory']);
|
275 |
|
|
$values['allowed_schemes'] = array_filter($values['allowed_schemes']);
|
276 |
|
|
|
277 |
|
|
if (!$values['direct']) {
|
278 |
|
|
// Ensure that the upload directory field is not empty when not in
|
279 |
|
|
// direct-mode.
|
280 |
|
|
if (!$values['directory']) {
|
281 |
|
|
form_set_error('directory', t('Please specify an upload directory.'));
|
282 |
|
|
// Do not continue validating the directory if none was specified.
|
283 |
|
|
return;
|
284 |
|
|
}
|
285 |
|
|
|
286 |
|
|
// Validate the URI scheme of the upload directory.
|
287 |
|
|
$scheme = file_uri_scheme($values['directory']);
|
288 |
|
|
if (!$scheme || !in_array($scheme, $this->getSchemes())) {
|
289 |
|
|
form_set_error('directory', t('Please enter a valid scheme into the directory location.'));
|
290 |
|
|
|
291 |
|
|
// Return here so that attempts to create the directory below don't
|
292 |
|
|
// throw warnings.
|
293 |
|
|
return;
|
294 |
|
|
}
|
295 |
|
|
|
296 |
|
|
// Ensure that the upload directory exists.
|
297 |
|
|
if (!file_prepare_directory($values['directory'], FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
|
298 |
|
|
form_set_error('directory', t('The chosen directory does not exist and attempts to create it failed.'));
|
299 |
|
|
}
|
300 |
|
|
}
|
301 |
|
|
}
|
302 |
|
|
|
303 |
|
|
/**
|
304 |
|
|
* Deletes a file.
|
305 |
|
|
*
|
306 |
|
|
* @param int $fid
|
307 |
|
|
* The file id.
|
308 |
|
|
* @param int $feed_nid
|
309 |
|
|
* The feed node's id, or 0 if a standalone feed.
|
310 |
|
|
*
|
311 |
|
|
* @return bool|array
|
312 |
|
|
* TRUE for success, FALSE in the event of an error, or an array if the file
|
313 |
|
|
* is being used by any modules.
|
314 |
|
|
*
|
315 |
|
|
* @see file_delete()
|
316 |
|
|
*/
|
317 |
|
|
protected function deleteFile($fid, $feed_nid) {
|
318 |
|
|
if ($file = file_load($fid)) {
|
319 |
|
|
file_usage_delete($file, 'feeds', get_class($this), $feed_nid);
|
320 |
|
|
return file_delete($file);
|
321 |
|
|
}
|
322 |
|
|
return FALSE;
|
323 |
|
|
}
|
324 |
|
|
|
325 |
|
|
/**
|
326 |
|
|
* Returns available schemes.
|
327 |
|
|
*
|
328 |
|
|
* @return array
|
329 |
|
|
* The available schemes.
|
330 |
|
|
*/
|
331 |
|
|
protected function getSchemes() {
|
332 |
|
|
return array_keys(file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE));
|
333 |
|
|
}
|
334 |
|
|
|
335 |
|
|
/**
|
336 |
|
|
* Returns available scheme options for use in checkboxes or select list.
|
337 |
|
|
*
|
338 |
|
|
* @return array
|
339 |
|
|
* The available scheme array keyed scheme => description
|
340 |
|
|
*/
|
341 |
|
|
protected function getSchemeOptions() {
|
342 |
|
|
$options = array();
|
343 |
|
|
foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $info) {
|
344 |
|
|
$options[$scheme] = check_plain($scheme . ': ' . $info['description']);
|
345 |
|
|
}
|
346 |
|
|
return $options;
|
347 |
|
|
}
|
348 |
|
|
|
349 |
|
|
} |