/usr/share/usermin
NameSizeModeActions
at/-0755rm
authentic-theme/-0755rm
blue-theme/-0755rm
changepass/-0755rm
chfn/-0755rm
commands/-0755rm
cron/-0755rm
cshrc/-0755rm
fetchmail/-0755rm
filemin/-0755rm
filter/-0755rm
forward/-0755rm
gnupg/-0755rm
gray-theme/-0755rm
htaccess/-0755rm
htaccess-htpasswd/-0755rm
images/-0755rm
lang/-0755rm
language/-0755rm
mailbox/-0755rm
mailcap/-0755rm
man/-0755rm
mysql/-0755rm
plan/-0755rm
postgresql/-0755rm
proc/-0755rm
procmail/-0755rm
quota/-0755rm
schedule/-0755rm
shell/-0755rm
spam/-0755rm
ssh/-0755rm
theme/-0755rm
tunnel/-0755rm
twofactor/-0755rm
ulang/-0755rm
unauthenticated/-0755rm
updown/-0755rm
usermount/-0755rm
vendor_perl/-0755rm
xterm/-0755rm
acl_security.pl29590755editdlrm
chooser.cgi73870755editdlrm
config-aix2270644editdlrm
config-cobalt-linux2640644editdlrm
config-coherent-linux2640644editdlrm
config-corel-linux2640644editdlrm
config-debian-linux2640644editdlrm
config-freebsd2560644editdlrm
config-generic-linux2640644editdlrm
config-gentoo-linux2640644editdlrm
config-hpux2430644editdlrm
config-irix2840644editdlrm
config-lib.pl121200755editdlrm
config-macos2600644editdlrm
config-mandrake-linux2780644editdlrm
config-msc-linux2640644editdlrm
config-netbsd2830644editdlrm
config-open-linux2640644editdlrm
config-openbsd2410644editdlrm
config-openmamba-linux2640644editdlrm
config-openserver2360644editdlrm
config-osf12660644editdlrm
config-redhat-linux2640644editdlrm
config-slackware-linux2800644editdlrm
config-sol-linux2640644editdlrm
config-solaris4170644editdlrm
config-suse-linux2640644editdlrm
config-trustix-linux2640644editdlrm
config-turbo-linux2640644editdlrm
config-united-linux2640644editdlrm
config-unixware2860644editdlrm
copyconfig.pl44360755editdlrm
date_chooser.cgi22410755editdlrm
deb-name80644editdlrm
defaultacl980644editdlrm
defaulttheme160644editdlrm
entities_map.txt15010644editdlrm
favicon.ico150860644editdlrm
feedback.cgi65190755editdlrm
feedback_form.cgi35330755editdlrm
group_chooser.cgi76900755editdlrm
help.cgi23130755editdlrm
html-editor-lib.pl181470644editdlrm
index.cgi57810755editdlrm
install-module.pl13040755editdlrm
install-type40644editdlrm
javascript-lib.pl150420755editdlrm
lang_list.txt25700644editdlrm
LICENCE15160644editdlrm
LICENCE.ja16540644editdlrm
maketemp.pl4240755editdlrm
mime.types127130644editdlrm
miniserv.pem29690644editdlrm
miniserv.pl1894390755editdlrm
newmods.pl12800755editdlrm
oschooser.pl46550755editdlrm
os_list.txt358250644editdlrm
pam_login.cgi44290755editdlrm
password_change.cgi71920755editdlrm
password_form.cgi13270755editdlrm
perlpath.pl5710755editdlrm
README19720644editdlrm
robots.txt260644editdlrm
session_login.cgi52300755editdlrm
setup.sh268100755editdlrm
switch_user.cgi4040755editdlrm
thirdparty.pl17740755editdlrm
uconfig.cgi14280755editdlrm
uconfig_save.cgi14900755editdlrm
ui-lib.pl1159450755editdlrm
update-from-repo.sh151240755editdlrm
updateboot.pl24290755editdlrm
uptracker.cgi29560755editdlrm
usermin-init19270755editdlrm
usermin-pam1010644editdlrm
usermin-pam-osx2430644editdlrm
usermin-systemd4430644editdlrm
user_chooser.cgi75710755editdlrm
version60644editdlrm
web-lib-funcs.pl4051600755editdlrm
web-lib.pl9070755editdlrm
webmin-search-lib.pl96970755editdlrm
WebminCore.pm88730644editdlrm
webmin_search.cgi26970755editdlrm
Edit: /usr/share/usermin/html-editor-lib.pl (18147B)
# html-editor-lib.pl # Quill HTML editor related subs sub html_editor_load_bundle { my ($opts) = @_; $opts ||= {}; my $html_editor_load_scripts; # Load extra modules first $html_editor_load_scripts .= &html_editor_load_modules($opts); # Load Quill HTML editor files $html_editor_load_scripts .= < EOF return $html_editor_load_scripts; } sub html_editor_load_modules { my ($opts) = @_; my $html_editor_load_modules; my $load_css_modules = sub { my ($css_modules) = @_; foreach my $module (@{$css_modules}) { $html_editor_load_modules .= "{'web'}->{'prefix'}/unauthenticated/css/$module.min.css?$opts->{'_'}->{'web'}->{'timestamp'}' rel='stylesheet'>\n"; } }; my $load_js_modules = sub { my ($js_modules) = @_; foreach my $module (@{$js_modules}) { $html_editor_load_modules .= "\n"; } }; # Load extra CSS modules if ($opts->{'extra'}->{'css'}) { &$load_css_modules($opts->{'extra'}->{'css'}) } # Load extra JS modules if ($opts->{'extra'}->{'js'}) { &$load_js_modules($opts->{'extra'}->{'js'}) } # Automatically load dependencies # based on editor mode if ($opts->{'type'} eq "advanced") { my $highlight_bundle = ['highlight/highlight']; my @highlight_bundle = @{$highlight_bundle}; if ($opts->{'_'}->{'client'}->{'palette'} eq 'dark') { foreach (@highlight_bundle) { $_ .= "-dark" if (-e "$root_directory/unauthenticated/css/$_-dark.min.css"); } } &$load_css_modules(\@highlight_bundle); &$load_js_modules($highlight_bundle); } return $html_editor_load_modules; } sub html_editor_template { my ($opts) = @_; $opts ||= {}; $html_editor_template = <{'before'}->{'container'}
$opts->{'before'}->{'editor'}
$opts->{'after'}->{'editor'}
$opts->{'after'}->{'container'} EOF return $html_editor_template; } sub html_editor_styles { my ($type) = @_; # HTML editor toolbar styles if ($type eq 'toolbar') { return < .ql-compose-container .ql-snow .ql-formats:empty { display: none; } .ql-compose-container .ql-snow .ql-picker.ql-font .ql-picker-label::before, .ql-compose-container .ql-snow .ql-picker.ql-font .ql-picker-item::before { content: '$text{'editor_fontfamily_default'}'; } .ql-compose-container .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before, .ql-compose-container .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before { content: '$text{'editor_fontfamily_monospace'}'; } .ql-compose-container .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="0.75em"]::before, .ql-compose-container .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="0.75em"]::before { content: '$text{'editor_font_small'}'; } .ql-compose-container .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="0.75em"]::before { font-size: 0.75em; } .ql-compose-container .ql-snow .ql-picker.ql-size .ql-picker-label::before, .ql-compose-container .ql-snow .ql-picker.ql-size .ql-picker-item::before { content: '$text{'editor_font_normal'}'; } .ql-compose-container .ql-snow .ql-picker.ql-size .ql-picker-item::before { font-size: 1em; } .ql-compose-container .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="1.15em"]::before, .ql-compose-container .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="1.15em"]::before { content: '$text{'editor_font_medium'}'; } .ql-compose-container .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="1.15em"]::before { font-size: 1.15em; } .ql-compose-container .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="1.3em"]::before, .ql-compose-container .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="1.3em"]::before { content: '$text{'editor_font_large'}'; } .ql-compose-container .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="1.3em"]::before { font-size: 1.3em; } .ql-compose-container .ql-snow .ql-picker.ql-header .ql-picker-label::before, .ql-compose-container .ql-snow .ql-picker.ql-header .ql-picker-item::before { content: '$text{'editor_paragraph'}'; } .ql-compose-container .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before, .ql-compose-container .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before { content: '$text{'editor_heading'} 1' } .ql-compose-container .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before, .ql-compose-container .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before { content: '$text{'editor_heading'} 2' } .ql-compose-container .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before, .ql-compose-container .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before { content: '$text{'editor_heading'} 3' } .ql-compose-container .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before, .ql-compose-container .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before { content: '$text{'editor_heading'} 4' } .ql-compose-container .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before, .ql-compose-container .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before { content: '$text{'editor_heading'} 5' } .ql-compose-container .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before, .ql-compose-container .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before { content: '$text{'editor_heading'} 6' } EOF } } sub html_editor_toolbar { my ($opts) = @_; # Toolbar modes if ($opts->{'type'} eq 'basic') { return <{'type'} eq 'simple') { return <{'type'} eq 'advanced') { return <{'textarea'}->{'target'}; my $target_attr = $target_text->{'attr'} || 'name'; my $target_type = $target_text->{'type'} || '='; my $target_name = $target_text->{'name'}; # HTML editor toolbar mode my $iframe_styles_mode = $opts->{'type'}; my $iframe_styles = "e_escape( &read_file_contents("$root_directory/unauthenticated/css/_iframe/$iframe_styles_mode.min.css"), '"'); $iframe_styles =~ s/\n/ /g; my $navigation_type = $ENV{'HTTP_X_NAVIGATION_TYPE'}; $navigation_type ||= 'reload'; my $html_editor_init_script = < function fn_${module_name}_quote_mail_iframe_loaded() { const editor = fn_${module_name}_html_editor_init.editor; editor.root.innerHTML = "\\n" + editor.root.innerHTML; fn_${module_name}_quote_mail_iframe_loaded = null; } function fn_${module_name}_html_editor_init() { const targ = document.querySelector('[$target_attr$target_type"$target_name"]'), qs = Quill.import('attributors/style/size'), qf = Quill.import('attributors/style/font'), isMac = navigator.userAgent.toLowerCase().includes('mac'), navigation_type = '$navigation_type', iframe_styles = "$iframe_styles"; qs.whitelist = ["0.75em", "1.15em", "1.3em"]; Quill.register(qs, true); qf.whitelist = ["monospace"], Quill.register(qf, true); const editor = new Quill('.ql-container', { modules: { syntax: typeof hljs === 'object', imageDrop: true, imageResize: { modules: [ 'DisplaySize', 'Resize', ], }, clipboard: { matchVisual: false }, toolbar: @{[&html_editor_toolbar($opts)]}, }, bounds: '.ql-compose-container', theme: 'snow' }); fn_${module_name}_html_editor_init.editor = editor; // Google Mail like key bind for creating numbered list (Ctrl+Shift+7) editor.keyboard.addBinding({ key: '7', shiftKey: true, ctrlKey: !isMac, metaKey: isMac, }, function(range, context) { const currentFormat = this.quill.getFormat(range.index); if (currentFormat.list === 'ordered') { this.quill.format('list', false); } else { this.quill.format('list', 'ordered'); } }); // Google Mail like key bind for creating bullet list (Ctrl+Shift+8) editor.keyboard.addBinding({ key: '8', shiftKey: true, ctrlKey: !isMac, metaKey: isMac, }, function(range, context) { const currentFormat = this.quill.getFormat(range.index); if (currentFormat.list === 'bullet') { this.quill.format('list', false); } else { this.quill.format('list', 'bullet'); } }); // Google Mail like key bind for creating blockquote (Ctrl+Shift+9) editor.keyboard.addBinding({ key: '9', shiftKey: true, ctrlKey: !isMac, metaKey: isMac, }, function(range, context) { const currentFormat = this.quill.getFormat(range.index); if (currentFormat.blockquote) { this.quill.format('blockquote', false); } else { this.quill.format('blockquote', true); } }); editor.on('text-change', function() { // This should most probably go to onSubmit event targ.value = editor.root.innerHTML + "
"; sessionStorage.setItem('$module_name/quill=last-message', editor.root.innerHTML); let extraValue = String(), sync = JSON.parse('@{[&convert_to_json($opts->{'textarea'}->{'sync'}->{'data'})]}'), position = '@{[$opts->{'textarea'}->{'sync'}->{'position'}]}', err = false; try { // Gather data from additional elements if given if (sync.constructor === Array) { sync.forEach(function(_) { let content_document = document; if (_.iframe) { content_document = document.querySelector(_.iframe).contentWindow.document; } _.elements.forEach(function(element) { const element_ = content_document.querySelector(element); (element_ && (extraValue += element_.innerHTML)); }) }); } } catch(e) { err = true; } if (!err) { if (position === 'before') { targ.value = extraValue + targ.value; } else { targ.value = targ.value + extraValue; } } // Inject our styles (unless already injected) to be sent alongside // with the message. These styles later can be optionally turned in // inline styling to satisfy GMail strict rules about HTML emails if (!targ.value.match(/data-iframe-mode=(.*?)$iframe_styles_mode(.*?)/)) { targ.value = targ.value + ''; } }); // Prevent loosing focus for toolbar selects (color picker, font select and etc) editor.getModule("toolbar").container.addEventListener("mousedown", (e) => { e.preventDefault(); }); // Don't loose message content if the page // is reloaded or history back is clicked let restore_message = false; try { restore_message = window.performance.getEntriesByType("navigation")[0].type !== 'navigate' && navigation_type !== 'navigate' } catch(e) { restore_message = false; } if (restore_message) { const quill_last_message = sessionStorage.getItem('$module_name/quill=last-message'); if (quill_last_message) { editor.pasteHTML(quill_last_message); return; } } // Update editor on initial load editor.pasteHTML(targ.value); sessionStorage.setItem('$module_name/quill=last-message', editor.root.innerHTML); } @{[$opts->{'load'} ? "fn_${module_name}_html_editor_init()" : '']} EOF return $html_editor_init_script; } sub html_editor { my ($opts) = @_; # Populate defaults &html_editor_opts_populate_defaults($opts); # Get template my $html_editor_template = &html_editor_template($opts); # Get toolbar styling my $html_editor_styles_toolbar = &html_editor_styles('toolbar'); # Load bundles my ($html_editor_load_scripts); my %tinfo = &get_theme_info($current_theme); if (!$tinfo{'spa'}) { # Load HTML editor files and dependencies $html_editor_load_scripts = &html_editor_load_bundle($opts); } # HTML editor init $opts->{'load'} = !$tinfo{'spa'}; my $html_editor_init_scripts = &html_editor_init_script($opts); # Return complete HTML editor return $html_editor_template . $html_editor_styles_toolbar . $html_editor_load_scripts . $html_editor_init_scripts; } sub html_editor_opts_populate_defaults { my ($opts) = @_; # Miniserv webprefix $opts->{'_'}->{'web'}->{'prefix'} = &get_webprefix(); # Webmin version to timestamp my $webmin_version = &get_webmin_version(); $webmin_version =~ s/[.-]+//g; $opts->{'_'}->{'web'}->{'timestamp'} = $webmin_version; # Client color palette $opts->{'_'}->{'client'}->{'palette'} = $ENV{'HTTP_X_COLOR_PALETTE'}; } sub html_editor_substitute_classes_with_styles { my ($styled_html_email) = @_; my ($document_styles_string) = $styled_html_email =~ /(.*)<\/style>/; if ($document_styles_string) { my (%document_styles_class_names) = $document_styles_string =~ /(\.[\w\-\_\d\,\.]+)\s*\{\s*([^}]*?)\s*\}/migx; my $class_string = sub { return "class=\"$_[0]\""; }; my $style_string = sub { return "style=\"$_[0]\""; }; my $style_format = sub { my ($stl) = @_; # Format style nicely, as Google Mail insists # on having these formatted neatly $stl =~ s/(:|;)\s*/$1 /g; $stl =~ s/(? length($b) } keys %document_styles_class_names) { my @classes = split(/\s*,\s*/, $classes); foreach my $class (reverse sort { length($a) <=> length($b) } @classes) { my (@class_parts) = $class =~ /\.([\S][^\.]+)/migx; my (@style_exact_full) = map { &$style_format($document_styles_class_names{$_}) } grep { $_ =~ /(?/migx; foreach my $tag_name (&unique(@document_styles_tag_names)) { my (%document_styles_tag_names) = $document_styles_string =~ /(\Q$tag_name\E)\s*\{\s*([^}]*?)\s*\}/migx; foreach my $tag (keys %document_styles_tag_names) { my $tag_style = &$style_format($document_styles_tag_names{$tag}); $styled_html_email =~ s/(<$tag)(?![^>]+style).*?>/$1 style="$tag_style">/mig; } } } return $styled_html_email; } 1;