1: <?php
2:
3: 4: 5:
6: class Quform_Uploader
7: {
8: 9: 10:
11: protected static $fileKeys = array('error', 'name', 'size', 'tmp_name', 'type');
12:
13: 14: 15:
16: protected $session;
17:
18: 19: 20:
21: protected $repository;
22:
23: 24: 25:
26: protected $formFactory;
27:
28: 29: 30: 31: 32:
33: public function __construct(Quform_Session $session, Quform_Repository $repository,
34: Quform_Form_Factory $formFactory)
35: {
36: $this->session = $session;
37: $this->repository = $repository;
38: $this->formFactory = $formFactory;
39: }
40:
41: 42: 43:
44: public function upload()
45: {
46: if ( ! Quform::isPostRequest() || Quform::get($_POST, 'quform_ajax_uploading') != '1') {
47: return;
48: }
49:
50: $this->validateUploadRequest();
51: $this->handleUploadRequest();
52: }
53:
54: 55: 56:
57: protected function handleUploadRequest()
58: {
59: $config = $this->repository->getConfig((int) $_POST['quform_form_id']);
60:
61: if ( ! is_array($config)) {
62: wp_send_json(array(
63: 'type' => 'error',
64: 'message' => __('Could not find the form config', 'quform')
65: ));
66: }
67:
68: $config['uniqueId'] = $_POST['quform_form_uid'];
69:
70: $form = $this->formFactory->create($config);
71:
72: if ( ! ($form instanceof Quform_Form) || $form->config('trashed')) {
73: wp_send_json(array(
74: 'type' => 'error',
75: 'message' => __('Could not find the form', 'quform')
76: ));
77: }
78:
79: if ( ! $form->isActive()) {
80: wp_send_json(array(
81: 'type' => 'error',
82: 'message' => __('This form is not currently active', 'quform')
83: ));
84: }
85:
86: $element = $form->getElementById((int) $_POST['quform_element_id']);
87:
88: if ( ! ($element instanceof Quform_Element_File)) {
89: wp_send_json(array(
90: 'type' => 'error',
91: 'message' => __('Could not find the element', 'quform')
92: ));
93: }
94:
95: if ( ! isset($_FILES[$element->getName()])) {
96: wp_send_json(array(
97: 'type' => 'error',
98: 'message' => __('File data not found', 'quform')
99: ));
100: }
101:
102: $uploadsTmpDir = $this->getUploadsTempDir();
103:
104: if ( ! is_dir($uploadsTmpDir)) {
105: wp_mkdir_p($uploadsTmpDir);
106: }
107:
108: if ( ! wp_is_writable($uploadsTmpDir)) {
109: wp_send_json(array(
110: 'type' => 'error',
111: 'message' => __('Temporary uploads directory is not writable', 'quform')
112: ));
113: }
114:
115:
116: $validator = $element->getFileUploadValidator();
117: $validator->setConfig('minimumNumberOfFiles', 0);
118:
119: if ($element->isValid()) {
120: $sessionKey = $form->getSessionKey() . '.uploads.' . $element->getName();
121:
122:
123: $uniqueId = $this->generateUploadUid();
124:
125:
126: $filename = tempnam($uploadsTmpDir, 'quform');
127: move_uploaded_file($_FILES[$element->getName()]['tmp_name'][0], $filename);
128: $_FILES[$element->getName()]['tmp_name'][0] = $filename;
129:
130: $files = $this->session->has($sessionKey) ? $this->session->get($sessionKey) : array();
131:
132: foreach (self::$fileKeys as $key) {
133: $files[$key][] = $_FILES[$element->getName()][$key][0];
134: }
135:
136: $files['quform_upload_uid'][] = $uniqueId;
137: $files['timestamp'][] = time();
138:
139: $this->session->set($sessionKey, $files);
140:
141: wp_send_json(array(
142: 'type' => 'success',
143: 'uid' => $uniqueId
144: ));
145: } else {
146: wp_send_json(array(
147: 'type' => 'error',
148: 'message' => $element->getError()
149: ));
150: }
151: }
152:
153: 154: 155: 156: 157:
158: protected function getUploadsTempDir()
159: {
160: return Quform::getTempDir('/quform/uploads');
161: }
162:
163: 164: 165:
166: protected function validateUploadRequest()
167: {
168: if ( ! isset($_POST['quform_form_id'], $_POST['quform_form_uid'], $_POST['quform_element_id']) ||
169: ! is_numeric($_POST['quform_form_id']) ||
170: ! Quform_Form::isValidUniqueId($_POST['quform_form_uid']) ||
171: ! is_numeric($_POST['quform_element_id'])
172: ) {
173: wp_send_json(array(
174: 'type' => 'error',
175: 'message' => __('Bad request', 'quform')
176: ));
177: }
178: }
179:
180: 181: 182: 183: 184:
185: public function process(Quform_Form $form)
186: {
187: foreach ($form->getRecursiveIterator() as $element) {
188: if ( ! ($element instanceof Quform_Element_File)) {
189: continue;
190: }
191:
192: $elementName = $element->getName();
193:
194: if ( ! array_key_exists($elementName, $_FILES) || ! is_array($_FILES[$elementName])) {
195: continue;
196: }
197:
198: $files = $_FILES[$elementName];
199:
200: if (is_array($files['error'])) {
201: foreach ($files['error'] as $key => $error) {
202: if ($error == UPLOAD_ERR_OK) {
203:
204: $file = array();
205:
206: foreach (self::$fileKeys as $k) {
207: $file[$k] = $files[$k][$key];
208: }
209:
210:
211: $file['quform_upload_uid'] = isset($files['quform_upload_uid'][$key]) && $this->isValidUploadUid($files['quform_upload_uid'][$key]) ? $files['quform_upload_uid'][$key] : $this->generateUploadUid();
212: $file['timestamp'] = isset($files['timestamp'][$key]) ? $files['timestamp'][$key] : time();
213:
214: $this->processUploadedFile($file, $element, $form);
215: }
216: }
217: }
218: }
219: }
220:
221: 222: 223: 224: 225: 226: 227:
228: protected function processUploadedFile(array $file, Quform_Element_File $element, Quform_Form $form)
229: {
230: $pathInfo = pathinfo($file['name']);
231: $extension = isset($pathInfo['extension']) ? $pathInfo['extension'] : '';
232:
233: $filename = Quform::isNonEmptyString($extension) ? str_replace(".$extension", '', $pathInfo['basename']) : $pathInfo['basename'];
234: $filename = sanitize_file_name($filename);
235: $filename = apply_filters('quform_filename_' . $element->getName(), $filename, $element, $form);
236: $filename = apply_filters('quform_upload_filename_' . $element->getIdentifier(), $filename, $file, $element, $form);
237:
238: if (Quform::isNonEmptyString($extension)) {
239: $filename = Quform::isNonEmptyString($filename) ? "$filename.$extension" : "upload.$extension";
240: } else {
241: $filename = Quform::isNonEmptyString($filename) ? $filename : 'upload';
242: }
243:
244: $file['name'] = $filename;
245: $file['path'] = $file['tmp_name'];
246:
247: unset($file['error'], $file['tmp_name']);
248:
249: if ($element->config('saveToServer')) {
250: $result = $this->saveUploadedFile($file, $element, $form);
251:
252: if (is_array($result)) {
253: $file = $result;
254:
255: if ($element->config('addToMediaLibrary')) {
256: $this->addToMediaLibrary($file, $element, $form);
257: }
258: }
259: } else {
260:
261: $tmpPath = trailingslashit(dirname($file['path']));
262:
263:
264: if (file_exists($tmpPath . $file['name'])) {
265: $count = 1;
266: $newFilenamePath = $tmpPath . $file['name'];
267:
268: while (file_exists($newFilenamePath)) {
269: $newFilename = $count++ . '_' . $file['name'];
270: $newFilenamePath = $tmpPath . $newFilename;
271: }
272:
273: $file['name'] = $newFilename;
274: }
275:
276:
277: if (rename($file['path'], $tmpPath . $file['name']) !== false) {
278: chmod($tmpPath . $file['name'], 0644);
279:
280: $file['path'] = $tmpPath . $file['name'];
281: }
282: }
283:
284: $element->addFile($file);
285: }
286:
287: 288: 289: 290: 291: 292: 293:
294: protected function addToMediaLibrary(array $file, Quform_Element_File $element, Quform_Form $form)
295: {
296: require_once ABSPATH . 'wp-admin/includes/image.php';
297: require_once ABSPATH . 'wp-admin/includes/media.php';
298:
299: $type = wp_check_filetype($file['name']);
300:
301: $attachment = array(
302: 'post_title' => $file['name'],
303: 'post_content' => '',
304: 'post_mime_type' => $type['type'],
305: 'guid' => $file['url']
306: );
307:
308: $attachment = apply_filters('quform_uploader_attachment', $attachment, $file, $element, $form);
309: $attachment = apply_filters('quform_uploader_attachment_' . $element->getIdentifier(), $attachment, $file, $element, $form);
310:
311: $attachId = wp_insert_attachment($attachment, $file['path']);
312: wp_update_attachment_metadata($attachId, wp_generate_attachment_metadata($attachId, $file['path']));
313: }
314:
315: 316: 317: 318: 319: 320: 321: 322: 323: 324:
325: protected function saveUploadedFile(array $file, Quform_Element_File $element, Quform_Form $form)
326: {
327: if (($wpUploadsDir = Quform::getUploadsDir()) == false) {
328:
329: return false;
330: }
331:
332:
333: $path = $element->config('savePath') == '' ? 'quform/{form_id}/{year}/{month}/' : $element->config('savePath');
334:
335:
336: $path = str_replace(array('{form_id}', '{year}', '{month}', '{day}'), array($form->getId(), Quform::date('Y'), Quform::date('m'), Quform::date('d')), $path);
337:
338:
339: $path = apply_filters('quform_upload_path', $path, $element, $form);
340: $path = apply_filters('quform_upload_path_' . $form->getId(), $path, $element, $form);
341:
342:
343: $absolutePath = rtrim($wpUploadsDir, '/') . '/' . ltrim($path, '/');
344:
345:
346: $absolutePath = apply_filters('quform_upload_absolute_path', $absolutePath, $element, $form);
347: $absolutePath = apply_filters('quform_upload_absolute_path_' . $form->getId(), $absolutePath, $element, $form);
348:
349:
350: $path = trailingslashit($path);
351: $absolutePath = trailingslashit($absolutePath);
352:
353:
354: if (!is_dir($absolutePath)) {
355: wp_mkdir_p($absolutePath);
356: }
357:
358:
359: if (file_exists($absolutePath . $file['name'])) {
360: $count = 1;
361: $newFilenamePath = $absolutePath . $file['name'];
362:
363: while (file_exists($newFilenamePath)) {
364: $newFilename = $count++ . '_' . $file['name'];
365: $newFilenamePath = $absolutePath . $newFilename;
366: }
367:
368: $file['name'] = $newFilename;
369: }
370:
371:
372: if (rename($file['path'], $absolutePath . $file['name']) !== false) {
373: chmod($absolutePath . $file['name'], 0644);
374:
375: $file['path'] = $absolutePath . $file['name'];
376: $file['url'] = Quform::getUploadsUrl($path . $file['name']);
377:
378: return $file;
379: } else {
380: return false;
381: }
382: }
383:
384: 385: 386: 387: 388:
389: public function mergeSessionFiles(Quform_Form $form)
390: {
391: $uploads = $this->session->get(sprintf('%s.uploads', $form->getSessionKey()));
392:
393:
394: $removedUploadUids = isset($_POST['quform_removed_upload_uids']) && Quform::isNonEmptyString($_POST['quform_removed_upload_uids']) ? explode(',', $_POST['quform_removed_upload_uids']) : array();
395:
396: if (is_array($uploads)) {
397: foreach ($uploads as $elementName => $uploadInfo) {
398: if (is_array($uploadInfo['quform_upload_uid'])) {
399:
400: foreach ($uploadInfo['quform_upload_uid'] as $key => $id) {
401: if (in_array($id, $removedUploadUids)) {
402: foreach (self::$fileKeys as $fileKey) {
403: unset($uploads[$elementName][$fileKey][$key]);
404: }
405: unset($uploads[$elementName]['quform_upload_uid'][$key]);
406: }
407: }
408:
409:
410: if ( ! count($uploads[$elementName]['quform_upload_uid'])) {
411: unset($uploads[$elementName]);
412: }
413: } else {
414:
415: if (in_array($uploadInfo['quform_upload_uid'], $removedUploadUids)) {
416: unset($uploads[$elementName]);
417: }
418: }
419: }
420:
421:
422: $_FILES = array_merge($_FILES, $uploads);
423: }
424:
425: $files = $this->session->get(sprintf('%s.files', $form->getSessionKey()));
426:
427: if (is_array($files)) {
428: foreach ($files as $elementName => $value) {
429: $element = $form->getElementByName($elementName);
430:
431: foreach ($value as $key => $file) {
432: if (in_array($file['quform_upload_uid'], $removedUploadUids)) {
433: unset($value[$key]);
434: }
435: }
436:
437: $value = array_values($value);
438:
439: if ($element instanceof Quform_Element_File) {
440: $element->setValue($value);
441: }
442: }
443: }
444: }
445:
446: 447: 448: 449: 450:
451: public function saveFileUploadValuesIntoSession(Quform_Form $form)
452: {
453: foreach ($form->getRecursiveIterator() as $element) {
454: if ( ! $element instanceof Quform_Element_File) {
455: continue;
456: }
457:
458: if ( ! $element->isEmpty()) {
459: $this->session->set($form->getSessionKey() . '.files.' . $element->getName(), $element->getValue());
460: }
461: }
462: }
463:
464: 465: 466: 467: 468:
469: public function saveUploadedFilesIntoSession(Quform_Form $form)
470: {
471: foreach ($form->getRecursiveIterator() as $element) {
472: if ( ! $element instanceof Quform_Element_File) {
473: continue;
474: }
475:
476: $elementName = $element->getName();
477:
478: if ( ! array_key_exists($elementName, $_FILES) || ! is_array($_FILES[$elementName])) {
479: continue;
480: }
481:
482: $uploadsTmpDir = $this->getUploadsTempDir();
483:
484: if ( ! is_dir($uploadsTmpDir)) {
485: wp_mkdir_p($uploadsTmpDir);
486: }
487:
488: if ( ! wp_is_writable($uploadsTmpDir)) {
489: continue;
490: }
491:
492: if ($element->isValid()) {
493: $sessionKey = $form->getSessionKey() . '.uploads.' . $elementName;
494: $files = array();
495:
496: foreach ($_FILES[$elementName]['error'] as $key => $error) {
497: if ($error == UPLOAD_ERR_OK) {
498: if (is_uploaded_file($_FILES[$elementName]['tmp_name'][$key])) {
499: $filename = tempnam($uploadsTmpDir, 'quform');
500: move_uploaded_file($_FILES[$elementName]['tmp_name'][$key], $filename);
501: $_FILES[$elementName]['tmp_name'][$key] = $filename;
502: }
503:
504: foreach (self::$fileKeys as $fileKey) {
505: $files[$fileKey][] = $_FILES[$elementName][$fileKey][$key];
506: $file[$fileKey] = $_FILES[$elementName][$fileKey][$key];
507: }
508:
509: $files['quform_upload_uid'][] = isset($_FILES[$elementName]['quform_upload_uid'][$key]) && $this->isValidUploadUid($_FILES[$elementName]['quform_upload_uid'][$key]) ? $_FILES[$elementName]['quform_upload_uid'][$key] : $this->generateUploadUid();
510: $files['timestamp'][] = time();
511:
512: $element->addFile($file);
513: }
514: }
515:
516: if (count($files)) {
517: $this->session->set($sessionKey, $files);
518: } else {
519: $this->session->forget($sessionKey);
520: }
521: }
522: }
523: }
524:
525: 526: 527: 528: 529: 530:
531: protected function isValidUploadUid($uid)
532: {
533: return is_string($uid) && preg_match('/^[a-zA-Z0-9]{40}$/', $uid);
534: }
535:
536: 537: 538: 539: 540:
541: protected function generateUploadUid()
542: {
543: return Quform::randomString(40);
544: }
545:
546: 547: 548: 549:
550: public function cleanup()
551: {
552: $uploadsTmpDir = $this->getUploadsTempDir();
553:
554: if (is_dir($uploadsTmpDir) && $handle = opendir($uploadsTmpDir)) {
555: clearstatcache();
556: $keepUntil = time() - 21600;
557: while (false !== ($file = readdir($handle))) {
558: $filePath = $uploadsTmpDir . '/' . $file;
559: $mtime = filemtime($filePath);
560: if ($file != '.' && $file != '..' && $mtime < $keepUntil) {
561: @unlink($filePath);
562: }
563: }
564:
565: closedir($handle);
566: }
567: }
568:
569: 570: 571:
572: protected function scheduleCleanup()
573: {
574: if ( ! wp_next_scheduled('quform_upload_cleanup')) {
575: wp_schedule_event(time() + (12 * HOUR_IN_SECONDS), 'twicedaily', 'quform_upload_cleanup');
576: }
577: }
578:
579: 580: 581:
582: protected function unscheduleCleanup()
583: {
584: if ($timestamp = wp_next_scheduled('quform_upload_cleanup')) {
585: wp_unschedule_event($timestamp, 'quform_upload_cleanup');
586: }
587: }
588:
589: 590: 591: 592: 593:
594: public function activate()
595: {
596: $this->scheduleCleanup();
597:
598: $uploadsTmpDir = $this->getUploadsTempDir();
599:
600: if ( ! is_dir($uploadsTmpDir)) {
601: wp_mkdir_p($uploadsTmpDir);
602: }
603: }
604:
605: 606: 607: 608: 609:
610: public function deactivate()
611: {
612: $this->unscheduleCleanup();
613: $this->cleanup();
614: }
615:
616: 617: 618: 619: 620:
621: public function uninstall()
622: {
623: $this->unscheduleCleanup();
624: $this->cleanup();
625: }
626: }
627: