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', type: 'text',
generateLimit: 250, generateLimit: 250,
text: { text: {
font: 'default',
wave: false, wave: false,
line: false, line: 0,
paint: false, paint: 2,
noise: 0,
}, },
grid: { grid: {
falses: ['○','□','♘','♢','▽','△','♖','✧','♔','♘','♕','♗','♙','♧'], falses: ['○','□','♘','♢','▽','△','♖','✧','♔','♘','♕','♗','♙','♧'],

@ -4,6 +4,7 @@ const changeGlobalSettings = require(__dirname+'/../../models/forms/changeglobal
, dynamicResponse = require(__dirname+'/../../lib/misc/dynamic.js') , dynamicResponse = require(__dirname+'/../../lib/misc/dynamic.js')
, themeHelper = require(__dirname+'/../../lib/misc/themes.js') , themeHelper = require(__dirname+'/../../lib/misc/themes.js')
, config = require(__dirname+'/../../lib/misc/config.js') , config = require(__dirname+'/../../lib/misc/config.js')
, { fontPaths } = require(__dirname+'/../../lib/misc/fonts.js')
, paramConverter = require(__dirname+'/../../lib/middleware/input/paramconverter.js') , paramConverter = require(__dirname+'/../../lib/middleware/input/paramconverter.js')
, { checkSchema, lengthBody, numberBody, minmaxBody, numberBodyVariable, , { checkSchema, lengthBody, numberBody, minmaxBody, numberBodyVariable,
inArrayBody } = require(__dirname+'/../../lib/input/schema.js'); inArrayBody } = require(__dirname+'/../../lib/input/schema.js');
@ -13,8 +14,8 @@ module.exports = {
paramConverter: paramConverter({ 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'], 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', 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'], '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', 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_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', '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', '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' }, }, expected: false, error: 'Extra mime types must be like type/subtype' },
{ result: () => { { result: () => {
if (req.body.archive_links) { 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 /https?\:\/\/[^\s<>\[\]{}|\\^]+%s[^\s<>\[\]{}|\\^]*/i.test(req.body.archive_links);
} }
return false; 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: 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: 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: 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.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_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' }, { 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') .fill('#000000')
.fontSize(65); .fontSize(65);
if (captchaOptions.text.font !== 'default') {
captcha.font(captchaOptions.text.font);
}
//draw each character at their x based on the characterWidth() //draw each character at their x based on the characterWidth()
const textWidth = totalTextWidth(text); const textWidth = totalTextWidth(text);
const startX = (width-textWidth)/2; const startX = (width-textWidth)/2;
@ -58,27 +62,26 @@ module.exports = async () => {
charX += characterWidth(text[i]); 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 //draw optional line/strikethrough
if (captchaOptions.text.line) { if (captchaOptions.text.line === true) {
const lineY = await randomRange(35,45); const lineY = await randomRange(35,45);
captcha.drawRectangle(startX, lineY, startX+textWidth, lineY+4); captcha.drawRectangle(startX, lineY, startX+textWidth, lineY+4);
} }
//add optional wave effect //add optional wave effect
if (captchaOptions.text.wave) { if (captchaOptions.text.wave > 0) {
captcha.wave(5, width/4); captcha.wave(captchaOptions.text.wave, width/4);
} }
//add optional paint effect //add optional paint effect
if (captchaOptions.text.paint) { if (captchaOptions.text.paint > 0) {
captcha.paint(2); 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 //create an array of distortions and apply to the image, if distortion is enabled
const { distortion, numDistorts } = captchaOptions; 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' }, { await db.collection('globalsettings').updateOne({ _id: 'globalsettings' }, {
'$set': { '$set': {
'captchaOptions.text': { 'captchaOptions.text': {
'font': 'default',
'line': false, 'line': false,
'wave': false, 'wave': 0,
'paint': false, 'paint': 2,
'noise': 0,
}, },
'captchaOptions.grid.falses': ['○','□','♘','♢','▽','△','♖','✧','♔','♘','♕','♗','♙','♧'], 'captchaOptions.grid.falses': ['○','□','♘','♢','▽','△','♖','✧','♔','♘','♕','♗','♙','♧'],
'captchaOptions.grid.trues': ['●','■','♞','♦','▼','▲','♜','✦','♚','♞','♛','♝','♟','♣'], 'captchaOptions.grid.trues': ['●','■','♞','♦','▼','▲','♜','✦','♚','♞','♛','♝','♟','♣'],

@ -78,9 +78,11 @@ module.exports = async (req, res) => {
falses: arraySetting(req.body.captcha_options_grid_falses, oldSettings.captchaOptions.grid.falses), falses: arraySetting(req.body.captcha_options_grid_falses, oldSettings.captchaOptions.grid.falses),
}, },
text: { 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), line: booleanSetting(req.body.captcha_options_text_line, oldSettings.captchaOptions.text.line),
wave: booleanSetting(req.body.captcha_options_text_wave, oldSettings.captchaOptions.text.wave), wave: numberSetting(req.body.captcha_options_text_wave, oldSettings.captchaOptions.text.wave),
paint: booleanSetting(req.body.captcha_options_text_paint, oldSettings.captchaOptions.text.paint), 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: { numDistorts: {
min: numberSetting(req.body.captcha_options_num_distorts_min, oldSettings.captchaOptions.numDistorts.min), min: numberSetting(req.body.captcha_options_num_distorts_min, oldSettings.captchaOptions.numDistorts.min),

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

@ -26,6 +26,7 @@ block content
.form-wrapper.flexleft.mt-10 .form-wrapper.flexleft.mt-10
form.form-post(action=`/forms/global/settings`, enctype='application/x-www-form-urlencoded', method='POST') form.form-post(action=`/forms/global/settings`, enctype='application/x-www-form-urlencoded', method='POST')
input(type='hidden' name='_csrf' value=csrf) input(type='hidden' name='_csrf' value=csrf)
input.row(type='submit', value='save settings')
.row.wrap.sb .row.wrap.sb
.col.mr-5 .col.mr-5
@ -193,20 +194,26 @@ block content
h4.mv-5 Text Captcha Options h4.mv-5 Text Captcha Options
.row .row
.label Font .label Font
select(name='captcha_options_text_font' disabled) select(name='captcha_options_text_font')
option(selected=true) Default 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 .row
.label Strikethrough Effect .label Strikethrough Effect
label.postform-style.ph-5 label.postform-style.ph-5
input(type='checkbox', name='captcha_options_text_line', value='true' checked=settings.captchaOptions.text.line) input(type='checkbox', name='captcha_options_text_line', value='true' checked=settings.captchaOptions.text.line)
.row .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 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 .row
.label Paint Effect .label Noise Effect Strength
label.postform-style.ph-5 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 .row
h4.mv-5 Captcha Distortion h4.mv-5 Captcha Distortion
.row .row

Loading…
Cancel
Save