Replace TinyMCE 4.2.8 with a small local editor.
TinyMCE 4.2.8 has known XSS bugs. publish/editor.js has the same toolbar and writes the same HTML. The textarea still posts the story.
This commit is contained in:
+1
-1
@@ -23,7 +23,7 @@ if (!isset($_SESSION['editor'])) {
|
||||
<link rel='stylesheet' type='text/css' href='../publish/style.css' />
|
||||
<script type="text/javascript" src="../utilities.js"></script>
|
||||
<link rel="alternate" type="application/rss+xml" title="xxx.org.za RSS feed" href="/feed" />
|
||||
<?php include_once('./tinymce_init.inc.php'); ?>
|
||||
<?php include_once('./editor.inc.php'); ?>
|
||||
</head>
|
||||
<?php
|
||||
flush();
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* The story editor (publish/editor.js). The colors are the ones of the
|
||||
* TinyMCE "xxx" skin that it replaces, and the size and border follow the
|
||||
* TinyMCE 5 settings of the live site: at most 450px wide, at least 300px
|
||||
* high, resizable, with the toolbar on top.
|
||||
*/
|
||||
.xed {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
max-width: 450px;
|
||||
min-width: 200px;
|
||||
min-height: 300px;
|
||||
overflow: hidden;
|
||||
resize: both;
|
||||
border: 1px dashed #006633;
|
||||
border-radius: 8px;
|
||||
background-color: #e2f0e2;
|
||||
}
|
||||
|
||||
.xed-area {
|
||||
box-sizing: border-box;
|
||||
flex: 1 1 300px;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
padding: 0 8px;
|
||||
background-color: #ffffff;
|
||||
color: #000000;
|
||||
font-size: 12pt;
|
||||
outline: none;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
/* The first and last paragraph keep their space, as in the old iframe */
|
||||
.xed-area > :first-child {
|
||||
margin-top: 1em;
|
||||
}
|
||||
.xed-area blockquote {
|
||||
margin: 1em 40px;
|
||||
}
|
||||
|
||||
.xed-toolbar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex: none;
|
||||
padding: 2px 0 4px;
|
||||
border-bottom: 1px dashed #006633;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
.xed-group {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
margin: 2px 0 0 2px;
|
||||
}
|
||||
.xed-group + .xed-group {
|
||||
margin-left: 3px;
|
||||
padding-left: 3px;
|
||||
border-left: 1px solid #9ef89e;
|
||||
}
|
||||
|
||||
.xed-btn {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 24px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 0;
|
||||
background-color: #e0efe0;
|
||||
color: #333333;
|
||||
cursor: pointer;
|
||||
}
|
||||
.xed-btn:hover,
|
||||
.xed-btn:focus-visible {
|
||||
background-color: #cde8cd;
|
||||
border-color: #86f686;
|
||||
outline: none;
|
||||
}
|
||||
.xed-btn.xed-active {
|
||||
background-color: #bfe7bf;
|
||||
border-color: #86f686;
|
||||
}
|
||||
.xed-btn svg {
|
||||
display: block;
|
||||
filter: drop-shadow(0 1px 1px rgba(255, 255, 255, 0.75));
|
||||
}
|
||||
|
||||
/* Dialogs */
|
||||
.xed-modal-block {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 1000;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.xed-window {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
z-index: 1001;
|
||||
transform: translate(-50%, -50%);
|
||||
box-sizing: border-box;
|
||||
max-width: calc(100vw - 20px);
|
||||
max-height: calc(100vh - 20px);
|
||||
overflow: auto;
|
||||
border: 1px dashed #006633;
|
||||
background-color: #e5fde5;
|
||||
color: #333333;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
}
|
||||
.xed-window-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 9px 15px;
|
||||
border-bottom: 1px dashed #006633;
|
||||
}
|
||||
.xed-title {
|
||||
flex: 1;
|
||||
padding-right: 10px;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
line-height: 20px;
|
||||
}
|
||||
.xed-close {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: none;
|
||||
color: #6d6d6d;
|
||||
font: bold 20px/20px "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
cursor: pointer;
|
||||
}
|
||||
.xed-close:hover {
|
||||
color: #333333;
|
||||
}
|
||||
.xed-window-body {
|
||||
padding: 20px;
|
||||
}
|
||||
.xed-foot {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 3px;
|
||||
padding: 10px;
|
||||
border-top: 1px dashed #006633;
|
||||
}
|
||||
.xed-button {
|
||||
box-sizing: border-box;
|
||||
height: 30px;
|
||||
padding: 4px 8px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 0;
|
||||
background-color: #e0efe0;
|
||||
color: #333333;
|
||||
font: 14px/20px "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
cursor: pointer;
|
||||
}
|
||||
.xed-button:hover,
|
||||
.xed-button:focus-visible {
|
||||
background-color: #cde8cd;
|
||||
border-color: #86f686;
|
||||
outline: none;
|
||||
}
|
||||
.xed-button.xed-primary {
|
||||
min-width: 52px;
|
||||
background-color: #006dcc;
|
||||
color: #ffffff;
|
||||
}
|
||||
.xed-button.xed-primary:hover,
|
||||
.xed-button.xed-primary:focus-visible {
|
||||
background-color: #005fb3;
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
/* Link dialog */
|
||||
.xed-form {
|
||||
width: 409px;
|
||||
max-width: 100%;
|
||||
}
|
||||
.xed-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.xed-row:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.xed-row label {
|
||||
flex: 0 0 120px;
|
||||
line-height: 16px;
|
||||
}
|
||||
.xed-textbox {
|
||||
box-sizing: border-box;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
height: 30px;
|
||||
padding: 0 4px;
|
||||
border: 1px solid #c5c5c5;
|
||||
border-radius: 0;
|
||||
background-color: #ffffff;
|
||||
color: #333333;
|
||||
font: 14px "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
.xed-textbox:focus {
|
||||
border-color: #59a5e1;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Special character dialog */
|
||||
.xed-window-body:has(.xed-charmap-body) {
|
||||
padding: 10px;
|
||||
}
|
||||
.xed-charmap-body {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.xed-charmap {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.xed-charmap td {
|
||||
min-width: 12px;
|
||||
height: 20px;
|
||||
padding: 2px;
|
||||
border: 1px dashed #006633;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
cursor: default;
|
||||
}
|
||||
.xed-charmap td:hover,
|
||||
.xed-charmap td:focus-visible {
|
||||
background-color: #9ec49e;
|
||||
outline: none;
|
||||
}
|
||||
.xed-charmap-preview {
|
||||
width: 120px;
|
||||
text-align: center;
|
||||
}
|
||||
.xed-charmap-char {
|
||||
height: 80px;
|
||||
font-size: 40px;
|
||||
line-height: 80px;
|
||||
}
|
||||
.xed-charmap-name {
|
||||
margin-top: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* Source code dialog */
|
||||
.xed-source {
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
width: 600px;
|
||||
max-width: 100%;
|
||||
height: 500px;
|
||||
max-height: calc(100vh - 160px);
|
||||
padding: 4px;
|
||||
border: 1px solid #59a5e1;
|
||||
border-radius: 0;
|
||||
background-color: #ffffff;
|
||||
color: #333333;
|
||||
font: 14px "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
resize: none;
|
||||
}
|
||||
.xed-source:focus {
|
||||
outline: none;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<link rel="stylesheet" type="text/css" href="../publish/editor.css" />
|
||||
<script type="text/javascript" src="../publish/utilities.js"></script>
|
||||
<script type="text/javascript" src="../publish/editor.js"></script>
|
||||
<script type="text/javascript">
|
||||
var storyEditor = null;
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var story = document.getElementById('story');
|
||||
if (!story)
|
||||
return;
|
||||
storyEditor = XxxEditor.attach(story);
|
||||
// Count words as the story changes
|
||||
story.addEventListener('input', updateWordCount);
|
||||
updateWordCount();
|
||||
});
|
||||
</script>
|
||||
+1065
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -17,7 +17,7 @@ require_once("../session.lib.php");
|
||||
<link rel='stylesheet' type='text/css' href='../publish/style.css' />
|
||||
<script type="text/javascript" src="../utilities.js"></script>
|
||||
<link rel="alternate" type="application/rss+xml" title="xxx.org.za RSS feed" href="/feed" />
|
||||
<?php include_once('tinymce_init.inc.php'); ?>
|
||||
<?php include_once('./editor.inc.php'); ?>
|
||||
<?php if (!$is_admin ) { ?>
|
||||
<?= captcha_script() ?>
|
||||
<?php } ?>
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
<script type="text/javascript" src="../publish/utilities.js"></script>
|
||||
<script type="text/javascript" src="../tinymce/js/tinymce/tinymce.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
tinymce.init({
|
||||
selector: "textarea#story",
|
||||
width : "450",
|
||||
height : "300",
|
||||
skin : "xxx",
|
||||
content_css : "../style.css",
|
||||
plugins : "link charmap code",
|
||||
toolbar : "bold italic | alignleft aligncenter alignright alignjustify | blockquote | outdent indent | link unlink | charmap code",
|
||||
toolbar_items_size : 'small',
|
||||
menubar : false,
|
||||
statusbar : false,
|
||||
setup : function(editor) {
|
||||
// Count words
|
||||
editor.on('keyup', function(e) {
|
||||
mceWordCount(e);
|
||||
});
|
||||
// Move location of toolbar
|
||||
editor.on('init', function(e) {
|
||||
var $ = tinymce.dom.DomQuery;
|
||||
var toolbar = $('div.mce-toolbar-grp');
|
||||
var editarea = $('div.mce-edit-area');
|
||||
if (toolbar.hasClass('mce-first')) {
|
||||
editarea.after(toolbar);
|
||||
editarea.css('border-width', '0px 0px 1px 0px');
|
||||
toolbar.removeClass('mce-first');
|
||||
toolbar.addClass('mce-last');
|
||||
if (editarea.hasClass('mce-last')) {
|
||||
editarea.removeClass('mce-last');
|
||||
editarea.addClass('mce-first');
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
</script>
|
||||
+16
-28
@@ -1,6 +1,3 @@
|
||||
// GLOBAL
|
||||
var tinyMCEstate = true;
|
||||
|
||||
function showHide(show) {
|
||||
if (show == 'text') {
|
||||
var hide = 'upload';
|
||||
@@ -14,18 +11,9 @@ function showHide(show) {
|
||||
document.getElementById(hide+'parent').style.display = "none";
|
||||
document.getElementById(show+'parent').style.display = "block";
|
||||
document.getElementById('wordcounter').style.display = "block";
|
||||
if (!tinyMCEstate && tinyMCE) {
|
||||
// Toggling breaks getContent if the inital state is toggled off.
|
||||
//tinyMCE.execCommand('mceToggleEditor',false,'text');
|
||||
tinyMCEstate = true;
|
||||
}
|
||||
tinyMCE.execCommand('mceFocus',false,'text');
|
||||
if (window.storyEditor)
|
||||
storyEditor.focus();
|
||||
} else if (show == 'upload') {
|
||||
if (tinyMCEstate && tinyMCE) {
|
||||
// Toggling breaks getContent if the inital state is toggled off.
|
||||
//tinyMCE.execCommand('mceToggleEditor',false,'text');
|
||||
tinyMCEstate = false;
|
||||
}
|
||||
document.getElementById('wordcounter').style.display = "none";
|
||||
document.getElementById(hide+'parent').style.display = "none";
|
||||
document.getElementById(show+'parent').style.display = "block";
|
||||
@@ -52,7 +40,7 @@ function review() {
|
||||
var upload = document.getElementById('upload');
|
||||
var uploaded = document.getElementById('uploaded');
|
||||
var remove_img = document.getElementById('remove_img');
|
||||
var txtcnt = wc(tinyMCE.get('story').getContent());
|
||||
var txtcnt = wc(document.getElementById('story').value);
|
||||
if (is_delete) return true;
|
||||
if (!title.value) {
|
||||
alert("Please add a title to your submission; one makes everything much prettier.");
|
||||
@@ -92,20 +80,20 @@ function toggle_remove() {
|
||||
}
|
||||
}
|
||||
|
||||
function mceWordCount (e) {
|
||||
if (e && e.type != 'keyup')
|
||||
return true;
|
||||
var txt = tinyMCE.get('story').getContent();
|
||||
// Show the number of words in the story (publish/editor.inc.php calls
|
||||
// this when the story changes)
|
||||
function updateWordCount() {
|
||||
var story = document.getElementById("story");
|
||||
var wordcounter = document.getElementById("wordcounter");
|
||||
var cnt = wc(txt);
|
||||
if (!story || !wordcounter)
|
||||
return true;
|
||||
var cnt = wc(story.value);
|
||||
var words = (cnt == 1) ? " word" : " words";
|
||||
if (wordcounter) {
|
||||
wordcounter.innerHTML = cnt + words;
|
||||
wordcounter.className = "counter";
|
||||
if (cnt == 100) wordcounter.className += " yes";
|
||||
if (cnt > 100) wordcounter.className += " no";
|
||||
}
|
||||
return true;
|
||||
wordcounter.innerHTML = cnt + words;
|
||||
wordcounter.className = "counter";
|
||||
if (cnt == 100) wordcounter.className += " yes";
|
||||
if (cnt > 100) wordcounter.className += " no";
|
||||
return true;
|
||||
}
|
||||
|
||||
function wc (txt) {
|
||||
@@ -115,7 +103,7 @@ function wc (txt) {
|
||||
txt = txt.replace(/\s[^a-zA-Z0-9]+\s/g," "); //strip punctuation
|
||||
txt = txt.replace(/\s*((\S+\s*)*)/, "$1"); //ltrim
|
||||
txt = txt.replace(/((\s*\S+)*)\s*/, "$1"); //rtrim
|
||||
txt = txt.replace(/( )/g, " "); //TinyMCE newlines leave spaces behind
|
||||
txt = txt.replace(/( )/g, " "); //empty paragraphs and non-breaking spaces
|
||||
txt = txt.replace(/(^\s+|\s+$)/, " "); // Remove trailing spaces
|
||||
if (txt.match(/^ *$/))
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user