yiisoft / yii2

Yii 2: The Fast, Secure and Professional PHP Framework
http://www.yiiframework.com
BSD 3-Clause "New" or "Revised" License
14.24k stars 6.91k forks source link

Under i18n Captcha image disappears by clicking on it #10261

Closed easyrider7522 closed 8 years ago

easyrider7522 commented 8 years ago

Server: Apache/2.4.16 (Win32) PHP/5.6.12 Yii/2.0.6 (same problem on live Linux server)

Although captchaAction is specified, registerClientScript() generates JS-script with wrong route: "hashKey":"yiiCaptcha/site/captcha" (Should be without site/).

yii\captcha\Captcha is used as widget in the view:

$form->field( $model, 'verifyCode' )->widget( Captcha::className(), [ 'captchaAction' => '/captcha' ]);

Under i18n environment works fine in app's main language but in other languages won't refresh on click.

SilverFire commented 8 years ago

Failed to reproduce on Yii2 Basic app template. Could you try to reproduce your problem on clean Yii2, please?

easyrider7522 commented 8 years ago

It's i18n project with pretty urls support including language identification, which is solved within extended UrlManager. Maybe following will help us both to understand on which side is the issue.

Yii generates following JavaScript for Captcha verification (simplified):

jQuery(document).ready(function () {
    jQuery('#contactform-verifycode-image').yiiCaptcha({
        "refreshUrl":"\/test\/frontend\/web\/ru\/captcha?refresh=1",
        "hashKey":"yiiCaptcha\/\/captcha"
    });
    jQuery('#contact-form').yiiActiveForm([{
            "validate":function (attribute, value, messages, deferred, $form) {
                yii.validation.captcha(value, messages, {
                    "hash":665,
                    "hashKey":"yiiCaptcha/site/captcha",
                    "caseSensitive":false,
                    "message":"Проверочный код введён неверно"
                });
            }
    }], []);
});

If you check the hashKey values in both procedures you can see that different routes were generated by Captcha. The first one is yiiCaptcha//captcha - dunno if its ok, but it seems to work, but the second one yiiCaptcha/site/captcha contains controller name, which is the problem, I think.

At page load the captcha image appears and has src attribute like /test/frontend/web/ru/captcha?v=565771ae05faa, which after click changes to /test/frontend/web/ru/site/captcha?v=565771ae05fab.

No client side JS verification customization was performed.

samdark commented 8 years ago

Looks like your custom URL manager implementation has a problem generating URLs where language isn't specified as in captcha's case.

easyrider7522 commented 8 years ago

I do avoid use of default controller name anywhere, that's why the link looks like it is. Still everything works fine.

Solved this by extending CaptchaValidator where the overridden clientValidateAttribute() removes the default controller name from resulting JS. Same does now also my URL Manager.

I don't insist on changes but still believe that CaptchaValidator should create routes using application-provided rules but not own fixed controller/action pattern.

samdark commented 8 years ago

Could you point to where CaptchaValidator creates URL using controller/action directly?

easyrider7522 commented 8 years ago

CaptchaValidator.php:94

samdark commented 8 years ago

That has nothing to do with the URL.

easyrider7522 commented 8 years ago

Validation fails if I live the controller name there

easyrider7522 commented 8 years ago

My point is that if I can call the widget with captchaAction like this:

$form->field( $model, 'verifyCode' )->widget( Captcha::className(), [ 'captchaAction' => '/captcha' ]);

then it must be also possible to set captchaAction of the model rule like this:

public function rules()
{
    return [
        [ 'verifyCode', 'captcha', 'captchaAction' => '/captcha' ]
    ]
}

which is not possible without controller name but leads to the whole problem I faced.

samdark commented 8 years ago

Action should be always specifed in terms of route, not as string.

easyrider7522 commented 8 years ago

Anyway, thank you for your attention. You helped me already many times without knowing it, and I think this way think many satisfied Yii programers.

P.S.: This is my navbar "in terms of route" ;-)

echo Nav::widget([
    'options'   => [ 'class' => 'navbar-nav navbar-left' ],
    'items'     => [
        [ 'label' => Yii::t( 'navmenu', 'home'    ), 'url' => [ '/'        ]],
        [ 'label' => Yii::t( 'navmenu', 'about'   ), 'url' => [ '/about'   ]],
        [ 'label' => Yii::t( 'navmenu', 'booking' ), 'url' => [ '/booking' ]],
        [ 'label' => Yii::t( 'navmenu', 'contact' ), 'url' => [ '/contact' ]],
    ],
]);
samdark commented 8 years ago

Yep, that's not correct. Route is controller/action pair, not the resulting URL.