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:
193:
194:
195: if ($element->isConditionallyHidden() || ! $element->isVisible()) {
196: continue;
197: }
198:
199: $elementName = $element->getName();
200:
201: if ( ! array_key_exists($elementName, $_FILES) || ! is_array($_FILES[$elementName])) {
202: continue;
203: }
204:
205: $files = $_FILES[$elementName];
206:
207: if (is_array($files['error'])) {
208: foreach ($files['error'] as $key => $error) {
209: if ($error == UPLOAD_ERR_OK) {
210:
211: $file = array();
212:
213: foreach (self::$fileKeys as $k) {
214: $file[$k] = $files[$k][$key];
215: }
216:
217:
218: $file['quform_upload_uid'] = isset($files['quform_upload_uid'][$key]) && $this->isValidUploadUid($files['quform_upload_uid'][$key]) ? $files['quform_upload_uid'][$key] : $this->generateUploadUid();
219: $file['timestamp'] = isset($files['timestamp'][$key]) ? $files['timestamp'][$key] : time();
220:
221: $this->processUploadedFile($file, $element, $form);
222: }
223: }
224: }
225: }
226: }
227:
228: 229: 230: 231: 232: 233: 234:
235: protected function processUploadedFile(array $file, Quform_Element_File $element, Quform_Form $form)
236: {
237: $pathInfo = pathinfo($file['name']);
238: $extension = isset($pathInfo['extension']) ? $pathInfo['extension'] : '';
239:
240: $filename = Quform::isNonEmptyString($extension) ? str_replace(".$extension", '', $pathInfo['basename']) : $pathInfo['basename'];
241: $filename = sanitize_file_name($filename);
242: $filename = apply_filters('quform_filename_' . $element->getName(), $filename, $element, $form);
243: $filename = apply_filters('quform_upload_filename_' . $element->getIdentifier(), $filename, $file, $element, $form);
244:
245: if (Quform::isNonEmptyString($extension)) {
246: $filename = Quform::isNonEmptyString($filename) ? "$filename.$extension" : "upload.$extension";
247: } else {
248: $filename = Quform::isNonEmptyString($filename) ? $filename : 'upload';
249: }
250:
251: $file['name'] = $filename;
252: $file['path'] = $file['tmp_name'];
253:
254: unset($file['error'], $file['tmp_name']);
255:
256: if ($element->config('saveToServer')) {
257: $result = $this->saveUploadedFile($file, $element, $form);
258:
259: if (is_array($result)) {
260: $file = $result;
261:
262: if ($element->config('addToMediaLibrary')) {
263: $this->addToMediaLibrary($file, $element, $form);
264: }
265: }
266: } else {
267:
268: $tmpPath = trailingslashit(dirname($file['path']));
269:
270:
271: if (file_exists($tmpPath . $file['name'])) {
272: $count = 1;
273: $newFilenamePath = $tmpPath . $file['name'];
274:
275: while (file_exists($newFilenamePath)) {
276: $newFilename = $count++ . '_' . $file['name'];
277: $newFilenamePath = $tmpPath . $newFilename;
278: }
279:
280: $file['name'] = $newFilename;
281: }
282:
283:
284: if (rename($file['path'], $tmpPath . $file['name']) !== false) {
285: chmod($tmpPath . $file['name'], 0644);
286:
287: $file['path'] = $tmpPath . $file['name'];
288: }
289: }
290:
291: $element->addFile($file);
292: }
293:
294: 295: 296: 297: 298: 299: 300:
301: protected function addToMediaLibrary(array $file, Quform_Element_File $element, Quform_Form $form)
302: {
303: require_once ABSPATH . 'wp-admin/includes/image.php';
304: require_once ABSPATH . 'wp-admin/includes/media.php';
305:
306: $type = wp_check_filetype($file['name']);
307:
308: $attachment = array(
309: 'post_title' => $file['name'],
310: 'post_content' => '',
311: 'post_mime_type' => $type['type'],
312: 'guid' => $file['url']
313: );
314:
315: $attachment = apply_filters('quform_uploader_attachment', $attachment, $file, $element, $form);
316: $attachment = apply_filters('quform_uploader_attachment_' . $element->getIdentifier(), $attachment, $file, $element, $form);
317:
318: $attachId = wp_insert_attachment($attachment, $file['path']);
319: wp_update_attachment_metadata($attachId, wp_generate_attachment_metadata($attachId, $file['path']));
320: }
321:
322: 323: 324: 325: 326: 327: 328: 329: 330: 331:
332: protected function saveUploadedFile(array $file, Quform_Element_File $element, Quform_Form $form)
333: {
334: if (($wpUploadsDir = Quform::getUploadsDir()) == false) {
335:
336: return false;
337: }
338:
339:
340: $path = $element->config('savePath') == '' ? 'quform/{form_id}-{upload_security_token}/{year}/{month}/' : $element->config('savePath');
341:
342:
343: $path = str_replace(
344: array(
345: '{form_id}',
346: '{year}',
347: '{month}',
348: '{day}',
349: '{upload_security_token}'
350: ),
351: array(
352: $form->getId(),
353: Quform::date('Y'),
354: Quform::date('m'),
355: Quform::date('d'),
356: md5($form->getId() . $form->config('createdAt'))
357: ),
358: $path
359: );
360:
361:
362: $path = apply_filters('quform_upload_path', $path, $element, $form);
363: $path = apply_filters('quform_upload_path_' . $form->getId(), $path, $element, $form);
364:
365:
366: $absolutePath = rtrim($wpUploadsDir, '/') . '/' . ltrim($path, '/');
367:
368:
369: $absolutePath = apply_filters('quform_upload_absolute_path', $absolutePath, $element, $form);
370: $absolutePath = apply_filters('quform_upload_absolute_path_' . $form->getId(), $absolutePath, $element, $form);
371:
372:
373: $path = trailingslashit($path);
374: $absolutePath = trailingslashit($absolutePath);
375:
376:
377: if (!is_dir($absolutePath)) {
378: wp_mkdir_p($absolutePath);
379: }
380:
381:
382: if (file_exists($absolutePath . $file['name'])) {
383: $count = 1;
384: $newFilenamePath = $absolutePath . $file['name'];
385:
386: while (file_exists($newFilenamePath)) {
387: $newFilename = $count++ . '_' . $file['name'];
388: $newFilenamePath = $absolutePath . $newFilename;
389: }
390:
391: $file['name'] = $newFilename;
392: }
393:
394:
395: if (rename($file['path'], $absolutePath . $file['name']) !== false) {
396: chmod($absolutePath . $file['name'], 0644);
397:
398: $file['path'] = $absolutePath . $file['name'];
399: $file['url'] = Quform::getUploadsUrl($path . $file['name']);
400:
401: return $file;
402: } else {
403: return false;
404: }
405: }
406:
407: 408: 409: 410: 411:
412: public function mergeSessionFiles(Quform_Form $form)
413: {
414: $uploads = $this->session->get(sprintf('%s.uploads', $form->getSessionKey()));
415:
416:
417: $removedUploadUids = isset($_POST['quform_removed_upload_uids']) && Quform::isNonEmptyString($_POST['quform_removed_upload_uids']) ? explode(',', $_POST['quform_removed_upload_uids']) : array();
418:
419: if (is_array($uploads)) {
420: foreach ($uploads as $elementName => $uploadInfo) {
421: if (is_array($uploadInfo['quform_upload_uid'])) {
422:
423: foreach ($uploadInfo['quform_upload_uid'] as $key => $id) {
424: if (in_array($id, $removedUploadUids)) {
425: foreach (self::$fileKeys as $fileKey) {
426: unset($uploads[$elementName][$fileKey][$key]);
427: }
428: unset($uploads[$elementName]['quform_upload_uid'][$key]);
429: }
430: }
431:
432:
433: if ( ! count($uploads[$elementName]['quform_upload_uid'])) {
434: unset($uploads[$elementName]);
435: }
436: } else {
437:
438: if (in_array($uploadInfo['quform_upload_uid'], $removedUploadUids)) {
439: unset($uploads[$elementName]);
440: }
441: }
442: }
443:
444:
445: $_FILES = array_merge($_FILES, $uploads);
446: }
447:
448: $files = $this->session->get(sprintf('%s.files', $form->getSessionKey()));
449:
450: if (is_array($files)) {
451: foreach ($files as $elementName => $value) {
452: $element = $form->getElementByName($elementName);
453:
454: foreach ($value as $key => $file) {
455: if (in_array($file['quform_upload_uid'], $removedUploadUids)) {
456: unset($value[$key]);
457: }
458: }
459:
460: $value = array_values($value);
461:
462: if ($element instanceof Quform_Element_File) {
463: $element->setValue($value);
464: }
465: }
466: }
467: }
468:
469: 470: 471: 472: 473:
474: public function saveFileUploadValuesIntoSession(Quform_Form $form)
475: {
476: foreach ($form->getRecursiveIterator() as $element) {
477: if ( ! $element instanceof Quform_Element_File) {
478: continue;
479: }
480:
481: if ( ! $element->isEmpty()) {
482: $this->session->set($form->getSessionKey() . '.files.' . $element->getName(), $element->getValue());
483: }
484: }
485: }
486:
487: 488: 489: 490: 491:
492: public function saveUploadedFilesIntoSession(Quform_Form $form)
493: {
494: foreach ($form->getRecursiveIterator() as $element) {
495: if ( ! $element instanceof Quform_Element_File) {
496: continue;
497: }
498:
499: $elementName = $element->getName();
500:
501: if ( ! array_key_exists($elementName, $_FILES) || ! is_array($_FILES[$elementName])) {
502: continue;
503: }
504:
505: $uploadsTmpDir = $this->getUploadsTempDir();
506:
507: if ( ! is_dir($uploadsTmpDir)) {
508: wp_mkdir_p($uploadsTmpDir);
509: }
510:
511: if ( ! wp_is_writable($uploadsTmpDir)) {
512: continue;
513: }
514:
515: if ($element->isValid()) {
516: $sessionKey = $form->getSessionKey() . '.uploads.' . $elementName;
517: $files = array();
518:
519: foreach ($_FILES[$elementName]['error'] as $key => $error) {
520: if ($error == UPLOAD_ERR_OK) {
521: if (is_uploaded_file($_FILES[$elementName]['tmp_name'][$key])) {
522: $filename = tempnam($uploadsTmpDir, 'quform');
523: move_uploaded_file($_FILES[$elementName]['tmp_name'][$key], $filename);
524: $_FILES[$elementName]['tmp_name'][$key] = $filename;
525: }
526:
527: foreach (self::$fileKeys as $fileKey) {
528: $files[$fileKey][] = $_FILES[$elementName][$fileKey][$key];
529: $file[$fileKey] = $_FILES[$elementName][$fileKey][$key];
530: }
531:
532: $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();
533: $files['timestamp'][] = time();
534:
535: $element->addFile($file);
536: }
537: }
538:
539: if (count($files)) {
540: $this->session->set($sessionKey, $files);
541: } else {
542: $this->session->forget($sessionKey);
543: }
544: }
545: }
546: }
547:
548: 549: 550: 551: 552: 553:
554: protected function isValidUploadUid($uid)
555: {
556: return is_string($uid) && preg_match('/^[a-zA-Z0-9]{40}$/', $uid);
557: }
558:
559: 560: 561: 562: 563:
564: protected function generateUploadUid()
565: {
566: return Quform::randomString(40);
567: }
568:
569: 570: 571: 572:
573: public function cleanup()
574: {
575: $uploadsTmpDir = $this->getUploadsTempDir();
576:
577: if (is_dir($uploadsTmpDir) && $handle = opendir($uploadsTmpDir)) {
578: clearstatcache();
579: $keepUntil = time() - 21600;
580: while (false !== ($file = readdir($handle))) {
581: $filePath = $uploadsTmpDir . '/' . $file;
582: $mtime = filemtime($filePath);
583: if ($file != '.' && $file != '..' && $mtime < $keepUntil) {
584: @unlink($filePath);
585: }
586: }
587:
588: closedir($handle);
589: }
590: }
591:
592: 593: 594:
595: protected function scheduleCleanup()
596: {
597: if ( ! wp_next_scheduled('quform_upload_cleanup')) {
598: wp_schedule_event(time() + (12 * HOUR_IN_SECONDS), 'twicedaily', 'quform_upload_cleanup');
599: }
600: }
601:
602: 603: 604:
605: protected function unscheduleCleanup()
606: {
607: if ($timestamp = wp_next_scheduled('quform_upload_cleanup')) {
608: wp_unschedule_event($timestamp, 'quform_upload_cleanup');
609: }
610: }
611:
612: 613: 614: 615: 616:
617: public function activate()
618: {
619: $this->scheduleCleanup();
620:
621: $uploadsTmpDir = $this->getUploadsTempDir();
622:
623: if ( ! is_dir($uploadsTmpDir)) {
624: wp_mkdir_p($uploadsTmpDir);
625: }
626: }
627:
628: 629: 630: 631: 632:
633: public function deactivate()
634: {
635: $this->unscheduleCleanup();
636: $this->cleanup();
637: }
638:
639: 640: 641: 642: 643:
644: public function uninstall()
645: {
646: $this->unscheduleCleanup();
647: $this->cleanup();
648: }
649: }
650: