简体   繁体   中英

Wordpress send form with multiple files to email without plugin

The aim is to allow users to send their own material for new articles (text and files) to the administrator's email.

There is a form:

<form class="form form-send" enctype="multipart/form-data">
    <div class="send__inputs-block">
        <div class="sliding-label__block send__textarea-block">
            <textarea name="comment" id="comment" class="input textarea send__textarea" placeholder=" "></textarea>
            <label for="comment" class="text text--fz-18 sliding-label">
                <?php echo get_post_meta( get_the_ID(), 'joint_send_comment_text', true ); ?>
            </label>
        </div>
        <div class="sliding-label__block">
            <input id="name" type="text" name="name" class="input input-text" placeholder=" ">
            <label for="name" class="text text--fz-18 sliding-label">
                <?php echo get_post_meta( get_the_ID(), 'joint_send_name_text', true ); ?>
            </label>
        </div>
    </div>
    <div class="send__file-block">
        <input id="file" type="file" name="file" class="input-file" multiple>
        <label for="file" class="label label-file">
            <i class="joint-upload icon"></i>
            <span class="text text--fz-14 upload__text">
                <?php echo get_post_meta( get_the_ID(), 'joint_send_file_text', true ); ?>
            </span>
        </label>
    </div>
    <button type="submit" class="button form-button">
        <span class="button-text">
            <?php echo get_post_meta( get_the_ID(), 'joint_send_submit_text', true ); ?>
        </span>
    </button>
</form>

Here's the JS code:

function formSend(e, form) {
    e.preventDefault()

    if (formValidate(form)) return

    let backFile = jointAjax.ajaxurl
    let curPage = ''

    let formData = new FormData(form)

    if (form.classList.contains('form-contacts')) {
       curPage = 'contacts'
    }
    else if (form.classList.contains('form-send')){
        curPage = 'send'
        let uploadFiles = []
        for (let single of form.file.files) {
            uploadFiles.push(single)
        }
        console.log(uploadFiles[0])
        formData.append('file', uploadFiles)
    }
    else {
        return
    }

    formData.append('action', curPage)

    fetch(backFile, {
       method: 'POST',
       body: formData,
    })
        .then(form.reset())
        .catch(error => {
            error.json().then(response => {
               alert(response.message)
            })
        })
}

Here's the PHP code:

add_action('wp_ajax_send', 'joint_send_send_form');
add_action('wp_ajax_nopriv_send', 'joint_send_send_form');
function joint_send_send_form() {
    global $joint_settings;

    $data = json_encode($_POST);
    $data = json_decode($data, true);

    $attachment = array();
    if (isset($_FILES['file'])) {
        foreach($_FILES['file'] as $key => $file) {
            $attachment[] = $file['tmp_name'] . $file['name'];
        }
    }

    $mailBody = '';

    foreach ($data as $key => $value) {
        if ($key === 'action' || $key === 'file') continue;
        if (!empty($data[$key]))
            $mailBody .= '<p><strong>' . ucfirst($key) . ':</strong> ' . esc_html($value) . '</p>';
    }

    $headers = array(
        'From: Joint Admin <' . SMTP_FROM . '>',
        'content-type: text/html'
    );

    wp_mail(
        $joint_settings['send_mail_to'],
        $joint_settings['send_mail_theme'],
        $mailBody,
        $headers,
        WP_CONTENT_DIR . '\\' . $_FILES['file']['name']
    );

    // wp_send_json_success($_FILES['file']['tmp_name'] . '\\' . $_FILES['file']['name']);

}

I've already reviewed all sorts of forums, articles and videos, but I can't seem to get the task done.

In wp_mail we have to pass the full path to the file, but where do we get that path? And no matter how I try to process several files, function returns only name of last file in reply.

SMTP is set up correctly. Emails are coming in, but no files.

Please help - I don't know what to do anymore.

I figured it out - I looked at the implementation in the Contact form 7 plugin and understood the principles.

The basic idea is to first move the uploaded file to the internal directory of the site, and then send it to the email from there.

In JS I have changed only one thing (you can send files to request without processing):

else if (form.classList.contains('form-send')){
    formData.append('file', form.file.files)
    curPage = 'send'
}

My handler function looks like this:

add_action('wp_ajax_send', 'joint_send_send_form');
add_action('wp_ajax_nopriv_send', 'joint_send_send_form');
function joint_send_send_form() {
    global $joint_settings;

    $data = json_encode($_POST);
    $data = json_decode($data, true);

    $mailBody = '';

    foreach ($data as $key => $value) {
        if ($key === 'action' || $key === 'file') continue;
        if (!empty($data[$key]))
            $mailBody .= '<p><strong>' . ucfirst($key) . ':</strong> ' . esc_html($value) . '</p>';
    }

    $headers = array(
        'From: Joint Admin <' . SMTP_FROM . '>',
        'content-type: text/html'
    );

    $file = $_FILES['file'];
    // joint_array_flatten() - converts multi-dimensional array to a flat array
    $names = joint_array_flatten( $file['name'] );
    $tmp_names = joint_array_flatten( $file['tmp_name'] );

    $uploads = wp_get_upload_dir()['basedir'];
    $uploads_dir = path_join( $uploads, 'joint_uploads' );

    $uploaded_files = array();

    foreach ( $names as $key => $filename ) {
        $tmp_name = $tmp_names[$key];

        if ( empty( $tmp_name ) or ! is_uploaded_file( $tmp_name ) ) {
            continue;
        }

        // joint_antiscript_file_name() - converts a file name to one that is not executable as a script
        $filename = joint_antiscript_file_name( $filename );

        $filename = wp_unique_filename( $uploads_dir, $filename );
        $new_file = path_join( $uploads_dir, $filename );

        if ( false === @move_uploaded_file( $tmp_name, $new_file ) ) {
            wp_send_json_error( json_encode( array('message' => 'Upload error') ) );
            return;
        }

        // Make sure the uploaded file is only readable for the owner process
        chmod( $new_file, 0400 );

        $uploaded_files[] = $new_file;
    }

    wp_mail(
        $joint_settings['send_mail_to'],
        $joint_settings['send_mail_theme'],
        $mailBody,
        $headers,
        $uploaded_files
    );

    // Deletes sent files as they are on the email
    foreach ( $uploaded_files as $filepath ) {
        wp_delete_file( $filepath );
    }
}

I stole a couple of features from Contact form 7. The anti-script is definitely useful, but whether joint_array_flatten is needed - I don't know, but I'm already too tired to think any more.

And at the end I delete the downloaded files from the directory so it doesn't get clogged.

Also, as user @TangentiallyPerpendicular correctly pointed out, you need to add [] to the file-input name in order to send multiple files:

<input id="file" type="file" name="file[]" class="input-file" multiple>

Otherwise, that's it. I hope someday this will help someone else and they won't have to agonise over a similar task as I did)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM