Change "wave" and "paint" text effect captcha options from toggle to slider

Add "noise" text captcha effect slider
Add font lib to get list of system fonts
Add "font" text captcha option
ref #469
indiachan-spamvector
Thomas Lynch 2 years ago
parent 0c2e99a96b
commit 6ec6b32ed5
  1. 6
      configs/template.js.example
  2. 11
      controllers/forms/globalsettings.js
  3. 23
      lib/captcha/generators/text.js
  4. 21
      lib/misc/fonts.js
  5. 6
      migrations/0.8.0.js
  6. 6
      models/forms/changeglobalsettings.js
  7. 2
      models/pages/globalmanage/settings.js
  8. 19
      views/pages/globalmanagesettings.pug

@ -35,9 +35,11 @@ module.exports = {
type: 'text',
generateLimit: 250,
text: {
font: 'default',
wave: false,
line: false,
paint: false,
line: 0,
paint: 2,
noise: 0,
},
grid: {
falses: ['○','□','♘','♢','▽','△','♖','✧','♔','♘','♕','♗','♙','♧'],

@ -4,6 +4,7 @@ const changeGlobalSettings = require(__dirname+'/../../models/forms/changeglobal
, dynamicResponse = require(__dirname+'/../../lib/misc/dynamic.js')
, themeHelper = require(__dirname+'/../../lib/misc/themes.js')
, config = require(__dirname+'/../../lib/misc/config.js')
, { fontPaths } = require(__dirname+'/../../lib/misc/fonts.js')
, paramConverter = require(__dirname+'/../../lib/middleware/input/paramconverter.js')
, { checkSchema, lengthBody, numberBody, minmaxBody, numberBodyVariable,
inArrayBody } = require(__dirname+'/../../lib/input/schema.js');
@ -13,8 +14,8 @@ module.exports = {
paramConverter: paramConverter({
timeFields: ['ban_duration', 'board_defaults_filter_ban_duration', 'default_ban_duration', 'block_bypass_expire_after_time', 'dnsbl_cache_time', 'board_defaults_delete_protection_age'],
trimFields: ['captcha_options_grid_question', 'captcha_options_grid_trues', 'captcha_options_grid_falses','allowed_hosts', 'dnsbl_blacklists', 'other_mime_types',
'highlight_options_language_subset', 'global_limits_custom_css_filters', 'board_defaults_filters', 'filters', 'archive_links', 'reverse_links'],
numberFields: ['filter_mode', 'auth_level',
'highlight_options_language_subset', 'global_limits_custom_css_filters', 'board_defaults_filters', 'filters', 'archive_links', 'reverse_links', 'captcha_options_text_font'],
numberFields: ['filter_mode', 'auth_level', 'captcha_options_text_wave', 'captcha_options_text_paint', 'captcha_options_text_noise',
'captcha_options_generate_limit', 'captcha_options_grid_size', 'captcha_options_grid_image_size', 'captcha_options_num_distorts_min', 'captcha_options_num_distorts_max',
'captcha_options_distortion', 'captcha_options_grid_icon_y_offset', 'flood_timers_same_content_same_ip', 'flood_timers_same_content_any_ip', 'flood_timers_any_content_same_ip',
'block_bypass_expire_after_uses', 'rate_limit_cost_captcha', 'rate_limit_cost_board_settings', 'rate_limit_cost_edit_post',
@ -58,7 +59,7 @@ module.exports = {
}, expected: false, error: 'Extra mime types must be like type/subtype' },
{ result: () => {
if (req.body.archive_links) {
/* eslint-disable no-useless-escape */
/* eslint-disable no-useless-escape */
return /https?\:\/\/[^\s<>\[\]{}|\\^]+%s[^\s<>\[\]{}|\\^]*/i.test(req.body.archive_links);
}
return false;
@ -87,6 +88,10 @@ module.exports = {
{ result: numberBody(req.body.captcha_options_num_distorts_max, 0, 10), expected: true, error: 'Captcha options max distorts must be a number from 0-10' },
{ result: minmaxBody(req.body.captcha_options_num_distorts_min, req.body.captcha_options_num_distorts_max), expected: true, error: 'Captcha options distorts min must be less than max' },
{ result: numberBody(req.body.captcha_options_distortion, 0, 50), expected: true, error: 'Captcha options distortion must be a number from 0-50' },
{ result: inArrayBody(req.body.captcha_options_text_font, fontPaths), expected: true, error: 'Invalid captcha options text font' },
{ result: numberBody(req.body.captcha_options_text_wave, 0, 10), expected: true, error: 'Captcha options text wave effect strength must be a number form 0-10' },
{ result: numberBody(req.body.captcha_options_text_paint, 0, 10), expected: true, error: 'Captcha options text paint effect strength must be a number from 0-10' },
{ result: numberBody(req.body.captcha_options_text_noise, 0, 10), expected: true, error: 'Captcha options text noise effect strength must be a number from 0-10' },
{ result: numberBody(req.body.dnsbl_cache_time), expected: true, error: 'Invalid dnsbl cache time' },
{ result: numberBody(req.body.flood_timers_same_content_same_ip), expected: true, error: 'Invalid flood time same content same ip' },
{ result: numberBody(req.body.flood_timers_same_content_any_ip), expected: true, error: 'Invalid flood time same contenet any ip' },

@ -49,6 +49,10 @@ module.exports = async () => {
.fill('#000000')
.fontSize(65);
if (captchaOptions.text.font !== 'default') {
captcha.font(captchaOptions.text.font);
}
//draw each character at their x based on the characterWidth()
const textWidth = totalTextWidth(text);
const startX = (width-textWidth)/2;
@ -58,27 +62,26 @@ module.exports = async () => {
charX += characterWidth(text[i]);
}
/*
TODO: possibilities for customising some of the values in these options.
Some can be converted to number inputs and 0=off?
*/
//draw optional line/strikethrough
if (captchaOptions.text.line) {
if (captchaOptions.text.line === true) {
const lineY = await randomRange(35,45);
captcha.drawRectangle(startX, lineY, startX+textWidth, lineY+4);
}
//add optional wave effect
if (captchaOptions.text.wave) {
captcha.wave(5, width/4);
if (captchaOptions.text.wave > 0) {
captcha.wave(captchaOptions.text.wave, width/4);
}
//add optional paint effect
if (captchaOptions.text.paint) {
captcha.paint(2);
if (captchaOptions.text.paint > 0) {
captcha.paint(captchaOptions.text.paint);
}
//TODO: noise effect
//add optional noise effect
if (captchaOptions.text.noise > 0) {
captcha.noise(captchaOptions.text.noise);
}
//create an array of distortions and apply to the image, if distortion is enabled
const { distortion, numDistorts } = captchaOptions;

@ -0,0 +1,21 @@
'use strict';
const fontList = require('child_process')
.execSync('fc-list -f "%{file}:%{family[0]} %{style[0]}\n"')
.toString()
.split('\n') //split by newlines, like here ^
.filter(line => line) //filter empty lines
.map(line => {
//map to an object with path and name
const [path, name] = line.split(':');
return { path, name };
})
.sort((a, b) => {
//alphabetical name sort
return a.name.localeCompare(b.name);
});
module.exports = {
fontList,
fontPaths: new Set(['default', ...fontList.map(f => f.path)]), //memoize paths
};

@ -5,9 +5,11 @@ module.exports = async(db, redis) => {
await db.collection('globalsettings').updateOne({ _id: 'globalsettings' }, {
'$set': {
'captchaOptions.text': {
'font': 'default',
'line': false,
'wave': false,
'paint': false,
'wave': 0,
'paint': 2,
'noise': 0,
},
'captchaOptions.grid.falses': ['○','□','♘','♢','▽','△','♖','✧','♔','♘','♕','♗','♙','♧'],
'captchaOptions.grid.trues': ['●','■','♞','♦','▼','▲','♜','✦','♚','♞','♛','♝','♟','♣'],

@ -78,9 +78,11 @@ module.exports = async (req, res) => {
falses: arraySetting(req.body.captcha_options_grid_falses, oldSettings.captchaOptions.grid.falses),
},
text: {
font: trimSetting(req.body.captcha_options_text_font, oldSettings.captchaOptions.text.font),
line: booleanSetting(req.body.captcha_options_text_line, oldSettings.captchaOptions.text.line),
wave: booleanSetting(req.body.captcha_options_text_wave, oldSettings.captchaOptions.text.wave),
paint: booleanSetting(req.body.captcha_options_text_paint, oldSettings.captchaOptions.text.paint),
wave: numberSetting(req.body.captcha_options_text_wave, oldSettings.captchaOptions.text.wave),
paint: numberSetting(req.body.captcha_options_text_paint, oldSettings.captchaOptions.text.paint),
noise: numberSetting(req.body.captcha_options_text_noise, oldSettings.captchaOptions.text.noise),
},
numDistorts: {
min: numberSetting(req.body.captcha_options_num_distorts_min, oldSettings.captchaOptions.numDistorts.min),

@ -1,6 +1,7 @@
'use strict';
const config = require(__dirname+'/../../../lib/misc/config.js')
, { fontList } = require(__dirname+'/../../../lib/misc/fonts.js')
, { themes, codeThemes } = require(__dirname+'/../../../lib/misc/themes.js')
, { countryNamesMap, countryCodes } = require(__dirname+'/../../../lib/misc/countries.js');
@ -16,6 +17,7 @@ module.exports = async (req, res) => {
countryCodes,
themes,
codeThemes,
fontList,
});
};

@ -26,6 +26,7 @@ block content
.form-wrapper.flexleft.mt-10
form.form-post(action=`/forms/global/settings`, enctype='application/x-www-form-urlencoded', method='POST')
input(type='hidden' name='_csrf' value=csrf)
input.row(type='submit', value='save settings')
.row.wrap.sb
.col.mr-5
@ -193,20 +194,26 @@ block content
h4.mv-5 Text Captcha Options
.row
.label Font
select(name='captcha_options_text_font' disabled)
option(selected=true) Default
select(name='captcha_options_text_font')
option(value='default' selected=(settings.captchaOptions.text.font === 'default')) Default
each font in fontList
option(value=font.path selected=(settings.captchaOptions.text.font === font.path)) #{font.name}
.row
.label Strikethrough Effect
label.postform-style.ph-5
input(type='checkbox', name='captcha_options_text_line', value='true' checked=settings.captchaOptions.text.line)
.row
.label Wave Effect
.label Wave Effect Strength
label.postform-style.ph-5
input(type='range' name='captcha_options_text_wave' min='0' max='10' value=settings.captchaOptions.text.wave)
.row
.label Paint Effect Strength
label.postform-style.ph-5
input(type='checkbox', name='captcha_options_text_wave', value='true' checked=settings.captchaOptions.text.wave)
input(type='range' name='captcha_options_text_paint' min='0' max='10' value=settings.captchaOptions.text.paint)
.row
.label Paint Effect
.label Noise Effect Strength
label.postform-style.ph-5
input(type='checkbox', name='captcha_options_text_paint', value='true' checked=settings.captchaOptions.text.text)
input(type='range' name='captcha_options_text_noise' min='0' max='10' value=settings.captchaOptions.text.noise)
.row
h4.mv-5 Captcha Distortion
.row

Loading…
Cancel
Save