<parent>wcf.acp.menu.link.language.server</parent>
<permissions>admin.language.canManageLanguage</permissions>
</acpmenuitem>-->
+
+ <acpmenuitem name="wcf.acp.menu.link.captcha">
+ <parent>wcf.acp.menu.link.display</parent>
+ <showorder>5</showorder>
+ </acpmenuitem>
+ <acpmenuitem name="wcf.acp.menu.link.captcha.question.list">
+ <parent>wcf.acp.menu.link.captcha</parent>
+ <controller><![CDATA[wcf\acp\page\CaptchaQuestionListPage]]></controller>
+ <permissions>admin.captcha.canManageCaptchaQuestion</permissions>
+ </acpmenuitem>
+ <acpmenuitem name="wcf.acp.menu.link.captcha.question.add">
+ <parent>wcf.acp.menu.link.captcha</parent>
+ <controller><![CDATA[wcf\acp\form\CaptchaQuestionAddForm]]></controller>
+ <permissions>admin.captcha.canManageCaptchaQuestion</permissions>
+ </acpmenuitem>
<!-- /language -->
<acpmenuitem name="wcf.acp.menu.link.content">
<conditiongroup>userOptions</conditiongroup>
</type>
<!-- /ad conditions -->
+
+ <!-- captcha types -->
+ <type>
+ <name>com.woltlab.wcf.recaptcha</name>
+ <definitionname>com.woltlab.wcf.captcha</definitionname>
+ <classname><![CDATA[wcf\system\captcha\RecaptchaHandler]]></classname>
+ </type>
+ <type>
+ <name>com.woltlab.wcf.captchaQuestion</name>
+ <definitionname>com.woltlab.wcf.captcha</definitionname>
+ <classname><![CDATA[wcf\system\captcha\CaptchaQuestionHandler]]></classname>
+ </type>
+ <!-- captcha types -->
</import>
</data>
<name>com.woltlab.wcf.condition.ad</name>
<interfacename><![CDATA[wcf\system\condition\IContentCondition]]></interfacename>
</definition>
+
+ <definition>
+ <name>com.woltlab.wcf.captcha</name>
+ <interfacename><![CDATA[wcf\system\captcha\ICaptchaHandler]]></interfacename>
+ </definition>
</import>
</data>
<category name="security.antispam">
<parent>security</parent>
</category>
+ <category name="security.antispam.captcha">
+ <parent>security.antispam</parent>
+ </category>
<category name="security.antispam.recaptcha">
<parent>security.antispam</parent>
<options>module_system_recaptcha</options>
</option>
<!-- /security.blacklist -->
+ <!-- security.antispam -->
+ <option name="register_captcha_type">
+ <categoryname>security.antispam.captcha</categoryname>
+ <optiontype>captchaSelect</optiontype>
+ <defaultvalue>com.woltlab.wcf.recaptcha</defaultvalue>
+ <allowemptyvalue>1</allowemptyvalue>
+ </option>
+ <option name="lost_password_captcha_type">
+ <categoryname>security.antispam.captcha</categoryname>
+ <optiontype>captchaSelect</optiontype>
+ <defaultvalue>com.woltlab.wcf.recaptcha</defaultvalue>
+ <allowemptyvalue>1</allowemptyvalue>
+ </option>
+ <option name="profile_mail_captcha_type">
+ <categoryname>security.antispam.captcha</categoryname>
+ <optiontype>captchaSelect</optiontype>
+ <defaultvalue>com.woltlab.wcf.recaptcha</defaultvalue>
+ <allowemptyvalue>1</allowemptyvalue>
+ </option>
+ <option name="search_captcha_type">
+ <categoryname>security.antispam.captcha</categoryname>
+ <optiontype>boolean</optiontype>
+ <optiontype>captchaSelect</optiontype>
+ <defaultvalue>com.woltlab.wcf.recaptcha</defaultvalue>
+ <allowemptyvalue>1</allowemptyvalue>
+ </option>
+ <option name="message_captcha_type">
+ <categoryname>security.antispam.captcha</categoryname>
+ <optiontype>boolean</optiontype>
+ <optiontype>captchaSelect</optiontype>
+ <defaultvalue>com.woltlab.wcf.recaptcha</defaultvalue>
+ <allowemptyvalue>1</allowemptyvalue>
+ </option>
+ <!-- /security.antispam -->
+
<option name="recaptcha_publickey">
<categoryname>security.antispam.recaptcha</categoryname>
<optiontype>text</optiontype>
<defaultvalue>6LfOlMYSAAAAADvo3s4puBAYDqI-6YK2ybe7BJE5</defaultvalue>
<showorder>1</showorder>
</option>
-
<option name="recaptcha_privatekey">
<categoryname>security.antispam.recaptcha</categoryname>
<optiontype>text</optiontype>
</option>
<!-- /user.3rdPartyAuth -->
- <!-- user.security -->
- <option name="register_use_captcha">
- <categoryname>security.antispam</categoryname>
- <optiontype>boolean</optiontype>
- <defaultvalue><![CDATA[1]]></defaultvalue>
- <options>module_system_recaptcha</options>
- </option>
- <option name="lost_password_use_captcha">
- <categoryname>security.antispam</categoryname>
- <optiontype>boolean</optiontype>
- <defaultvalue><![CDATA[1]]></defaultvalue>
- <options>module_system_recaptcha</options>
- </option>
- <option name="profile_mail_use_captcha">
- <categoryname>security.antispam</categoryname>
- <optiontype>boolean</optiontype>
- <defaultvalue><![CDATA[1]]></defaultvalue>
- <options>module_system_recaptcha</options>
- </option>
- <!-- /user.security -->
-
<!-- user.avatar -->
<option name="max_avatar_width">
<categoryname>user.avatar</categoryname>
<minvalue>5</minvalue>
<maxvalue>40</maxvalue>
</option>
- <option name="search_use_captcha">
- <categoryname>security.antispam</categoryname>
- <optiontype>boolean</optiontype>
- <options>module_system_recaptcha</options>
- </option>
<option name="search_default_sort_field">
<categoryname>message.search</categoryname>
<maxvalue>100</maxvalue>
</option>
<!-- /message.general.poll -->
+
+ <!-- deprecated -->
+ <option name="register_use_captcha">
+ <categoryname>security.antispam</categoryname>
+ <optiontype>boolean</optiontype>
+ <defaultvalue><![CDATA[1]]></defaultvalue>
+ <options>module_system_recaptcha</options>
+ <hidden>1</hidden>
+ </option>
+ <option name="lost_password_use_captcha">
+ <categoryname>security.antispam</categoryname>
+ <optiontype>boolean</optiontype>
+ <defaultvalue><![CDATA[1]]></defaultvalue>
+ <options>module_system_recaptcha</options>
+ <hidden>1</hidden>
+ </option>
+ <option name="profile_mail_use_captcha">
+ <categoryname>security.antispam</categoryname>
+ <optiontype>boolean</optiontype>
+ <defaultvalue><![CDATA[1]]></defaultvalue>
+ <options>module_system_recaptcha</options>
+ <hidden>1</hidden>
+ </option>
+ <option name="search_use_captcha">
+ <categoryname>security.antispam</categoryname>
+ <optiontype>boolean</optiontype>
+ <options>module_system_recaptcha</options>
+ <hidden>1</hidden>
+ </option>
+ <!-- deprecated -->
</options>
</import>
'wcf.comment.button.response.add': '{lang}wcf.comment.button.response.add{/lang}',
'wcf.comment.delete.confirmMessage': '{lang}wcf.comment.delete.confirmMessage{/lang}',
'wcf.comment.description': '{lang}wcf.comment.description{/lang}',
+ 'wcf.comment.guestDialog.title': '{lang}wcf.comment.guestDialog.title{/lang}',
'wcf.comment.more': '{lang}wcf.comment.more{/lang}',
'wcf.comment.response.add': '{lang}wcf.comment.response.add{/lang}',
'wcf.comment.response.more': '{lang}wcf.comment.response.more{/lang}',
--- /dev/null
+{if $captchaObjectType}
+ {@$captchaObjectType->getProcessor()->getFormElement()}
+{/if}
--- /dev/null
+<input type="hidden" name="captchaQuestion" value="{$captchaQuestion}" />
+
+{if !$captchaQuestionAnswered}
+ <fieldset>
+ <legend>{lang}wcf.captcha.question.captcha{/lang}</legend>
+ <small>{lang}wcf.captcha.question.captcha.description{/lang}</small>
+
+ <dl{if (($errorType|isset && $errorType|is_array && $errorType[captchaAnswer]|isset) || ($errorField|isset && $errorField == 'captchaAnswer'))} class="formError"{/if}>
+ <dt><label for="captchaAnswer">{lang}{$captchaQuestionObject->question}{/lang}</label></dt>
+ <dd>
+ <input type="text" id="captchaAnswer" name="captchaAnswer" class="medium" />
+ {if (($errorType|isset && $errorType|is_array && $errorType[captchaAnswer]|isset) || ($errorField|isset && $errorField == 'captchaAnswer'))}
+ {if $errorType|is_array && $errorType[captchaAnswer]|isset}
+ {assign var='__errorType' value=$errorType[captchaAnswer]}
+ {else}
+ {assign var='__errorType' value=$errorType}
+ {/if}
+
+ {if $__errorType == 'empty'}
+ <small class="innerError">{lang}wcf.global.form.error.empty{/lang}</small>
+ {else}
+ <small class="innerError">{lang}wcf.captcha.question.answer.error.{$__errorType}{/lang}</small>
+ {/if}
+ {/if}
+ </dd>
+ </dl>
+ </fieldset>
+
+ {if !$ajaxCaptcha|empty}
+ <script data-relocate="true">
+ //<![CDATA[
+ $(function() {
+ WCF.System.Captcha.addCallback('{$captchaID}', function() {
+ return {
+ captchaAnswer: $('#captchaAnswer').val(),
+ captchaQuestion: '{$captchaQuestion}'
+ };
+ });
+ });
+ //]]>
+ </script>
+ {/if}
+{/if}
<div>
<fieldset>
- <dl>
+ <dl{if $errorType[username]|isset} class="formError"{/if}>
<dt><label for="username">{lang}wcf.user.username{/lang}</label></dt>
<dd>
<input type="text" id="username" name="username" value="{$username}" required="required" class="long" autofocus="true" />
+ {if $errorType[username]|isset}
+ <small class="innerError">
+ {if $errorType[username] == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.user.username.error.$errorType[username]{/lang}
+ {/if}
+ </small>
+ {/if}
</dd>
</dl>
</fieldset>
- {if MODULE_SYSTEM_RECAPTCHA}
- {include file='recaptcha'}
- {/if}
+ {include file='captcha'}
</div>
<div class="formSubmit">
{event name='fieldsets'}
- {if $useCaptcha}{include file='recaptcha'}{/if}
+ {include file='captcha'}
</div>
-
+
<div class="formSubmit">
<input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s" />
{@SECURITY_TOKEN_INPUT_TAG}
{event name='fieldsets'}
- {if $useCaptcha}
- {include file='recaptcha'}
- {/if}
+ {include file='captcha'}
</div>
<div class="formSubmit">
-<fieldset>
- <legend><label for="recaptcha_response_field">{lang}wcf.recaptcha.title{/lang}</label></legend>
- <small>{lang}wcf.recaptcha.description{/lang}</small>
-
- <dl class="wide reCaptcha{if $errorField|isset && $errorField == 'recaptchaString'} formError{/if}">
- {if !$ajaxRecaptcha|isset || !$ajaxRecaptcha}
- <script data-relocate="true">
- //<![CDATA[
- var RecaptchaOptions = {
- lang: '{@$recaptchaLanguageCode}',
- theme : 'custom'
- }
- //]]>
- </script>
- {/if}
- <dt class="jsOnly">
- <label for="recaptcha_response_field">reCAPTCHA</label>
- </dt>
- <dd class="jsOnly">
- <div id="recaptcha_image" class="framed"></div>
- <input type="text" id="recaptcha_response_field" name="recaptcha_response_field" class="medium marginTop" />
- {if $errorField|isset && $errorField == 'recaptchaString'}
- <small class="innerError">
- {if $errorType == 'empty'}{lang}wcf.global.form.error.empty{/lang}{/if}
- {if $errorType == 'false'}{lang}wcf.recaptcha.error.recaptchaString.false{/lang}{/if}
- </small>
- {/if}
- </dd>
-
- {event name='fields'}
-
- <dd class="jsOnly">
- <ul class="buttonList smallButtons">
- <li><a href="javascript:Recaptcha.reload()" class="button small"><span class="icon icon16 icon-repeat"></span> <span>{lang}wcf.recaptcha.reload{/lang}</span></a></li>
- <li class="recaptcha_only_if_image"><a href="javascript:Recaptcha.switch_type('audio')" class="button small"><span class="icon icon16 icon-volume-up"></span> <span>{lang}wcf.recaptcha.audio{/lang}</span></a></li>
- <li class="recaptcha_only_if_audio"><a href="javascript:Recaptcha.switch_type('image')" class="button small"><span class="icon icon16 icon-eye-open"></span> <span>{lang}wcf.recaptcha.image{/lang}</span></a></li>
- <li><a href="javascript:Recaptcha.showhelp()" class="button small"><span class="icon icon16 icon-question-sign"></span> <span>{lang}wcf.recaptcha.help{/lang}</span></a></li>
- {event name='buttons'}
- </ul>
- </dd>
+{if $recaptchaLegacyMode|empty}
+ {include file='captcha'}
+{else}
+ <fieldset>
+ <legend><label for="recaptcha_response_field">{lang}wcf.recaptcha.title{/lang}</label></legend>
+ <small>{lang}wcf.recaptcha.description{/lang}</small>
- {if !$ajaxRecaptcha|isset || !$ajaxRecaptcha}
- <script data-relocate="true" src="http{if $recaptchaUseSSL}s{/if}://www.google.com/recaptcha/api/challenge?k={$recaptchaPublicKey}"></script>
- <noscript>
- <dd>
- <iframe src="http{if $recaptchaUseSSL}s{/if}://www.google.com/recaptcha/api/noscript?k={$recaptchaPublicKey}" height="300" width="500" seamless="seamless"></iframe><br />
- <textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
- <input type="hidden" name="recaptcha_response_field" value="manual_challenge" />
- </dd>
- {if $errorField == 'recaptchaString'}
+ <dl class="wide reCaptcha{if $errorField|isset && $errorField == 'recaptchaString'} formError{/if}">
+ {if !$ajaxCaptcha|isset || !$ajaxCaptcha}
+ <script data-relocate="true">
+ //<![CDATA[
+ var RecaptchaOptions = {
+ lang: '{@$recaptchaLanguageCode}',
+ theme : 'custom'
+ }
+ //]]>
+ </script>
+ {/if}
+ <dt class="jsOnly">
+ <label for="recaptcha_response_field">reCAPTCHA</label>
+ </dt>
+ <dd class="jsOnly">
+ <div id="recaptcha_image" class="framed"></div>
+ <input type="text" id="recaptcha_response_field" name="recaptcha_response_field" class="medium marginTop" />
+ {if (($errorType|isset && $errorType|is_array && $errorType[recaptchaString]|isset) || ($errorField|isset && $errorField == 'recaptchaString'))}
+ {if $errorType|is_array && $errorType[recaptchaString]|isset}
+ {assign var='__errorType' value=$errorType[recaptchaString]}
+ {else}
+ {assign var='__errorType' value=$errorType}
+ {/if}
<small class="innerError">
- {if $errorType == 'empty'}{lang}wcf.global.form.error.empty{/lang}{/if}
- {if $errorType == 'false'}{lang}wcf.recaptcha.error.recaptchaString.false{/lang}{/if}
+ {if $__errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.recaptcha.error.recaptchaString.{$__errorType}{/lang}
+ {/if}
</small>
{/if}
- </noscript>
- {else}
- <script data-relocate="true">
- //<![CDATA[
- Recaptcha.create("{$recaptchaPublicKey}", "recaptcha_image", {
- lang: '{@$recaptchaLanguageCode}',
- theme : 'custom'
- });
- //]]>
- </script>
- {/if}
- </dl>
-</fieldset>
+ </dd>
+
+ {event name='fields'}
+
+ <dd class="jsOnly">
+ <ul class="buttonList smallButtons">
+ <li><a href="javascript:Recaptcha.reload()" class="button small"><span class="icon icon16 icon-repeat"></span> <span>{lang}wcf.recaptcha.reload{/lang}</span></a></li>
+ <li class="recaptcha_only_if_image"><a href="javascript:Recaptcha.switch_type('audio')" class="button small"><span class="icon icon16 icon-volume-up"></span> <span>{lang}wcf.recaptcha.audio{/lang}</span></a></li>
+ <li class="recaptcha_only_if_audio"><a href="javascript:Recaptcha.switch_type('image')" class="button small"><span class="icon icon16 icon-eye-open"></span> <span>{lang}wcf.recaptcha.image{/lang}</span></a></li>
+ <li><a href="javascript:Recaptcha.showhelp()" class="button small"><span class="icon icon16 icon-question-sign"></span> <span>{lang}wcf.recaptcha.help{/lang}</span></a></li>
+ {event name='buttons'}
+ </ul>
+ </dd>
+
+ {if !$ajaxCaptcha|isset || !$ajaxCaptcha}
+ <script data-relocate="true" src="http{if $recaptchaUseSSL}s{/if}://www.google.com/recaptcha/api/challenge?k={$recaptchaPublicKey}"></script>
+ <noscript>
+ <dd>
+ <iframe src="http{if $recaptchaUseSSL}s{/if}://www.google.com/recaptcha/api/noscript?k={$recaptchaPublicKey}" height="300" width="500" seamless="seamless"></iframe><br />
+ <textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
+ <input type="hidden" name="recaptcha_response_field" value="manual_challenge" />
+ </dd>
+ {if (($errorType|isset && $errorType|is_array && $errorType[recaptchaString]|isset) || ($errorField|isset && $errorField == 'recaptchaString'))}
+ {if $errorType|is_array && $errorType[recaptchaString]|isset}
+ {assign var='__errorType' value=$errorType[recaptchaString]}
+ {else}
+ {assign var='__errorType' value=$errorType}
+ {/if}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.recaptcha.error.recaptchaString.{$__errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </noscript>
+ {else}
+ <script data-relocate="true">
+ //<![CDATA[
+ Recaptcha.create("{$recaptchaPublicKey}", "recaptcha_image", {
+ lang: '{@$recaptchaLanguageCode}',
+ theme : 'custom'
+ });
+
+ WCF.System.Captcha.addCallback('{$captchaID}', function() {
+ return {
+ recaptcha_challenge_field: Recaptcha.get_challenge(),
+ recaptcha_response_field: Recaptcha.get_response()
+ };
+ });
+ //]]>
+ </script>
+ {/if}
+ </dl>
+ </fieldset>
+{/if}
{event name='fieldsets'}
- {if $useCaptcha}
- {if $errorType.recaptchaString|isset}
- {assign var=errorField value='recaptchaString'}
- {assign var=errorType value=$errorType.recaptchaString}
- {/if}
- {include file='recaptcha'}
- {/if}
+ {include file='captcha'}
</div>
<div class="formSubmit">
{include file='footer'}
</body>
-</html>
\ No newline at end of file
+</html>
</fieldset>
{event name='fieldsets'}
- {if $useCaptcha}{include file='recaptcha'}{/if}
+
+ {include file='captcha'}
{foreach from=$objectTypes key=objectTypeName item=objectType}
{if $objectType->isAccessible() && $objectType->getFormTemplateName()}
</script>
</body>
-</html>
\ No newline at end of file
+</html>
<category name="admin.template">
<parent>admin.display</parent>
</category>
+ <category name="admin.captcha">
+ <parent>admin.display</parent>
+ </category>
<category name="admin.content">
<parent>admin</parent>
<defaultvalue>0</defaultvalue>
<admindefaultvalue>1</admindefaultvalue>
</option>
+
+ <option name="admin.captcha.canManageCaptchaQuestion">
+ <categoryname>admin.display.captcha</categoryname>
+ <optiontype>boolean</optiontype>
+ <defaultvalue>0</defaultvalue>
+ <admindefaultvalue>1</admindefaultvalue>
+ </option>
</options>
</import>
</data>
--- /dev/null
+{include file='header' pageTitle='wcf.acp.captcha.question.'|concat:$action}
+
+<header class="boxHeadline">
+ <h1>{lang}wcf.acp.captcha.question.{$action}{/lang}</h1>
+</header>
+
+{include file='formError'}
+
+{if $success|isset}
+ <p class="success">{lang}wcf.global.success.{$action}{/lang}</p>
+{/if}
+
+<div class="contentNavigation">
+ <nav>
+ <ul>
+ <li><a href="{link controller='CaptchaQuestionList'}{/link}" class="button"><span class="icon icon16 icon-list"></span> <span>{lang}wcf.acp.menu.link.captcha.question.list{/lang}</span></a></li>
+
+ {event name='contentNavigationButtons'}
+ </ul>
+ </nav>
+</div>
+
+<form id="adForm" method="post" action="{if $action == 'add'}{link controller='CaptchaQuestionAdd'}{/link}{else}{link controller='CaptchaQuestionEdit' id=$captchaQuestion->questionID}{/link}{/if}">
+ <div class="container containerPadding marginTop">
+ <fieldset>
+ <legend>{lang}wcf.global.form.data{/lang}</legend>
+
+ <dl{if $errorField == 'question'} class="formError"{/if}>
+ <dt><label for="question">{lang}wcf.acp.captcha.question.question{/lang}</label></dt>
+ <dd>
+ <input type="text" id="question" name="question" value="{$i18nPlainValues[question]}" required="required" autofocus="autofocus" class="long" />
+ {if $errorField == 'question'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {elseif $errorType == 'multilingual'}
+ {lang}wcf.global.form.error.multilingual{/lang}
+ {else}
+ {lang}wcf.acp.captcha.question.question.error.{$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+ {include file='multipleLanguageInputJavascript' elementIdentifier='question' forceSelection=false}
+
+ <dl{if $errorField == 'answers'} class="formError"{/if}>
+ <dt><label for="answers">{lang}wcf.acp.captcha.question.answers{/lang}</label></dt>
+ <dd>
+ <textarea id="answers" name="answers" cols="40" rows="10">{$i18nPlainValues[answers]}</textarea>
+ <small>{lang}wcf.acp.captcha.question.answers.description{/lang}</small>
+ {if $errorField == 'answers'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {elseif $errorType == 'multilingual'}
+ {lang}wcf.global.form.error.multilingual{/lang}
+ {else}
+ {lang}wcf.acp.captcha.question.answers.error.{$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+ {include file='multipleLanguageInputJavascript' elementIdentifier='answers' forceSelection=false}
+
+ <dl>
+ <dt></dt>
+ <dd>
+ <label><input type="checkbox" name="isDisabled" value="1"{if $isDisabled} checked="checked"{/if} /> {lang}wcf.acp.captcha.question.isDisabled{/lang}</label>
+ </dd>
+ </dl>
+
+ {event name='dataFields'}
+ </fieldset>
+
+ {event name='fieldsets'}
+ </div>
+
+ <div class="formSubmit">
+ <input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s" />
+ {@SECURITY_TOKEN_INPUT_TAG}
+ </div>
+</form>
+
+{include file='footer'}
--- /dev/null
+{include file='header' pageTitle='wcf.acp.captcha.question.list'}
+
+<header class="boxHeadline">
+ <h1>{lang}wcf.acp.captcha.question.list{/lang}</h1>
+</header>
+
+<script data-relocate="true">
+ //<![CDATA[
+ $(function() {
+ new WCF.Action.Delete('wcf\\data\\captcha\\question\\CaptchaQuestionAction', '.jsQuestionRow');
+ new WCF.Action.Toggle('wcf\\data\\captcha\\question\\CaptchaQuestionAction', '.jsQuestionRow');
+
+ var options = { };
+ {if $pages > 1}
+ options.refreshPage = true;
+ {if $pages == $pageNo}
+ options.updatePageNumber = -1;
+ {/if}
+ {else}
+ options.emptyMessage = '{lang}wcf.global.noItems{/lang}';
+ {/if}
+
+ new WCF.Table.EmptyTableHandler($('#captchaQuestionTabelContainer'), 'jsQuestionRow', options);
+ });
+ //]]>
+</script>
+
+<div class="contentNavigation">
+ {pages print=true assign=pagesLinks controller="CaptchaQuestionList" link="pageNo=%d"}
+
+ <nav>
+ <ul>
+ <li><a href="{link controller='CaptchaQuestionAdd'}{/link}" class="button"><span class="icon icon16 icon-plus"></span> <span>{lang}wcf.acp.captcha.question.add{/lang}</span></a></li>
+
+ {event name='contentNavigationButtonsTop'}
+ </ul>
+ </nav>
+</div>
+
+{hascontent}
+ <div id="captchaQuestionTabelContainer" class="tabularBox tabularBoxTitle marginTop">
+ <header>
+ <h2>{lang}wcf.acp.captcha.question.list{/lang} <span class="badge badgeInverse">{#$items}</span></h2>
+ </header>
+
+ <table class="table">
+ <thead>
+ <tr>
+ <th class="columnID columnQuestionID active ASC" colspan="2">{lang}wcf.global.objectID{/lang}</th>
+ <th class="columnText columnQuestion">{lang}wcf.acp.captcha.question.question{/lang}</th>
+
+ {event name='columnHeads'}
+ </tr>
+ </thead>
+
+ <tbody>
+ {content}
+ {foreach from=$objects item='question'}
+ <tr class="jsQuestionRow">
+ <td class="columnIcon">
+ <span class="icon icon16 icon-check{if $question->isDisabled}-empty{/if} jsToggleButton jsTooltip pointer" title="{lang}wcf.global.button.{if $question->isDisabled}enable{else}disable{/if}{/lang}" data-object-id="{@$question->questionID}" data-disable-message="{lang}wcf.global.button.disable{/lang}" data-enable-message="{lang}wcf.global.button.enable{/lang}"></span>
+ <a href="{link controller='CaptchaQuestionEdit' id=$question->questionID}{/link}" title="{lang}wcf.global.button.edit{/lang}" class="jsTooltip"><span class="icon icon16 icon-pencil"></span></a>
+ <span class="icon icon16 icon-remove jsDeleteButton jsTooltip pointer" title="{lang}wcf.global.button.delete{/lang}" data-object-id="{@$question->questionID}" data-confirm-message="{lang}wcf.acp.captcha.question.delete.confirmMessage{/lang}"></span>
+
+ {event name='rowButtons'}
+ </td>
+ <td class="columnID columnQuestionID">{$question->questionID}</td>
+ <td class="columnText columnQuestion"><a href="{link controller='CaptchaQuestionEdit' id=$question->questionID}{/link}">{lang}{$question->question}{/lang}</a></td>
+
+ {event name='columns'}
+ </tr>
+ {/foreach}
+ {/content}
+ </tbody>
+ </table>
+ </div>
+{hascontentelse}
+ <p class="info">{lang}wcf.global.noItems{/lang}</p>
+{/hascontent}
+
+<div class="contentNavigation">
+ {@$pagesLinks}
+
+ <nav>
+ <ul>
+ <li><a href="{link controller='CaptchaQuestionAdd'}{/link}" class="button"><span class="icon icon16 icon-plus"></span> <span>{lang}wcf.acp.captcha.question.add{/lang}</span></a></li>
+
+ {event name='contentNavigationButtonsBottom'}
+ </ul>
+ </nav>
+</div>
+
+{include file='footer'}
+
\ No newline at end of file
* @var jQuery
*/
_guestDialog: null,
-
- /**
- * true if the guest has to solve a recaptcha challenge to save the comment
- * @var boolean
- */
- _useRecaptcha: true,
/**
* Initializes the WCF.Comment.Handler class.
WCF.DOMNodeInsertedHandler.execute();
WCF.DOMNodeInsertedHandler.addCallback('WCF.Comment.Handler', $.proxy(this._domNodeInserted, this));
+
+ WCF.System.ObjectStore.add('WCF.Comment.Handler', this);
},
/**
*/
_loadResponses: function(event) {
this._loadResponsesExecute($(event.currentTarget).disable().data('commentID'), false);
-
},
/**
this._commentData = $data;
// check if guest dialog has already been loaded
- if (this._guestDialog === null) {
- this._proxy.setOption('data', {
- actionName: 'getGuestDialog',
- className: 'wcf\\data\\comment\\CommentAction',
- parameters: {
- data: {
- message: $value,
- objectID: this._container.data('objectID'),
- objectTypeID: this._container.data('objectTypeID')
- }
+ this._proxy.setOption('data', {
+ actionName: 'getGuestDialog',
+ className: 'wcf\\data\\comment\\CommentAction',
+ parameters: {
+ data: {
+ message: $value,
+ objectID: this._container.data('objectID'),
+ objectTypeID: this._container.data('objectTypeID')
}
- });
- this._proxy.sendRequest();
- }
- else {
- // request a new recaptcha
- if (this._useRecaptcha) {
- Recaptcha.reload();
}
-
- this._guestDialog.find('input[type="submit"]').enable();
-
- this._guestDialog.wcfDialog('open');
- }
+ });
+ this._proxy.sendRequest();
}
else {
this._proxy.setOption('data', {
_success: function(data, textStatus, jqXHR) {
switch (data.actionName) {
case 'addComment':
- if (data.returnValues.errors) {
- this._handleGuestDialogErrors(data.returnValues.errors);
+ if (data.returnValues.guestDialog) {
+ this._createGuestDialog(data.returnValues.guestDialog, data.returnValues.useCaptcha);
}
else {
this._commentAdd.find('textarea').val('').blur().trigger('updateHeight');
break;
case 'addResponse':
- if (data.returnValues.errors) {
- this._handleGuestDialogErrors(data.returnValues.errors);
+ if (data.returnValues.guestDialog) {
+ this._createGuestDialog(data.returnValues.guestDialog, data.returnValues.useCaptcha);
}
else {
var $comment = this._comments[data.returnValues.commentID];
break;
case 'getGuestDialog':
- this._createGuestDialog(data);
+ this._createGuestDialog(data.returnValues.template, data.returnValues.useCaptcha);
break;
}
},
/**
- * Creates the guest dialog based on the given return data from the AJAX
- * request.
+ * Creates the guest dialog using the given template.
*
- * @param object data
+ * @param string template
+ * @param boolean useCaptcha
*/
- _createGuestDialog: function(data) {
- this._guestDialog = $('<div id="commentAddGuestDialog" />').append(data.returnValues.template).hide().appendTo(document.body);
+ _createGuestDialog: function(template, useCaptcha) {
+ var $refreshGuestDialog = !!this._guestDialog;
+ if (!this._guestDialog) {
+ this._guestDialog = $('<div id="commentAddGuestDialog" />').hide().appendTo(document.body);
+ }
+
+ this._guestDialog.html(template);
+ this._guestDialog.data('useCaptcha', useCaptcha);
// bind submit event listeners
this._guestDialog.find('input[type="submit"]').click($.proxy(this._submit, this));
-
this._guestDialog.find('input[type="text"]').keydown($.proxy(this._keyDown, this));
-
- // check if recaptcha is used
- this._useRecaptcha = this._guestDialog.find('dl.reCaptcha').length > 0;
this._guestDialog.wcfDialog({
'title': WCF.Language.get('wcf.comment.guestDialog.title')
});
},
-
+
/**
* Handles clicking enter in the input fields of the guest dialog by
* submitting it.
this._submit();
}
},
-
- /**
- * Handles errors during creation of a comment or response due to the input
- * in the guest dialog.
- *
- * @param object errors
- */
- _handleGuestDialogErrors: function(errors) {
- if (errors.username) {
- var $usernameInput = this._guestDialog.find('input[name="username"]');
- var $errorMessage = $usernameInput.next('.innerError');
- if (!$errorMessage.length) {
- $errorMessage = $('<small class="innerError" />').text(errors.username).insertAfter($usernameInput);
- }
- else {
- $errorMessage.text(errors.username).show();
- }
- }
-
- if (errors.recaptcha) {
- Recaptcha.reload();
-
- var $recaptchaInput = this._guestDialog.find('input[name="recaptcha_response_field"]');
- var $errorMessage = $recaptchaInput.next('.innerError');
- if (!$errorMessage.length) {
- $errorMessage = $('<small class="innerError" />').text(errors.recaptcha).insertAfter($recaptchaInput);
- }
- else {
- $errorMessage.text(errors.recaptcha).show();
- }
- }
-
- this._guestDialog.find('input[type="submit"]').enable();
- },
/**
* Handles submitting the guest dialog.
* @param Event event
*/
_submit: function(event) {
- var $submit = true;
-
- this._guestDialog.find('input[type="submit"]').enable();
-
- // validate username
- var $usernameInput = this._guestDialog.find('input[name="username"]');
- var $username = $usernameInput.val();
- var $usernameErrorMessage = $usernameInput.next('.innerError');
- if (!$username) {
- $submit = false;
- if (!$usernameErrorMessage.length) {
- $usernameErrorMessage = $('<small class="innerError" />').text(WCF.Language.get('wcf.global.form.error.empty')).insertAfter($usernameInput);
- }
- else {
- $usernameErrorMessage.text(WCF.Language.get('wcf.global.form.error.empty')).show();
- }
- }
-
- // validate recaptcha
- if (this._useRecaptcha) {
- var $recaptchaInput = this._guestDialog.find('input[name="recaptcha_response_field"]');
- var $recaptchaResponse = $recaptchaInput.val();
- var $recaptchaErrorMessage = $recaptchaInput.next('.innerError');
- if (!$recaptchaResponse) {
- $submit = false;
- if (!$recaptchaErrorMessage.length) {
- $recaptchaErrorMessage = $('<small class="innerError" />').text(WCF.Language.get('wcf.global.form.error.empty')).insertAfter($recaptchaInput);
- }
- else {
- $recaptchaErrorMessage.text(WCF.Language.get('wcf.global.form.error.empty')).show();
- }
- }
- }
-
- if ($submit) {
- if ($usernameErrorMessage.length) {
- $usernameErrorMessage.hide();
- }
-
- if (this._useRecaptcha && $recaptchaErrorMessage.length) {
- $recaptchaErrorMessage.hide();
- }
-
- var $data = this._commentData;
- $data.username = $username;
-
- var $parameters = {
- data: $data
- };
-
- if (this._useRecaptcha) {
- $parameters.recaptchaChallenge = Recaptcha.get_challenge();
- $parameters.recaptchaResponse = Recaptcha.get_response();
- }
-
- this._proxy.setOption('data', {
- actionName: this._commentData.commentID ? 'addResponse' : 'addComment',
- className: 'wcf\\data\\comment\\CommentAction',
- parameters: $parameters
- });
- this._proxy.sendRequest();
-
- this._guestDialog.find('input[type="submit"]').disable();
- }
+ var $requestData = {
+ actionName: this._commentData.commentID ? 'addResponse' : 'addComment',
+ className: 'wcf\\data\\comment\\CommentAction'
+ };
+
+ var $data = this._commentData;
+ $data.username = this._guestDialog.find('input[name="username"]').val();
+
+ $requestData.parameters = {
+ data: $data
+ };
+
+ $requestData = $.extend(WCF.System.Captcha.getData('commentAdd'), $requestData);
+
+ this._proxy.setOption('data', $requestData);
+ this._proxy.sendRequest();
},
/**
}
};
+/**
+ * Stores captcha callbacks used for captchas in AJAX contexts.
+ */
+WCF.System.Captcha = {
+ /**
+ * adds call
+ * @var object<function>
+ */
+ _captchas: { },
+
+ /**
+ * Adds a callback for a certain captcha.
+ *
+ * @param string captchaID
+ * @param function callback
+ */
+ addCallback: function(captchaID, callback) {
+ if (!$.isFunction(callback)) {
+ console.debug('[WCF.System.Captcha] Given callback is no function');
+ return;
+ }
+
+ this._captchas[captchaID] = callback;
+ },
+
+ /**
+ * Returns the captcha data for the captcha with the given id.
+ *
+ * @return object
+ */
+ getData: function(captchaID) {
+ if (this._captchas[captchaID] === undefined) {
+ console.debug('[WCF.System.Captcha] Unknow captcha id "' + captchaID + '"');
+ return;
+ }
+
+ return this._captchas[captchaID]();
+ },
+
+ /**
+ * Removes the callback with the given captcha id.
+ */
+ removeCallback: function(captchaID) {
+ delete this._captchas[captchaID];
+ }
+};
+
WCF.System.Page = { };
WCF.System.Page.Multiple = Class.extend({
--- /dev/null
+<?php
+namespace wcf\acp\form;
+use wcf\data\captcha\question\CaptchaQuestionAction;
+use wcf\data\captcha\question\CaptchaQuestionEditor;
+use wcf\form\AbstractForm;
+use wcf\system\exception\UserInputException;
+use wcf\system\language\I18nHandler;
+use wcf\system\Regex;
+use wcf\system\WCF;
+use wcf\util\StringUtil;
+
+/**
+ * Shows the form to create a new captcha question.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage acp.form
+ * @category Community Framework
+ */
+class CaptchaQuestionAddForm extends AbstractForm {
+ /**
+ * @see \wcf\page\AbstractPage::$activeMenuItem
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.captcha.question.add';
+
+ /**
+ * invalid regex in answers
+ * @var string
+ */
+ public $invalidRegex = '';
+
+ /**
+ * 1 if the question is disabled
+ * @var integer
+ */
+ public $isDisabled = 0;
+
+ /**
+ * @see \wcf\page\AbstractPage::$neededPermissions
+ */
+ public $neededPermissions = array('admin.display.canManageCaptchaQuestion');
+
+ /**
+ * @see \wcf\page\IPage::assignVariables()
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ I18nHandler::getInstance()->assignVariables();
+
+ WCF::getTPL()->assign(array(
+ 'action' => 'add',
+ 'isDisabled' => $this->isDisabled,
+ 'invalidRegex' => $this->invalidRegex
+ ));
+ }
+
+ /**
+ * @see \wcf\form\IForm::readFormParameters()
+ */
+ public function readFormParameters() {
+ parent::readFormParameters();
+
+ I18nHandler::getInstance()->readValues();
+
+ if (isset($_POST['isDisabled'])) $this->isDisabled = 1;
+ }
+
+ /**
+ * @see \wcf\page\IPage::readParameters()
+ */
+ public function readParameters() {
+ parent::readParameters();
+
+ I18nHandler::getInstance()->register('question');
+ I18nHandler::getInstance()->register('answers');
+ }
+
+ /**
+ * @see \wcf\form\IForm::save()
+ */
+ public function save() {
+ parent::save();
+
+ $this->objectAction = new CaptchaQuestionAction(array(), 'create', array(
+ 'data' => array_merge($this->additionalFields, array(
+ 'answers' => I18nHandler::getInstance()->isPlainValue('answers') ? I18nHandler::getInstance()->getValue('answers') : '',
+ 'isDisabled' => $this->isDisabled,
+ 'question' => I18nHandler::getInstance()->isPlainValue('question') ? I18nHandler::getInstance()->getValue('question') : ''
+ ))
+ ));
+ $returnValues = $this->objectAction->executeAction();
+ $questionID = $returnValues['returnValues']->questionID;
+
+ // set i18n values
+ $questionUpdates = array();
+ if (!I18nHandler::getInstance()->isPlainValue('question')) {
+ I18nHandler::getInstance()->save('question', 'wcf.captcha.question.question.question'.$questionID, 'wcf.captcha.question', 1);
+
+ $questionUpdates['question'] = 'wcf.captcha.question.question.question'.$questionID;
+ }
+ if (!I18nHandler::getInstance()->isPlainValue('answers')) {
+ I18nHandler::getInstance()->save('answers', 'wcf.captcha.question.answers.question'.$questionID, 'wcf.captcha.question', 1);
+
+ $questionUpdates['answers'] = 'wcf.captcha.question.answers.question'.$questionID;
+ }
+
+ if (!empty($questionUpdates)) {
+ $questionEditor = new CaptchaQuestionEditor($returnValues['returnValues']);
+ $questionEditor->update($questionUpdates);
+ }
+
+ $this->saved();
+
+ // reset values
+ I18nHandler::getInstance()->reset();
+ $this->isDisabled = 0;
+
+ // show success message
+ WCF::getTPL()->assign('success', true);
+ }
+
+ /**
+ * @see \wcf\form\IForm::validate()
+ */
+ public function validate() {
+ parent::validate();
+
+ // validate question
+ if (!I18nHandler::getInstance()->validateValue('question')) {
+ if (I18nHandler::getInstance()->isPlainValue('question')) {
+ throw new UserInputException('question');
+ }
+ else {
+ throw new UserInputException('question', 'multilingual');
+ }
+ }
+
+ // validate answers
+ if (!I18nHandler::getInstance()->validateValue('answers')) {
+ if (I18nHandler::getInstance()->isPlainValue('answers')) {
+ throw new UserInputException('answers');
+ }
+ else {
+ throw new UserInputException('answers', 'multilingual');
+ }
+ }
+
+ if (I18nHandler::getInstance()->isPlainValue('answers')) {
+ $answers = explode("\n", StringUtil::unifyNewlines(I18nHandler::getInstance()->getValue('answers')));
+ foreach ($answers as $answer) {
+ if (mb_substr($answer, 0, 1) == '~' && mb_substr($answer, -1, 1) == '~') {
+ $regexLength = mb_strlen($answer) - 2;
+ if (!$regexLength || !Regex::compile(mb_substr($answer, 1, $regexLength))->isValid()) {
+ $this->invalidRegex = $answer;
+
+ throw new UserInputException('answers', 'regexNotValid');
+ }
+ }
+ }
+ }
+ foreach (I18nHandler::getInstance()->getValues('answers') as $languageAnswers) {
+ $answers = explode("\n", StringUtil::unifyNewlines($languageAnswers));
+ foreach ($answers as $answer) {
+ if (mb_substr($answer, 0, 1) == '~' && mb_substr($answer, -1, 1) == '~') {
+ $regexLength = mb_strlen($answer) - 2;
+ if (!$regexLength || !Regex::compile(mb_substr($answer, 1, $regexLength))->isValid()) {
+ $this->invalidRegex = $answer;
+
+ throw new UserInputException('answers', 'regexNotValid');
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null
+<?php
+namespace wcf\acp\form;
+use wcf\data\captcha\question\CaptchaQuestion;
+use wcf\data\captcha\question\CaptchaQuestionAction;
+use wcf\data\captcha\question\CaptchaQuestionEditor;
+use wcf\form\AbstractForm;
+use wcf\system\exception\IllegalLinkException;
+use wcf\system\language\I18nHandler;
+use wcf\system\WCF;
+
+/**
+ * Shows the form to edit an existing captcha question.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage acp.form
+ * @category Community Framework
+ */
+class CaptchaQuestionEditForm extends CaptchaQuestionAddForm {
+ /**
+ * @see \wcf\page\AbstractPage::$activeMenuItem
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.captcha';
+
+ /**
+ * edited captcha question
+ * @var \wcf\data\captcha\question\CaptchaQuestion
+ */
+ public $captchaQuestion = null;
+
+ /**
+ * id of the edited captcha question
+ * @var integer
+ */
+ public $captchaQuestionID = 0;
+
+ /**
+ * @see \wcf\page\IPage::assignVariables()
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ I18nHandler::getInstance()->assignVariables(!empty($_POST));
+
+ WCF::getTPL()->assign(array(
+ 'action' => 'edit',
+ 'captchaQuestion' => $this->captchaQuestion
+ ));
+ }
+
+ /**
+ * @see \wcf\page\IPage::readData()
+ */
+ public function readData() {
+ parent::readData();
+
+ if (empty($_POST)) {
+ I18nHandler::getInstance()->setOptions('question', 1, $this->captchaQuestion->question, 'wcf.captcha.question.question.question\d+');
+ I18nHandler::getInstance()->setOptions('answers', 1, $this->captchaQuestion->answers, 'wcf.captcha.question.question.answers\d+');
+
+ $this->isDisabled = $this->captchaQuestion->isDisabled;
+ }
+ }
+
+ /**
+ * @see \wcf\page\IPage::readParameters()
+ */
+ public function readParameters() {
+ parent::readParameters();
+
+ if (isset($_REQUEST['id'])) $this->captchaQuestionID = intval($_REQUEST['id']);
+ $this->captchaQuestion = new CaptchaQuestion($this->captchaQuestionID);
+ if (!$this->captchaQuestion->questionID) {
+ throw new IllegalLinkException();
+ }
+ }
+
+ /**
+ * @see \wcf\form\IForm::save()
+ */
+ public function save() {
+ AbstractForm::save();
+
+ if (I18nHandler::getInstance()->isPlainValue('question')) {
+ if ($this->captchaQuestion->question == 'wcf.captcha.question.question.question'.$this->captchaQuestion->questionID) {
+ I18nHandler::getInstance()->remove($this->captchaQuestion->question);
+ }
+ }
+ else {
+ I18nHandler::getInstance()->save('question', 'wcf.captcha.question.question.question'.$this->captchaQuestion->questionID, 'wcf.captcha.question', 1);
+ }
+
+ if (I18nHandler::getInstance()->isPlainValue('answers')) {
+ if ($this->captchaQuestion->answers == 'wcf.captcha.question.question.answers'.$this->captchaQuestion->questionID) {
+ I18nHandler::getInstance()->remove($this->captchaQuestion->answers);
+ }
+ }
+ else {
+ I18nHandler::getInstance()->save('answers', 'wcf.captcha.question.question.answers'.$this->captchaQuestion->questionID, 'wcf.captcha.question', 1);
+ }
+
+ $this->objectAction = new CaptchaQuestionAction(array($this->captchaQuestion), 'update', array(
+ 'data' => array_merge($this->additionalFields, array(
+ 'answers' => I18nHandler::getInstance()->isPlainValue('answers') ? I18nHandler::getInstance()->getValue('answers') : 'wcf.captcha.question.question.answers'.$this->captchaQuestion->questionID,
+ 'isDisabled' => $this->isDisabled,
+ 'question' => I18nHandler::getInstance()->isPlainValue('question') ? I18nHandler::getInstance()->getValue('question') : 'wcf.captcha.question.question.question'.$this->captchaQuestion->questionID
+ ))
+ ));
+ $this->objectAction->executeAction();
+
+ $this->saved();
+
+ // show success message
+ WCF::getTPL()->assign('success', true);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\acp\page;
+use wcf\page\MultipleLinkPage;
+
+/**
+ * Lists the available captcha questions.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage acp.page
+ * @category Community Framework
+ */
+class CaptchaQuestionListPage extends MultipleLinkPage {
+ /**
+ * @see \wcf\page\AbstractPage::$activeMenuItem
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.captcha.question.list';
+
+ /**
+ * @see \wcf\page\AbstractPage::$neededPermissions
+ */
+ public $neededPermissions = array('admin.captcha.canManageCaptchaQuestion');
+
+ /**
+ * @see \wcf\page\MultipleLinkPage::$objectListClassName
+ */
+ public $objectListClassName = 'wcf\data\captcha\question\CaptchaQuestionList';
+
+ /**
+ * @see \wcf\page\MultipleLinkPage::$sortField
+ */
+ public $sortField = 'questionID';
+
+ /**
+ * @see \wcf\page\MultipleLinkPage::$sortOrder
+ */
+ public $sortOrder = 'ASC';
+}
WCF::getSession()->register('__facebookData', $userData);
// we assume that bots won't register on facebook first
+ // todo: captcha
WCF::getSession()->register('recaptchaDone', true);
WCF::getSession()->update();
WCF::getSession()->register('__githubToken', $data['access_token']);
// we assume that bots won't register on github first
+ // todo: captcha
WCF::getSession()->register('recaptchaDone', true);
WCF::getSession()->update();
WCF::getSession()->register('__googleData', $userData);
// we assume that bots won't register on facebook first
+ // todo: captcha
WCF::getSession()->register('recaptchaDone', true);
WCF::getSession()->update();
WCF::getSession()->register('__twitterData', $data);
// we assume that bots won't register on twitter first
+ // todo: captcha
WCF::getSession()->register('recaptchaDone', true);
WCF::getSession()->update();
--- /dev/null
+<?php
+namespace wcf\data\captcha\question;
+use wcf\data\DatabaseObject;
+use wcf\system\Regex;
+use wcf\system\WCF;
+use wcf\util\StringUtil;
+
+/**
+ * Represents a captcha question.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.captcha.question
+ * @category Community Framework
+ */
+class CaptchaQuestion extends DatabaseObject {
+ /**
+ * @see \wcf\data\DatabaseObject::$databaseTableName
+ */
+ protected static $databaseTableName = 'captcha_question';
+
+ /**
+ * @see \wcf\data\DatabaseObject::$databaseTableIndexName
+ */
+ protected static $databaseTableIndexName = 'questionID';
+
+ /**
+ * Returns true if the given user input is an answer to this question.
+ *
+ * @param string $answer
+ * @return boolean
+ */
+ public function isAnswer($answer) {
+ $answers = explode("\n", StringUtil::unifyNewlines(WCF::getLanguage()->get($this->answers)));
+ foreach ($answers as $__answer) {
+ if (mb_substr($__answer, 0, 1) == '~' && mb_substr($__answer, -1, 1) == '~') {
+ if (Regex::compile(mb_substr($__answer, 1, mb_strlen($__answer) - 2))->match($answer)) {
+ return true;
+ }
+
+ continue;
+ }
+ else if ($__answer == $answer) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\captcha\question;
+use wcf\data\AbstractDatabaseObjectAction;
+use wcf\data\IToggleAction;
+
+/**
+ * Executes captcha question-related actions.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.captcha.question
+ * @category Community Framework
+ */
+class CaptchaQuestionAction extends AbstractDatabaseObjectAction implements IToggleAction {
+ /**
+ * @see \wcf\data\AbstractDatabaseObjectAction::$permissionsDelete
+ */
+ protected $permissionsDelete = array('admin.captcha.canManageCaptchaQuestion');
+
+ /**
+ * @see \wcf\data\AbstractDatabaseObjectAction::$permissionsUpdate
+ */
+ protected $permissionsUpdate = array('admin.captcha.canManageCaptchaQuestion');
+
+ /**
+ * @see \wcf\data\IToggleAction::toggle()
+ */
+ public function toggle() {
+ foreach ($this->objects as $question) {
+ $question->update(array(
+ 'isDisabled' => $question->isDisabled ? 0 : 1
+ ));
+ }
+ }
+
+ /**
+ * @see \wcf\data\IToggleAction::validateToggle()
+ */
+ public function validateToggle() {
+ parent::validateUpdate();
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\captcha\question;
+use wcf\data\DatabaseObjectEditor;
+use wcf\data\IEditableCachedObject;
+use wcf\system\cache\builder\CaptchaQuestionCacheBuilder;
+
+/**
+ * Provides functions to edit captcha questions.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.captcha.question
+ * @category Community Framework
+ */
+class CaptchaQuestionEditor extends DatabaseObjectEditor implements IEditableCachedObject {
+ /**
+ * @see \wcf\data\DatabaseObjectDecorator::$baseClass
+ */
+ protected static $baseClass = 'wcf\data\captcha\question\CaptchaQuestion';
+
+ /**
+ * @see \wcf\data\IEditableCachedObject::resetCache()
+ */
+ public static function resetCache() {
+ CaptchaQuestionCacheBuilder::getInstance()->reset();
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\captcha\question;
+use wcf\data\DatabaseObjectList;
+
+/**
+ * Represents a list of captcha questions.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.captcha.question
+ * @category Community Framework
+ */
+class CaptchaQuestionList extends DatabaseObjectList { }
use wcf\data\object\type\ObjectTypeCache;
use wcf\data\user\UserProfile;
use wcf\data\AbstractDatabaseObjectAction;
+use wcf\system\captcha\CaptchaHandler;
use wcf\system\comment\CommentHandler;
use wcf\system\exception\PermissionDeniedException;
use wcf\system\exception\UserInputException;
use wcf\system\like\LikeHandler;
-use wcf\system\recaptcha\RecaptchaHandler;
use wcf\system\user\activity\event\UserActivityEventHandler;
use wcf\system\user\notification\object\CommentResponseUserNotificationObject;
use wcf\system\user\notification\object\CommentUserNotificationObject;
*/
protected $allowGuestAccess = array('addComment', 'addResponse', 'loadComments', 'getGuestDialog');
+ /**
+ * captcha object type used for comments
+ * @var \wcf\data\object\type\ObjectType
+ */
+ public $captchaObjectType = null;
+
/**
* @see \wcf\data\AbstractDatabaseObjectAction::$className
*/
$this->readInteger('objectID', false, 'data');
$this->validateUsername();
- $this->validateRecaptcha();
+ $this->validateCaptcha();
$this->validateMessage();
$objectType = $this->validateObjectType();
*/
public function addComment() {
if (!empty($this->validationErrors)) {
+ if (!empty($this->parameters['data']['username'])) {
+ WCF::getSession()->register('username', $this->parameters['data']['username']);
+ }
+ WCF::getTPL()->assign('errorType', $this->validationErrors);
+
+ $guestDialog = $this->getGuestDialog();
return array(
- 'errors' => $this->validationErrors
+ 'useCaptcha' => $guestDialog['useCaptcha'],
+ 'guestDialog' => $guestDialog['template']
);
}
// save last comment time for flood control
WCF::getSession()->register('lastCommentTime', $this->createdComment->time);
- // unmark recaptcha as done for furture requests
- WCF::getSession()->unregister('recaptchaDone');
+ // reset captcha for future requests
+ if ($this->captchaObjectType) {
+ $this->captchaObjectType->getProcessor()->reset();
+ }
}
return array(
$this->validateMessage();
$this->validateUsername();
- $this->validateRecaptcha();
+ $this->validateCaptcha();
// validate comment id
$this->validateCommentID();
*/
public function addResponse() {
if (!empty($this->validationErrors)) {
+ if (!empty($this->parameters['data']['username'])) {
+ WCF::getSession()->register('username', $this->parameters['data']['username']);
+ }
+ WCF::getTPL()->assign('errorType', $this->validationErrors);
+
+ $guestDialog = $this->getGuestDialog();
return array(
- 'errors' => $this->validationErrors
+ 'useCaptcha' => $guestDialog['useCaptcha'],
+ 'guestDialog' => $guestDialog['template']
);
}
// save last comment time for flood control
WCF::getSession()->register('lastCommentTime', $this->createdResponse->time);
- // unmark recaptcha as done for furture requests
- WCF::getSession()->unregister('recaptchaDone');
+ // reset captcha for future requests
+ if ($this->captchaObjectType) {
+ $this->captchaObjectType->getProcessor()->reset();
+ }
}
return array(
* @return array
*/
public function getGuestDialog() {
- RecaptchaHandler::getInstance()->assignVariables();
+ if (MESSAGE_CAPTCHA_TYPE) {
+ $captchaObjectType = CaptchaHandler::getInstance()->getObjectTypeByName(MESSAGE_CAPTCHA_TYPE);
+ if ($captchaObjectType === null) {
+ throw new SystemException("Unknown captcha object type with name '".MESSAGE_CAPTCHA_TYPE."'");
+ }
+
+ if (!$captchaObjectType->getProcessor()->isAvailable()) {
+ $captchaObjectType = null;
+ }
+ }
return array(
+ 'useCaptcha' => $captchaObjectType !== null,
'template' => WCF::getTPL()->fetch('commentAddGuestDialog', 'wcf', array(
- 'ajaxRecaptcha' => true,
+ 'ajaxCaptcha' => true,
+ 'captchaID' => 'commentAdd',
+ 'captchaObjectType' => $captchaObjectType,
'username' => WCF::getSession()->getVar('username')
))
);
}
}
catch (UserInputException $e) {
- if ($e->getType() == 'empty') {
- $this->validationErrors['username'] = WCF::getLanguage()->get('wcf.global.form.error.empty');
- }
- else {
- $this->validationErrors['username'] = WCF::getLanguage()->get('wcf.user.username.error.'.$e->getType());
- }
+ $this->validationErrors['username'] = $e->getType();
}
}
-
+
/**
- * Validates the recaptcha challenge.
+ * Validates the captcha challenge.
*/
- protected function validateRecaptcha() {
- if (WCF::getUser()->userID || !MODULE_SYSTEM_RECAPTCHA || WCF::getSession()->getVar('recaptchaDone')) return;
+ protected function validateCaptcha() {
+ if (WCF::getUser()->userID) return;
- $this->readString('recaptchaChallenge');
- $this->readString('recaptchaResponse');
+ if (MESSAGE_CAPTCHA_TYPE) {
+ $this->captchaObjectType = CaptchaHandler::getInstance()->getObjectTypeByName(MESSAGE_CAPTCHA_TYPE);
+ if ($this->captchaObjectType === null) {
+ throw new SystemException("Unknown captcha object type with name '".MESSAGE_CAPTCHA_TYPE."'");
+ }
+
+ if (!$this->captchaObjectType->getProcessor()->isAvailable()) {
+ $this->captchaObjectType = null;
+ }
+ }
+
+ if ($this->captchaObjectType === null) return;
try {
- RecaptchaHandler::getInstance()->validate($this->parameters['recaptchaChallenge'], $this->parameters['recaptchaResponse']);
+ $this->captchaObjectType->getProcessor()->readFormParameters();
+ $this->captchaObjectType->getProcessor()->validate();
}
catch (UserInputException $e) {
- $this->validationErrors['recaptcha'] = WCF::getLanguage()->get('wcf.recaptcha.error.recaptchaString.false');
+ $this->validationErrors = array_merge($this->validationErrors,
+ array(
+ $e->getField() => $e->getType()
+ )
+ );
}
}
--- /dev/null
+<?php
+namespace wcf\form;
+use wcf\system\captcha\CaptchaHandler;
+use wcf\system\exception\SystemException;
+use wcf\system\recaptcha\RecaptchaHandler;
+use wcf\system\WCF;
+use wcf\util\StringUtil;
+
+/**
+ * Abstract implementation of a form using captcha.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage form
+ * @category Community Framework
+ */
+abstract class AbstractCaptchaForm extends AbstractForm {
+ /**
+ * captcha object type object
+ * @var \wcf\data\object\type\ObjectType
+ */
+ public $captchaObjectType = null;
+
+ /**
+ * name of the captcha object type; if empty, captcha is disabled
+ * @var string
+ */
+ public $captchaObjectTypeName = '';
+
+ /**
+ * challenge (legacy property from RecaptchaForm, do not use!)
+ * @var string
+ */
+ public $challenge = '';
+
+ /**
+ * response (legacy property from RecaptchaForm, do not use!)
+ * @var string
+ */
+ public $response = '';
+
+ /**
+ * true if recaptcha is used (legacy property from RecaptchaForm, do not use!)
+ * @var boolean
+ */
+ public $useCaptcha = true;
+
+ /**
+ * @see \wcf\page\IPage::assignVariables()
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ WCF::getTPL()->assign(array(
+ 'captchaObjectType' => $this->captchaObjectType
+ ));
+
+ if (!$this->captchaObjectType) {
+ RecaptchaHandler::getInstance()->assignVariables();
+ WCF::getTPL()->assign(array(
+ 'useCaptcha' => $this->useCaptcha
+ ));
+ }
+ }
+
+ /**
+ * @see \wcf\page\IPage::readData()
+ */
+ public function readData() {
+ if ($this->captchaObjectTypeName) {
+ $this->captchaObjectType = CaptchaHandler::getInstance()->getObjectTypeByName($this->captchaObjectTypeName);
+ if ($this->captchaObjectType === null) {
+ throw new SystemException("Unknown captcha object type with name '".$this->captchaObjectTypeName."'");
+ }
+
+ if (!$this->captchaObjectType->getProcessor()->isAvailable()) {
+ $this->captchaObjectType = null;
+ }
+ }
+
+ parent::readData();
+ }
+
+ /**
+ * @see \wcf\form\IForm::readFormParameters()
+ */
+ public function readFormParameters() {
+ parent::readFormParameters();
+
+ if ($this->captchaObjectType) {
+ $this->captchaObjectType->getProcessor()->readFormParameters();
+ }
+ else if ($this->useCaptcha) {
+ if (isset($_POST['recaptcha_challenge_field'])) $this->challenge = StringUtil::trim($_POST['recaptcha_challenge_field']);
+ if (isset($_POST['recaptcha_response_field'])) $this->response = StringUtil::trim($_POST['recaptcha_response_field']);
+ }
+ }
+
+ /**
+ * @see \wcf\page\IPage::readParameters()
+ */
+ public function readParameters() {
+ parent::readParameters();
+
+ if ($this->captchaObjectType === null && (!MODULE_SYSTEM_RECAPTCHA || WCF::getUser()->userID || WCF::getSession()->getVar('recaptchaDone'))) {
+ $this->useCaptcha = false;
+ }
+ }
+
+ /**
+ * @see \wcf\form\IForm::save()
+ */
+ public function save() {
+ parent::save();
+
+ if ($this->captchaObjectType) {
+ $this->captchaObjectType->getProcessor()->reset();
+ }
+ else {
+ WCF::getSession()->unregister('recaptchaDone');
+ }
+ }
+
+ /**
+ * @see \wcf\form\IForm::validate()
+ */
+ public function validate() {
+ parent::validate();
+
+ $this->validateCaptcha();
+ }
+
+ /**
+ * Validates the captcha.
+ */
+ protected function validateCaptcha() {
+ if ($this->captchaObjectType) {
+ $this->captchaObjectType->getProcessor()->validate();
+ }
+ else if ($this->useCaptcha) {
+ RecaptchaHandler::getInstance()->validate($this->challenge, $this->response);
+ $this->useCaptcha = false;
+ }
+ }
+}
* @subpackage form
* @category Community Framework
*/
-class LostPasswordForm extends RecaptchaForm {
+class LostPasswordForm extends AbstractCaptchaForm {
const AVAILABLE_DURING_OFFLINE_MODE = true;
/**
public $user;
/**
- * @see \wcf\form\RecaptchaForm::$useCaptcha
+ * @see \wcf\form\CaptchaForm::$captchaObjectTypeName
*/
- public $useCaptcha = LOST_PASSWORD_USE_CAPTCHA;
+ public $captchaObjectTypeName = LOST_PASSWORD_CAPTCHA_TYPE;
/**
* @see \wcf\form\IForm::readFormParameters()
* @subpackage form
* @category Community Framework
*/
-class MailForm extends RecaptchaForm {
+class MailForm extends AbstractCaptchaForm {
/**
- * @see \wcf\form\RecaptchaForm::$useCaptcha
+ * @see \wcf\form\AbstractCaptchaForm::$captchaObjectTypeName
*/
- public $useCaptcha = PROFILE_MAIL_USE_CAPTCHA;
+ public $captchaObjectTypeName = PROFILE_MAIL_CAPTCHA_TYPE;
/**
* recipient's user id
* @subpackage form
* @category Community Framework
*/
-abstract class MessageForm extends RecaptchaForm {
+abstract class MessageForm extends AbstractCaptchaForm {
/**
* name of the permission which contains the allowed BBCodes
* @var string
* @package com.woltlab.wcf
* @subpackage form
* @category Community Framework
+ * @deprecated since 2.1
*/
abstract class RecaptchaForm extends AbstractForm {
/**
use wcf\data\user\UserEditor;
use wcf\data\user\UserProfile;
use wcf\data\user\UserProfileAction;
+use wcf\system\captcha\CaptchaHandler;
use wcf\system\exception\NamedUserException;
use wcf\system\exception\PermissionDeniedException;
+use wcf\system\exception\SystemException;
use wcf\system\exception\UserInputException;
use wcf\system\language\LanguageFactory;
use wcf\system\mail\Mail;
-use wcf\system\recaptcha\RecaptchaHandler;
use wcf\system\request\LinkHandler;
use wcf\system\user\authentication\UserAuthenticationFactory;
use wcf\system\Regex;
* @category Community Framework
*/
class RegisterForm extends UserAddForm {
- /**
- * recaptcha challenge
- * @var string
- */
- public $challenge = '';
-
/**
* @see \wcf\page\AbstractPage::$enableTracking
*/
public $message = '';
/**
- * recaptcha response
- * @var string
+ * captcha object type object
+ * @var \wcf\data\object\type\ObjectType
*/
- public $response = '';
+ public $captchaObjectType = null;
/**
- * enable recaptcha
- * @var boolean
+ * name of the captcha object type; if empty, captcha is disabled
+ * @var string
*/
- public $useCaptcha = REGISTER_USE_CAPTCHA;
+ public $captchaObjectTypeName = REGISTER_CAPTCHA_TYPE;
/**
* field names
exit;
}
- if (!MODULE_SYSTEM_RECAPTCHA || WCF::getSession()->getVar('recaptchaDone')) {
- $this->useCaptcha = false;
- }
-
if (WCF::getSession()->getVar('__3rdPartyProvider')) {
$this->isExternalAuthentication = true;
}
if (isset($_POST[$this->randomFieldNames['confirmPassword']])) $this->confirmPassword = $_POST[$this->randomFieldNames['confirmPassword']];
$this->groupIDs = array();
- if (isset($_POST['recaptcha_challenge_field'])) $this->challenge = StringUtil::trim($_POST['recaptcha_challenge_field']);
- if (isset($_POST['recaptcha_response_field'])) $this->response = StringUtil::trim($_POST['recaptcha_response_field']);
+
+ if ($this->captchaObjectType) {
+ $this->captchaObjectType->getProcessor()->readFormParameters();
+ }
}
/**
*/
public function validate() {
// validate captcha first
- if ($this->useCaptcha) {
- $this->validateCaptcha();
- }
+ $this->validateCaptcha();
parent::validate();
* @see \wcf\page\IPage::readData()
*/
public function readData() {
+ if ($this->captchaObjectTypeName) {
+ $this->captchaObjectType = CaptchaHandler::getInstance()->getObjectTypeByName($this->captchaObjectTypeName);
+ if ($this->captchaObjectType === null) {
+ throw new SystemException("Unknown captcha object type with id '".$this->captchaObjectTypeName."'");
+ }
+
+ if (!$this->captchaObjectType->getProcessor()->isAvailable()) {
+ $this->captchaObjectType = null;
+ }
+ }
+
parent::readData();
if (empty($_POST)) {
public function assignVariables() {
parent::assignVariables();
- RecaptchaHandler::getInstance()->assignVariables();
WCF::getTPL()->assign(array(
+ 'captchaObjectType' => $this->captchaObjectType,
'isExternalAuthentication' => $this->isExternalAuthentication,
- 'useCaptcha' => $this->useCaptcha,
'randomFieldNames' => $this->randomFieldNames
));
}
* Validates the captcha.
*/
protected function validateCaptcha() {
- if ($this->useCaptcha) {
+ if ($this->captchaObjectType) {
try {
- RecaptchaHandler::getInstance()->validate($this->challenge, $this->response);
- $this->useCaptcha = false;
+ $this->captchaObjectType->getProcessor()->validate();
}
catch (UserInputException $e) {
$this->errorType[$e->getField()] = $e->getType();
$mail->send();
}
+ if ($this->captchaObjectType) {
+ $this->captchaObjectType->getProcessor()->reset();
+ }
+
// login user
UserAuthenticationFactory::getInstance()->getUserAuthentication()->storeAccessData($user, $this->username, $this->password);
- WCF::getSession()->unregister('recaptchaDone');
WCF::getSession()->unregister('registrationRandomFieldNames');
WCF::getSession()->unregister('registrationStartTime');
$this->saved();
* @subpackage form
* @category Community Framework
*/
-class SearchForm extends RecaptchaForm {
+class SearchForm extends AbstractCaptchaForm {
/**
* list of additional conditions
* @var array<string>
public $username = '';
/**
- * @see \wcf\form\RecaptchaForm::$useCaptcha
+ * @see \wcf\form\AbstractCaptchaForm::$captchaObjectTypeName
*/
- public $useCaptcha = SEARCH_USE_CAPTCHA;
+ public $captchaObjectTypeName = SEARCH_CAPTCHA_TYPE;
/**
* parameters used for previous search
*/
public function getUserIDs() {
$userIDs = array();
-
+
// username
if (!empty($this->username)) {
$sql = "SELECT userID
*/
public $signatureCache = null;
- /**
- * @see \wcf\form\RecaptchaForm::$useCaptcha
- */
- public $useCaptacha = false;
-
/**
* @see \wcf\form\MessageForm::$allowedBBCodesPermission
*/
--- /dev/null
+<?php
+namespace wcf\system\cache\builder;
+use wcf\data\captcha\question\CaptchaQuestionList;
+
+/**
+ * Caches the enabled captcha questions.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.cache.builder
+ * @category Community Framework
+ */
+class CaptchaQuestionCacheBuilder extends AbstractCacheBuilder {
+ /**
+ * @see \wcf\system\cache\builder\AbstractCacheBuilder::rebuild()
+ */
+ public function rebuild(array $parameters) {
+ $questionList = new CaptchaQuestionList();
+ $questionList->getConditionBuilder()->add('isDisabled = ?', array(0));
+ $questionList->readObjects();
+
+ return $questionList->getObjects();
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\captcha;
+use wcf\data\object\type\ObjectTypeCache;
+use wcf\system\SingletonFactory;
+use wcf\system\WCF;
+
+/**
+ * Handles captchas.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.captcha
+ * @category Community Framework
+ */
+class CaptchaHandler extends SingletonFactory {
+ /**
+ * Returns the available captcha types for selection.
+ *
+ * @return array<string>
+ */
+ public function getCaptchaSelection() {
+ $selection = array();
+ foreach ($this->objectTypes as $objectType) {
+ if ($objectType->getProcessor()->isAvailable()) {
+ $selection[$objectType->objectType] = WCF::getLanguage()->get('wcf.captcha.'.$objectType->objectType);
+ }
+ }
+
+ return $selection;
+ }
+
+ /**
+ * Returns the captcha object type with the given id or null if no such
+ * object type exists.
+ *
+ * @param integer $objectTypeID
+ * @return \wcf\data\object\type\ObjectType
+ */
+ public function getObjectType($objectTypeID) {
+ if (isset($this->objectTypes[$objectTypeID])) {
+ return $this->objectTypes[$objectTypeID];
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the captcha object type with the given name or null if no such
+ * object type exists.
+ *
+ * @param string $objectType
+ * @return \wcf\data\object\type\ObjectType
+ */
+ public function getObjectTypeByName($objectType) {
+ return ObjectTypeCache::getInstance()->getObjectTypeByName('com.woltlab.wcf.captcha', $objectType);
+ }
+
+ /**
+ * @see \wcf\system\SingletonFactory::init()
+ */
+ protected function init() {
+ $objectTypes = ObjectTypeCache::getInstance()->getObjectTypes('com.woltlab.wcf.captcha');
+ foreach ($objectTypes as $objectType) {
+ $this->objectTypes[$objectType->objectTypeID] = $objectType;
+ }
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\captcha;
+use wcf\system\cache\builder\CaptchaQuestionCacheBuilder;
+use wcf\system\exception\UserInputException;
+use wcf\system\WCF;
+use wcf\util\StringUtil;
+
+/**
+ * Captcha handler for captcha questions.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.captcha
+ * @category Community Framework
+ */
+class CaptchaQuestionHandler implements ICaptchaHandler {
+ /**
+ * answer to the captcha question
+ * @var string
+ */
+ protected $captchaAnswer = '';
+
+ /**
+ * unique identifier of the captcha question
+ * @var string
+ */
+ protected $captchaQuestion = '';
+
+ /**
+ * captcha question to answer
+ * @var \wcf\data\captcha\question\CaptchaQuestion
+ */
+ protected $question = null;
+
+ /**
+ * Creates a new instance of CaptchaQuestionHandler.
+ */
+ public function __construct() {
+ $this->questions = CaptchaQuestionCacheBuilder::getInstance()->getData();
+ }
+
+ /**
+ * @see \wcf\system\captcha\ICaptchaHandler::isAvailable()
+ */
+ public function isAvailable() {
+ return count($this->questions) > 0;
+ }
+
+ /**
+ * @see \wcf\system\captcha\ICaptchaHandler::getFormElement()
+ */
+ public function getFormElement() {
+ if ($this->question === null) {
+ $this->readCaptchaQuestion();
+ }
+
+ return WCF::getTPL()->fetch('captchaQuestion', 'wcf', array(
+ 'captchaQuestion' => $this->captchaQuestion,
+ 'captchaQuestionAnswered' => WCF::getSession()->getVar('captchaQuestionSolved_'.$this->captchaQuestion) !== null,
+ 'captchaQuestionObject' => $this->question
+ ));
+ }
+
+ /**
+ * @see \wcf\system\captcha\ICaptchaHandler::readFormParameters()
+ */
+ public function readFormParameters() {
+ if (isset($_POST['captchaQuestion'])) $this->captchaQuestion = StringUtil::trim($_POST['captchaQuestion']);
+ if (isset($_POST['captchaAnswer'])) $this->captchaAnswer = StringUtil::trim($_POST['captchaAnswer']);
+ }
+
+ /**
+ * @see \wcf\system\captcha\ICaptchaHandler::reset()
+ */
+ public function reset() {
+ WCF::getSession()->unregister('captchaQuestion_'.$this->captchaQuestion);
+ WCF::getSession()->unregister('captchaQuestionSolved_'.$this->captchaQuestion);
+ }
+
+ /**
+ * Reads a random captcha question.
+ */
+ protected function readCaptchaQuestion() {
+ $questionID = array_rand($this->questions);
+ $this->question = $this->questions[$questionID];
+
+ do {
+ $this->captchaQuestion = StringUtil::getRandomID();
+ }
+ while (WCF::getSession()->getVar('captchaQuestion_'.$this->captchaQuestion) !== null);
+
+ WCF::getSession()->register('captchaQuestion_'.$this->captchaQuestion, $questionID);
+ }
+
+ /**
+ * @see \wcf\system\captcha\ICaptchaHandler::validate()
+ */
+ public function validate() {
+ $questionID = WCF::getSession()->getVar('captchaQuestion_'.$this->captchaQuestion);
+
+ if ($questionID === null || !isset($this->questions[$questionID])) {
+ throw new UserInputException('captchaQuestion');
+ }
+
+ $this->question = $this->questions[$questionID];
+
+ // check if question has already been answered
+ if (WCF::getSession()->getVar('captchaQuestionSolved_'.$this->captchaQuestion) !== null) return;
+
+ if ($this->captchaAnswer == '') {
+ throw new UserInputException('captchaAnswer');
+ }
+ else if (!$this->question->isAnswer($this->captchaAnswer)) {
+ throw new UserInputException('captchaAnswer', 'false');
+ }
+
+ WCF::getSession()->register('captchaQuestionSolved_'.$this->captchaQuestion, true);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\captcha;
+
+/**
+ * Every captcha type has to implement this interface.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.captcha
+ * @category Community Framework
+ */
+interface ICaptchaHandler {
+ /**
+ * Returns the form element.
+ *
+ * @return string
+ */
+ public function getFormElement();
+
+ /**
+ * Returns true if this kind of captcha is available.
+ *
+ * @return boolean
+ */
+ public function isAvailable();
+
+ /**
+ * Reads the parameters of the captcha form element.
+ */
+ public function readFormParameters();
+
+ /**
+ * Resets the captcha after it is no longer needed.
+ */
+ public function reset();
+
+ /**
+ * Validates the response to the challenge and marks the captcha as done.
+ */
+ public function validate();
+}
--- /dev/null
+<?php
+namespace wcf\system\captcha;
+use wcf\system\WCF;
+use wcf\util\StringUtil;
+
+/**
+ * Captcha handler for reCAPTCHA.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.captcha
+ * @category Community Framework
+ */
+class RecaptchaHandler implements ICaptchaHandler {
+ /**
+ * recaptcha challenge
+ * @var string
+ */
+ public $challenge = '';
+
+ /**
+ * response to the challenge
+ * @var string
+ */
+ public $response = '';
+
+ /**
+ * @see \wcf\system\captcha\ICaptchaHandler::getFormElement()
+ */
+ public function getFormElement() {
+ \wcf\system\recaptcha\RecaptchaHandler::getInstance()->assignVariables();
+
+ return WCF::getTPL()->fetch('recaptcha');
+ }
+
+ /**
+ * @see \wcf\system\captcha\ICaptchaHandler::isAvailable()
+ */
+ public function isAvailable() {
+ return MODULE_SYSTEM_RECAPTCHA && RECAPTCHA_PUBLICKEY && RECAPTCHA_PRIVATEKEY;
+ }
+
+ /**
+ * @see \wcf\system\captcha\ICaptchaHandler::readFormParameters()
+ */
+ public function readFormParameters() {
+ if (isset($_POST['recaptcha_challenge_field'])) $this->challenge = StringUtil::trim($_POST['recaptcha_challenge_field']);
+ if (isset($_POST['recaptcha_response_field'])) $this->response = StringUtil::trim($_POST['recaptcha_response_field']);
+ }
+
+ /**
+ * @see \wcf\system\captcha\ICaptchaHandler::reset()
+ */
+ public function reset() {
+ WCF::getSession()->unregister('recaptchaDone');
+ }
+
+ /**
+ * @see \wcf\system\captcha\ICaptchaHandler::validate()
+ */
+ public function validate() {
+ \wcf\system\recaptcha\RecaptchaHandler::getInstance()->validate($this->challenge, $this->response);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\option;
+use wcf\data\option\Option;
+use wcf\system\captcha\CaptchaHandler;
+use wcf\system\WCF;
+
+/**
+ * Option type implementation for selecting a captcha type.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.option
+ * @category Community Framework
+ */
+class CaptchaSelectOptionType extends AbstractOptionType {
+ /**
+ * @see \wcf\system\option\IOptionType::getFormElement()
+ */
+ public function getFormElement(Option $option, $value) {
+ $selectOptions = CaptchaHandler::getInstance()->getCaptchaSelection();
+ if ($option->allowemptyvalue) {
+ $selectOptions = array_merge(
+ array(
+ '' => WCF::getLanguage()->get('wcf.captcha.useNoCaptcha')
+ ),
+ $selectOptions
+ );
+ }
+
+ return WCF::getTPL()->fetch('selectOptionType', 'wcf', array(
+ 'selectOptions' => $selectOptions,
+ 'option' => $option,
+ 'value' => $value
+ ));
+ }
+
+ /**
+ * @see \wcf\system\option\IOptionType::validate()
+ */
+ public function validate(Option $option, $newValue) {
+ if (!$newValue) return;
+
+ $selection = CaptchaHandler::getInstance()->getCaptchaSelection();
+ if (!isset($selection[$newValue])) {
+ throw new UserInputException($option->optionName);
+ }
+ }
+}
WCF::getTPL()->assign(array(
'recaptchaLanguageCode' => $this->languageCode,
'recaptchaPublicKey' => $this->publicKey,
- 'recaptchaUseSSL' => RouteHandler::secureConnection()
+ 'recaptchaUseSSL' => RouteHandler::secureConnection(),
+ 'recaptchaLegacyMode' => true
));
}
}
<item name="wcf.acp.cache.type.template"><![CDATA[Templates]]></item>
</category>
+ <category name="wcf.acp.captcha">
+ <item name="wcf.acp.captcha.question.add"><![CDATA[Captcha-Frage hinzufügen]]></item>
+ <item name="wcf.acp.captcha.question.answers"><![CDATA[Antworten]]></item>
+ <item name="wcf.acp.captcha.question.answers.description"><![CDATA[Geben Sie pro Zeile eine mögliche Antwort ein. Antworten, die mit „~“ beginnen und enden, werden als reguläre Ausdrücke interpretiert.]]></item>
+ <item name="wcf.acp.captcha.question.answers.error.regexNotValid"><![CDATA[Der reguläre Ausdruck „{$invalidRegex}“ ist ungültig.]]></item>
+ <item name="wcf.acp.captcha.question.delete.confirmMessage"><![CDATA[Wollen Sie diese Frage wirklich löschen?]]></item>
+ <item name="wcf.acp.captcha.question.edit"><![CDATA[Captcha-Frage bearbeiten]]></item>
+ <item name="wcf.acp.captcha.question.isDisabled"><![CDATA[Fragen deaktivieren]]></item>
+ <item name="wcf.acp.captcha.question.list"><![CDATA[Captcha-Fragen]]></item>
+ <item name="wcf.acp.captcha.question.question"><![CDATA[Frage]]></item>
+ </category>
+
<category name="wcf.acp.cronjob">
<item name="wcf.acp.cronjob.list"><![CDATA[Cronjobs]]></item>
<item name="wcf.acp.cronjob.add"><![CDATA[Cronjob hinzufügen]]></item>
<item name="wcf.acp.group.option.admin.content.ad.canManageAd"><![CDATA[Kann Werbung verwalten]]></item>
<item name="wcf.acp.group.option.user.profile.aboutMeMaxLength"><![CDATA[Maximallänge „Über mich“]]></item>
<item name="wcf.acp.group.option.user.profile.canReportContent"><![CDATA[Kann Inhalte melden]]></item>
+ <item name="wcf.acp.group.option.admin.captcha.canManageCaptchaQuestion"><![CDATA[Kann Captcha-Fragen verwalten]]></item>
+ <item name="wcf.acp.group.category.admin.display.captcha"><![CDATA[Captchas]]></item>
</category>
<category name="wcf.acp.index">
<item name="wcf.acp.menu.link.ad"><![CDATA[Werbung]]></item>
<item name="wcf.acp.menu.link.ad.add"><![CDATA[Werbung hinzufügen]]></item>
<item name="wcf.acp.menu.link.ad.list"><![CDATA[Werbung auflisten]]></item>
+ <item name="wcf.acp.menu.link.captcha"><![CDATA[Captchas]]></item>
+ <item name="wcf.acp.menu.link.captcha.question.add"><![CDATA[Frage hinzufügen]]></item>
+ <item name="wcf.acp.menu.link.captcha.question.list"><![CDATA[Fragen auflisten]]></item>
</category>
<category name="wcf.acp.notice">
<item name="wcf.acp.option.register_activation_method.byAdmin"><![CDATA[Aktivierung erfolgt durch Administrator]]></item>
<item name="wcf.acp.option.register_activation_method.byUser"><![CDATA[Benutzer aktiviert sich durch E-Mail-Bestätigung]]></item>
<item name="wcf.acp.option.register_activation_method.disabled"><![CDATA[Keine Aktivierung notwendig]]></item>
- <item name="wcf.acp.option.register_use_captcha"><![CDATA[reCAPTCHA in Registrierung aktivieren]]></item>
- <item name="wcf.acp.option.lost_password_use_captcha"><![CDATA[reCAPTCHA in „Kennwort vergessen“ aktivieren]]></item>
- <item name="wcf.acp.option.profile_mail_use_captcha"><![CDATA[reCAPTCHA in „E-Mail an Benutzer schicken“ aktivieren]]></item>
<item name="wcf.acp.option.signature_max_image_height"><![CDATA[Maximale Höhe von Signatur-Bildern]]></item>
<item name="wcf.acp.option.user_title_max_length"><![CDATA[Maximale Länge des Benutzertitels]]></item>
<item name="wcf.acp.option.user_forbidden_titles"><![CDATA[Reservierte Benutzertitel]]></item>
<item name="wcf.acp.option.search_results_per_page"><![CDATA[Ergebnisse pro Seite]]></item>
<item name="wcf.acp.option.search_default_sort_field"><![CDATA[Standardsortierung]]></item>
<item name="wcf.acp.option.search_default_sort_order"><![CDATA[Standardreihenfolge]]></item>
- <item name="wcf.acp.option.search_use_captcha"><![CDATA[reCAPTCHA in Suchfunktion aktivieren]]></item>
<item name="wcf.acp.option.category.message.general.poll"><![CDATA[Umfragen]]></item>
<item name="wcf.acp.option.module_poll"><![CDATA[Umfragen]]></item>
<item name="wcf.acp.option.poll_max_options"><![CDATA[Maximale Anzahl an Antworten]]></item>
<item name="wcf.acp.option.url_omit_index_php"><![CDATA[„index.php“ aus URLs entfernen]]></item>
<item name="wcf.acp.option.url_omit_index_php.description"><![CDATA[TODO]]></item>
<item name="wcf.acp.option.module_ad"><![CDATA[Werbung]]></item>
+ <item name="wcf.acp.option.register_captcha_type"><![CDATA[Registierung]]></item>
+ <item name="wcf.acp.option.lost_password_captcha_type"><![CDATA[Kennwort vergessen]]></item>
+ <item name="wcf.acp.option.profile_mail_captcha_type"><![CDATA[E-Mail an Benutzer schicken]]></item>
+ <item name="wcf.acp.option.search_captcha_type"><![CDATA[Suche]]></item>
+ <item name="wcf.acp.option.message_captcha_type"><![CDATA[Nachrichten]]></item>
+ <item name="wcf.acp.option.category.security.antispam.captcha"><![CDATA[Captchas]]></item>
</category>
<category name="wcf.acp.package">
<item name="wcf.bbcode.spoiler.text"><![CDATA[(Versteckter Text)]]></item>
</category>
+ <category name="wcf.captcha">
+ <item name="wcf.captcha.useNoCaptcha"><![CDATA[(Kein Captcha)]]></item>
+ <item name="wcf.captcha.com.woltlab.wcf.recaptcha"><![CDATA[reCAPTCHA]]></item>
+ <item name="wcf.captcha.com.woltlab.wcf.captchaQuestion"><![CDATA[Frage]]></item>
+ </category>
+
+ <category name="wcf.captcha.question">
+ <item name="wcf.captcha.question.answer.error.false"><![CDATA[Sie haben eine falsche Antwort angegeben.]]></item>
+ <item name="wcf.captcha.question.captcha"><![CDATA[Frage]]></item>
+ <item name="wcf.captcha.question.captcha.description"><![CDATA[Bitte beantworten Sie die untenstehende Frage bzw. lösen Sie die untenstehende Aufgabe.]]></item>
+ </category>
+
<category name="wcf.category">
<item name="wcf.category.add"><![CDATA[Kategorie hinzufügen]]></item>
<item name="wcf.category.button.list"><![CDATA[Kategorien auflisten]]></item>
<item name="wcf.acp.cache.type.template"><![CDATA[Templates]]></item>
</category>
+ <category name="wcf.acp.captcha">
+ <item name="wcf.acp.captcha.question.add"><![CDATA[Add Captcha Question]]></item>
+ <item name="wcf.acp.captcha.question.answers"><![CDATA[Answers]]></item>
+ <item name="wcf.acp.captcha.question.answers.description"><![CDATA[Enter one possible answer per line. Answers beginning and ending with “~” will be interpreted as regular expressions.]]></item>
+ <item name="wcf.acp.captcha.question.answers.error.regexNotValid"><![CDATA[The regular expression “{$invalidRegex}” is not valid.]]></item>
+ <item name="wcf.acp.captcha.question.delete.confirmMessage"><![CDATA[Do you really want to delete this question?]]></item>
+ <item name="wcf.acp.captcha.question.edit"><![CDATA[Edit Captcha Question]]></item>
+ <item name="wcf.acp.captcha.question.isDisabled"><![CDATA[Disable Question]]></item>
+ <item name="wcf.acp.captcha.question.list"><![CDATA[Captcha Questions]]></item>
+ <item name="wcf.acp.captcha.question.question"><![CDATA[Question]]></item>
+ </category>
+
<category name="wcf.acp.cronjob">
<item name="wcf.acp.cronjob.list"><![CDATA[Cronjobs]]></item>
<item name="wcf.acp.cronjob.add"><![CDATA[Add Cronjob]]></item>
<item name="wcf.acp.group.option.admin.content.ad.canManageAd"><![CDATA[Can manage ads]]></item>
<item name="wcf.acp.group.option.user.profile.aboutMeMaxLength"><![CDATA[“About Me” maximum length]]></item>
<item name="wcf.acp.group.option.user.profile.canReportContent"><![CDATA[Can report content]]></item>
+ <item name="wcf.acp.group.option.admin.captcha.canManageCaptchaQuestion"><![CDATA[Can manage captcha questions]]></item>
+ <item name="wcf.acp.group.category.admin.display.captcha"><![CDATA[Captchas]]></item>
</category>
<category name="wcf.acp.index">
<item name="wcf.acp.menu.link.ad"><![CDATA[Ad]]></item>
<item name="wcf.acp.menu.link.ad.add"><![CDATA[Add Ad]]></item>
<item name="wcf.acp.menu.link.ad.list"><![CDATA[List Ads]]></item>
+ <item name="wcf.acp.menu.link.captcha"><![CDATA[Captchas]]></item>
+ <item name="wcf.acp.menu.link.captcha.question.add"><![CDATA[Add Question]]></item>
+ <item name="wcf.acp.menu.link.captcha.question.list"><![CDATA[List Questions]]></item>
</category>
<category name="wcf.acp.notice">
<item name="wcf.acp.option.register_activation_method.byAdmin"><![CDATA[Only administrators can approve]]></item>
<item name="wcf.acp.option.register_activation_method.byUser"><![CDATA[Approval through email confirmation]]></item>
<item name="wcf.acp.option.register_activation_method.disabled"><![CDATA[No approval required]]></item>
- <item name="wcf.acp.option.register_use_captcha"><![CDATA[Enable reCAPTCHA protection during registration]]></item>
- <item name="wcf.acp.option.lost_password_use_captcha"><![CDATA[Enable reCAPTCHA protection for “Lost Password”]]></item>
- <item name="wcf.acp.option.profile_mail_use_captcha"><![CDATA[Enable reCAPTCHA protection for “Send Email to User”]]></item>
<item name="wcf.acp.option.signature_max_image_height"><![CDATA[Maximum Height of Images in Signatures]]></item>
<item name="wcf.acp.option.user_title_max_length"><![CDATA[Maximum User Title Length]]></item>
<item name="wcf.acp.option.user_forbidden_titles"><![CDATA[Reserved User Titles]]></item>
<item name="wcf.acp.option.search_results_per_page"><![CDATA[Results Per Page]]></item>
<item name="wcf.acp.option.search_default_sort_field"><![CDATA[Default Sort Field]]></item>
<item name="wcf.acp.option.search_default_sort_order"><![CDATA[Default Sort Order]]></item>
- <item name="wcf.acp.option.search_use_captcha"><![CDATA[Enable reCAPTCHA protection for search]]></item>
<item name="wcf.acp.option.category.message.general.poll"><![CDATA[Polls]]></item>
<item name="wcf.acp.option.module_poll"><![CDATA[Polls]]></item>
<item name="wcf.acp.option.poll_max_options"><![CDATA[Maximum Number of Options]]></item>
<item name="wcf.acp.option.url_omit_index_php"><![CDATA[TODO: „index.php“ aus URLs entfernen]]></item>
<item name="wcf.acp.option.url_omit_index_php.description"><![CDATA[TODO]]></item>
<item name="wcf.acp.option.module_ad"><![CDATA[Ads]]></item>
+ <item name="wcf.acp.option.register_captcha_type"><![CDATA[Registration]]></item>
+ <item name="wcf.acp.option.lost_password_captcha_type"><![CDATA[Lost Password]]></item>
+ <item name="wcf.acp.option.profile_mail_captcha_type"><![CDATA[Send Email to User]]></item>
+ <item name="wcf.acp.option.search_captcha_type"><![CDATA[Search]]></item>
+ <item name="wcf.acp.option.message_captcha_type"><![CDATA[Messages]]></item>
+ <item name="wcf.acp.option.category.security.antispam.captcha"><![CDATA[Captchas]]></item>
</category>
<category name="wcf.acp.package">
<item name="wcf.bbcode.spoiler.text"><![CDATA[(Hidden Content)]]></item>
</category>
+ <category name="wcf.captcha">
+ <item name="wcf.captcha.useNoCaptcha"><![CDATA[(No Captcha)]]></item>
+ <item name="wcf.captcha.com.woltlab.wcf.recaptcha"><![CDATA[reCAPTCHA]]></item>
+ <item name="wcf.captcha.com.woltlab.wcf.captchaQuestion"><![CDATA[Question]]></item>
+ </category>
+
+ <category name="wcf.captcha.question">
+ <item name="wcf.captcha.question.answer.error.false"><![CDATA[You have entered an incorrect answer.]]></item>
+ <item name="wcf.captcha.question.captcha"><![CDATA[Question]]></item>
+ <item name="wcf.captcha.question.captcha.description"><![CDATA[Please answer the question below or solve the problem below.]]></item>
+ </category>
+
<category name="wcf.category">
<item name="wcf.category.add"><![CDATA[Add Category]]></item>
<item name="wcf.category.button.list"><![CDATA[List Categories]]></item>
html TEXT NOT NULL
);
+DROP TABLE IF EXISTS wcf1_captcha_question;
+CREATE TABLE wcf1_captcha_question (
+ questionID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ question VARCHAR(255) NOT NULL,
+ answers MEDIUMTEXT,
+ isDisabled TINYINT(1) NOT NULL DEFAULT 0
+);
+
DROP TABLE IF EXISTS wcf1_category;
CREATE TABLE wcf1_category (
categoryID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,