0

I want to create a cookie after a file download.

This is an extension on the existing module: https://www.drupal.org/project/file_utility

Each time you click on a download, a popup opens. For me it is fine to have the visitors e-mail address once. Then direct download is possible without showing the popup again on other download pages.

This part of code is not generating a cookie .. why?

EDIT: $c_response->send(); was missing here.

    $c_response = new \Symfony\Component\HttpFoundation\Response();
    $c_response->headers->setCookie(new \Symfony\Component\HttpFoundation\Cookie('foo', 'bar'));
    $c_response->send();

    // Get cookie value
    $c_request  = new \Symfony\Component\HttpFoundation\Request();
    $c_value = $c_request->cookies->get('foo');

    if($c_value) {
        \Drupal::logger('file_utility')->notice($c_value);
    } else {
        \Drupal::logger('file_utility')->notice("cookie foo is empty");
    }

This is the function where the download logic happens: In this function, first I need to check if the cookie exists. If so, direct download can start. If not, popup must be initiated. The first time, the cookie will be empty.

    public function downloadAction() {
        // Get cookie value
        $c_request = new \Symfony\Component\HttpFoundation\Request();
        $c_value = $c_request->cookies->get('foo');
        \Drupal::logger('file_utility')->notice("cookie: " . $c_value);
        if ($c_value) {
            \Drupal::logger('file_utility')->notice($c_value);
        } else {
            \Drupal::logger('file_utility')->notice("cookie foo is empty");
        }

        /** set cookie if $c_value is empty **/        
        $c_response = new \Symfony\Component\HttpFoundation\Response();
        $c_response->headers->setCookie(new \Symfony\Component\HttpFoundation\Cookie('foo2', 'bar2'));
        $c_response->send();
        /** end set cookie **/

        if (!isset($_GET['f_path'])) {
            return;
        }
        // Process download.
        $f_path_encoded = \Drupal::request()->query->get('f_path');
        $full_path = base64_decode(urldecode($f_path_encoded));
        $force_download = \Drupal::request()->query->get('force_download');
        $response['file_name'] = basename($full_path);
        $uid = \Drupal::currentUser()->id();
        $user = User::load($uid);
        if ($user->hasPermission('file download access') && $force_download == '1') {
            header('Content-Description: File Transfer');
            header('Content-Type: application/octet-stream');
            header('Content-Disposition: attachment; filename="' . basename($response['file_name']) . '"');
            header('Expires: 0');
            header('Content-Transfer-Encoding: binary');
            header('Cache-Control: must-revalidate');
            header('Pragma: public');
            // Flush system output buffer.
            flush();
        } elseif ($user->hasPermission('file download access') && $force_download == '0') {
            $response = new RedirectResponse($full_path);
            $response->send();
            die;
        } else {
            $url = $full_path;
            if (strpos($full_path, 'sites/default') !== FALSE) {
                throw new AccessDeniedHttpException();
            } else {
                $response = new RedirectResponse($url);
                $response->send();
                die;
            }
        }

    }

Extra: Can I set the cookie after the formsubmit? I found that you can add a response to a form_state but that is also not setting the cookie. I have added the first 3 lines in below function, but that is not working at the moment.

     /**
     * AJAX callback handler that displays any errors or a success message.
     */
    public function submitUserInfoFormAjax(array $form, FormStateInterface $form_state) {
        $c_response = new \Symfony\Component\HttpFoundation\Response();
        $c_response->headers->setCookie(new \Symfony\Component\HttpFoundation\Cookie('john', 'do'));
        $form_state->setResponse($c_response);

        $response = new AjaxResponse();
        $value = $form_state->getValues();
        //$name = $value['name'];
        $email = $value['email'];
        $user_ip = isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : \Drupal::request()->getHost();
        $f_path_encoded = \Drupal::request()->query->get('f_path');
        $f_path = base64_decode(urldecode($f_path_encoded));
        $force_download = \Drupal::request()->query->get('force_download');
        // If there are any form errors, re-display the form.
        if ($form_state->hasAnyErrors()) {
            $response->addCommand(new ReplaceCommand('#status_message_div', $form));
        } else {
            $status = parent::save($form, $form_state);

            $entity = $this->entity;
            $response->addCommand(new RedirectCommand('/filedownload?f_path=' . $f_path_encoded . '&force_download=' . $force_download));
            $response->addCommand(new CloseModalDialogCommand());
        }

        return $response;
    }

Important notice for making the cookie available for javascript:

httponly must be set to false in the constructor.

$response->headers->setCookie(new \Symfony\Component\HttpFoundation\Cookie('_john', 'do', time() + 3600, '/', '.yourdomain.com', false, false));
10
  • 3
    You have to send the response to get a browser to set the cookie. Then on the next request, you can get cookies from that request. Creating an arbitrary response and request won't do anything, they're not connected to one another
    – Clive
    Commented Mar 31, 2020 at 9:23
  • The cookie is now created, but that throws up an other issue: the function is also sending out a RedirectResponse. But that is not working anymore because the HttpFoundation Response is already send ...
    – Lance
    Commented Mar 31, 2020 at 9:58
  • 1
    Yeah, I confess I don't really understand what your code would be attempting to do, I thought it was just representative rather than identical. You can't set a cookie and read that same cookie in the same request, it's not conceptually feasible. You'd never need to either, because if you have a variable that you want to set as a cookie, you can do so when you send the response, at the end of the request, and the variable is still available earlier on in that same request. It sounds like what you need to do is override the controller that serves the file, set your cookie there, then hook into..
    – Clive
    Commented Mar 31, 2020 at 10:54
  • 1
    ..the other part of the code, the one that decides whether to show the popup, and check the cookie there
    – Clive
    Commented Mar 31, 2020 at 10:54
  • 1
    Maybe you can better use drupal private temp store for this job: api.drupal.org/api/drupal/…
    – Rick B
    Commented Mar 31, 2020 at 18:06

1 Answer 1

1

I think the problem in your current code is that you're overwriting the response. You should add the cookie to the default AjaxResponse instead:

public function submitUserInfoFormAjax(array $form, FormStateInterface $form_state) {
    $response = new AjaxResponse();

    $response->headers->setCookie(new \Symfony\Component\HttpFoundation\Cookie('john', 'do'));

    $value = $form_state->getValues();
    //$name = $value['name'];
    $email = $value['email'];
    $user_ip = isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : \Drupal::request()->getHost();
    $f_path_encoded = \Drupal::request()->query->get('f_path');
    $f_path = base64_decode(urldecode($f_path_encoded));
    $force_download = \Drupal::request()->query->get('force_download');
    // If there are any form errors, re-display the form.
    if ($form_state->hasAnyErrors()) {
        $response->addCommand(new ReplaceCommand('#status_message_div', $form));
    } else {
        $status = parent::save($form, $form_state);

        $entity = $this->entity;
        $response->addCommand(new RedirectCommand('/filedownload?f_path=' . $f_path_encoded . '&force_download=' . $force_download));
        $response->addCommand(new CloseModalDialogCommand());
    }

    return $response;
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.