# HG changeset patch # User Dan # Date 1198816331 18000 # Node ID bfa2e9c23f0346108183e6acf2781a785441dd2b # Parent 67bd3121a12ee5085ec9ead608b13b669801749f Added ability to require CAPTCHA for guests when editing pages (AJAX INTERFACE ONLY) diff -r 67bd3121a12e -r bfa2e9c23f03 ajax.php --- a/ajax.php Thu Dec 27 22:09:33 2007 -0500 +++ b/ajax.php Thu Dec 27 23:32:11 2007 -0500 @@ -114,13 +114,24 @@ $allowed = false; $src = ''; } + + $auth_edit = ( $session->get_permissions('edit_page') && ( $session->get_permissions('even_when_protected') || !$paths->page_protected ) ); + $return = array( 'mode' => 'editor', 'src' => $src, 'auth_view_source' => $allowed, - 'auth_edit' => $session->get_permissions('edit_page'), - 'time' => time() + 'auth_edit' => $auth_edit, + 'time' => time(), + 'require_captcha' => false, ); + + if ( $auth_edit && !$session->user_logged_in && getConfig('guest_edit_require_captcha') == '1' ) + { + $return['require_captcha'] = true; + $return['captcha_id'] = $session->make_captcha(); + } + echo enano_json_encode($return); break; case "getpage": @@ -178,6 +189,27 @@ break; } + // Verify captcha, if needed + if ( !$session->user_logged_in && getConfig('guest_edit_require_captcha') == '1' ) + { + if ( !isset($request['captcha_id']) || !isset($request['captcha_code']) ) + { + die('Invalid request, need captcha metadata'); + } + $code_correct = strtolower($session->get_captcha($request['captcha_id'])); + $code_input = strtolower($request['captcha_code']); + if ( $code_correct !== $code_input ) + { + $return = array( + 'mode' => 'errors', + 'errors' => array($lang->get('editor_err_captcha_wrong')), + 'new_captcha' => $session->make_captcha() + ); + echo enano_json_encode($return); + break; + } + } + // Verification complete. Start the PageProcessor and let it do the dirty work for us. $page = new PageProcessor($paths->page_id, $paths->namespace); if ( $page->update_page($request['src'], $request['summary'], ( $request['minor_edit'] == 1 )) ) @@ -197,6 +229,10 @@ 'mode' => 'errors', 'errors' => array_values($errors) ); + if ( !$session->user_logged_in && getConfig('guest_edit_require_captcha') == '1' ) + { + $return['new_captcha'] = $session->make_captcha(); + } } echo enano_json_encode($return); diff -r 67bd3121a12e -r bfa2e9c23f03 includes/clientside/static/acl.js --- a/includes/clientside/static/acl.js Thu Dec 27 22:09:33 2007 -0500 +++ b/includes/clientside/static/acl.js Thu Dec 27 23:32:11 2007 -0500 @@ -471,7 +471,7 @@ b.appendChild(document.createTextNode($lang.get('acl_lbl_delete_success_title'))); note.appendChild(b); note.appendChild(document.createElement('br')); - note.appendChild(document.createTextNode($lang.get('acl_lbl_delete_success_title', { target_name: aclDataCache.target_name }))); + note.appendChild(document.createTextNode($lang.get('acl_lbl_delete_success_body', { target_name: aclDataCache.target_name }))); note.appendChild(document.createElement('br')); a = document.createElement('a'); a.href = '#'; diff -r 67bd3121a12e -r bfa2e9c23f03 includes/clientside/static/editor.js --- a/includes/clientside/static/editor.js Thu Dec 27 22:09:33 2007 -0500 +++ b/includes/clientside/static/editor.js Thu Dec 27 23:32:11 2007 -0500 @@ -80,12 +80,15 @@ return false; } - ajaxBuildEditor(response.src, (!response.auth_edit), response.time); + // do we need to enter a captcha before saving the page? + var captcha_hash = ( response.require_captcha ) ? response.captcha_id : false; + + ajaxBuildEditor(response.src, (!response.auth_edit), response.time, captcha_hash); } }); } -function ajaxBuildEditor(content, readonly, timestamp) +function ajaxBuildEditor(content, readonly, timestamp, captcha_hash) { // Set flags // We don't want the fancy confirmation framework to trigger if the user is only viewing the page source @@ -255,6 +258,47 @@ tr2.appendChild(td2_1); tr2.appendChild(td2_2); + if ( captcha_hash ) + { + // generate captcha field (effectively third row) + var tr4 = document.createElement('tr'); + var td4_1 = document.createElement('td'); + var td4_2 = document.createElement('td'); + td4_1.className = 'row2'; + td4_2.className = 'row1'; + + td4_1.appendChild(document.createTextNode($lang.get('editor_lbl_field_captcha'))); + td4_1.appendChild(document.createElement('br')); + var small2 = document.createElement('small'); + small2.appendChild(document.createTextNode($lang.get('editor_msg_captcha_pleaseenter'))); + small2.appendChild(document.createElement('br')); + small2.appendChild(document.createElement('br')); + small2.appendChild(document.createTextNode($lang.get('editor_msg_captcha_blind'))); + td4_1.appendChild(small2); + + var img = document.createElement('img'); + img.src = makeUrlNS('Special', 'Captcha/' + captcha_hash); + img._captchaHash = captcha_hash; + img.id = 'enano_editor_captcha_img'; + img.onclick = function() + { + this.src = makeUrlNS('Special', 'Captcha/' + this._captchaHash + '/' + Math.floor(Math.random() * 100000)); + } + img.style.cursor = 'pointer'; + td4_2.appendChild(img); + td4_2.appendChild(document.createElement('br')); + td4_2.appendChild(document.createTextNode($lang.get('editor_lbl_field_captcha_code') + ' ')); + var input = document.createElement('input'); + input.type = 'text'; + input.id = 'enano_editor_field_captcha'; + input._captchaHash = captcha_hash; + input.size = '9'; + td4_2.appendChild(input); + + tr4.appendChild(td4_1); + tr4.appendChild(td4_2); + } + // Third row: controls var tr3 = document.createElement('tr'); var td3 = document.createElement('th'); @@ -302,6 +346,10 @@ metatable.appendChild(tr1); metatable.appendChild(tr2); + if ( captcha_hash ) + { + metatable.appendChild(tr4); + } metatable.appendChild(tr3); } tblholder.appendChild(metatable); @@ -339,6 +387,14 @@ { ajaxSetEditorLoading(); var ta_content = $('ajaxEditArea').getContent(); + + if ( ta_content == '' || ta_content == '

' || ta_content == '

 

' ) + { + new messagebox(MB_OK|MB_ICONSTOP, $lang.get('editor_err_no_text_title'), $lang.get('editor_err_no_text_body')); + ajaxUnSetEditorLoading(); + return false; + } + var edit_summ = $('enano_editor_field_summary').object.value; if ( !edit_summ ) edit_summ = ''; @@ -351,6 +407,21 @@ minor_edit: is_minor, time: timestamp }; + + // Do we need to add captcha info? + if ( document.getElementById('enano_editor_field_captcha') ) + { + var captcha_field = document.getElementById('enano_editor_field_captcha'); + if ( captcha_field.value == '' ) + { + new messagebox(MB_OK|MB_ICONSTOP, $lang.get('editor_err_need_captcha_title'), $lang.get('editor_err_need_captcha_body')); + ajaxUnSetEditorLoading(); + return false; + } + json_packet.captcha_code = captcha_field.value; + json_packet.captcha_id = captcha_field._captchaHash; + } + json_packet = ajaxEscape(toJSONString(json_packet)); ajaxPost(stdAjaxPrefix + '&_mode=savepage_json', 'r=' + json_packet, function() { @@ -374,6 +445,21 @@ // This will be used if the PageProcessor generated errors (usually security/permissions related) if ( response.mode == 'errors' ) { + // This will be true if the user entered a captcha code incorrectly, thus + // invalidating the code and requiring a new image to be generated. + if ( response.new_captcha ) + { + // Generate the new captcha field + var img = document.getElementById('enano_editor_captcha_img'); + var input = document.getElementById('enano_editor_field_captcha'); + if ( img && input ) + { + img._captchaHash = response.new_captcha; + input._captchaHash = response.new_captcha; + img.src = makeUrlNS('Special', 'Captcha/' + response.new_captcha); + input.value = ''; + } + } var errors = ''; new messagebox(MB_OK | MB_ICONSTOP, $lang.get('editor_err_save_title'), $lang.get('editor_err_save_body') + errors); return false; @@ -392,14 +478,17 @@ setAjaxLoading(); editor_open = false; enableUnload(); + changeOpac(0, 'ajaxEditContainer'); ajaxGet(stdAjaxPrefix + '&_mode=getpage&noheaders', function() { if ( ajax.readyState == 4 ) { unsetAjaxLoading(); - document.getElementById('ajaxEditContainer').innerHTML = '
' + $lang.get('editor_msg_saved') + '
' + ajax.responseText; selectButtonMajor('article'); unselectAllButtonsMinor(); + + document.getElementById('ajaxEditContainer').innerHTML = '
' + $lang.get('editor_msg_saved') + '
' + ajax.responseText; + opacity('ajaxEditContainer', 0, 100, 1000); } }); } diff -r 67bd3121a12e -r bfa2e9c23f03 language/english/core.json --- a/language/english/core.json Thu Dec 27 22:09:33 2007 -0500 +++ b/language/english/core.json Thu Dec 27 23:32:11 2007 -0500 @@ -220,10 +220,15 @@ err_save_body: 'A few problems were encountered while your page was being saved:', err_obsolete_title: 'Someone else modified this page while you were editing it', err_obsolete_body: 'While you were editing this page, %author% modified this page. The edit took place on %timestamp%. You can view the latest version of the page, or click %this.editor_btn_save% again to replace the page with your revision.', + err_need_captcha_title: 'Missing confirmation code', + err_need_captcha_body: 'Please enter the confirmation code in the box labeled "%this.editor_lbl_field_captcha_code%". %this.editor_msg_captcha_blind%', + err_no_text_title: 'No text entered', + err_no_text_body: 'Please enter the text that will be in this page.', // Server-side errors err_no_rows: 'Page doesn\'t exist in the database', err_no_permission: 'You do not have permission to edit this page.', err_page_protected: 'This page is protected, and you do not have permission to edit protected pages.', + err_captcha_wrong: 'The confirmation code you entered is incorrect.', msg_saved: 'Your changes to this page have been saved.', msg_revert_confirm: 'Do you really want to revert your changes?', @@ -252,6 +257,11 @@ preview_blurb: 'Reminder: This is only a preview - your changes to this page have not yet been saved.', msg_save_success_title: 'Changes saved', msg_save_success_body: 'Your changes to this page have been saved. Redirecting...', + + msg_captcha_pleaseenter: 'Please enter the code shown in the image to the right into the text box. This process helps to ensure that this page is not being edited by an automated bot. If the image to the right is illegible, you can regenerate it by clicking on the image.', + msg_captcha_blind: 'If you are visually impaired or otherwise cannot read the text shown to the right, please contact the site management and they will be able to make your requested edits.', + lbl_field_captcha: 'Visual confirmation', + lbl_field_captcha_code: 'Code:', }, history: { summary_clearlogs: 'Automatic backup created when logs were purged', diff -r 67bd3121a12e -r bfa2e9c23f03 plugins/SpecialAdmin.php --- a/plugins/SpecialAdmin.php Thu Dec 27 22:09:33 2007 -0500 +++ b/plugins/SpecialAdmin.php Thu Dec 27 23:32:11 2007 -0500 @@ -181,6 +181,8 @@ if(isset($_POST['editmsg'])) setConfig('wiki_edit_notice', '1'); else setConfig('wiki_edit_notice', '0'); setConfig('wiki_edit_notice_text', $_POST['editmsg_text']); + if(isset($_POST['guest_edit_require_captcha'])) setConfig('guest_edit_require_captcha', '1'); + else setConfig('guest_edit_require_captcha', '0'); // Stats if(isset($_POST['log_hits'])) setConfig('log_hits', '1'); @@ -341,6 +343,19 @@ + + + Require visual confirmation for guests editing pages
+ If this is enabled, guests will be asked to enter a visual confirmation code before saving changes to a page. + + + + + + Statistics and hit counting