From b03f881b11204b7e0fe323dd9148085ef0dfa35d Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Sun, 7 Apr 2013 04:40:04 -0400 Subject: allowed to choose plain text or rich text editor for the comments --- askbot/conf/forum_data_rules.py | 23 +++- askbot/doc/source/changelog.rst | 2 + askbot/media/js/post.js | 88 +++++++++++---- askbot/media/js/utils.js | 32 +++++- askbot/media/js/wmd/wmd.css | 67 +++++------ askbot/media/js/wmd/wmd.js | 142 +++++++++++++----------- askbot/media/style/style.less | 13 ++- askbot/models/post.py | 40 ++++--- askbot/templates/meta/bottom_scripts.html | 10 ++ askbot/templates/meta/html_head_javascript.html | 9 -- askbot/utils/markup.py | 17 +++ askbot/views/writers.py | 4 - 12 files changed, 282 insertions(+), 165 deletions(-) diff --git a/askbot/conf/forum_data_rules.py b/askbot/conf/forum_data_rules.py index 524ab16a..b88d551e 100644 --- a/askbot/conf/forum_data_rules.py +++ b/askbot/conf/forum_data_rules.py @@ -28,6 +28,21 @@ settings.register( ) ) +COMMENTS_EDITOR_CHOICES = ( + ('plain-text', 'Plain text editor'), + ('rich-text', 'Same editor as for questions and answers') +) + +settings.register( + livesettings.StringValue( + FORUM_DATA_RULES, + 'COMMENTS_EDITOR_TYPE', + default='plain-text', + choices=COMMENTS_EDITOR_CHOICES, + description=_('Editor for the comments') + ) +) + settings.register( livesettings.BooleanValue( FORUM_DATA_RULES, @@ -315,8 +330,12 @@ settings.register( livesettings.BooleanValue( FORUM_DATA_RULES, 'SAVE_COMMENT_ON_ENTER', - default = True, - description = _('Save comment by pressing key') + default=False, + description=_('Save comment by pressing key'), + help_text=_( + 'This may be useful when only one-line comments ' + 'are desired. Will not work with TinyMCE editor.' + ) ) ) diff --git a/askbot/doc/source/changelog.rst b/askbot/doc/source/changelog.rst index f59138c5..90ec1257 100644 --- a/askbot/doc/source/changelog.rst +++ b/askbot/doc/source/changelog.rst @@ -3,6 +3,8 @@ Changes in Askbot Development version ------------------- +* Added possibility to choose editor for comments: plain text, or same as + editor used for the questions or answers: WMD or TinyMCE. * Added a placeholder template for the custom javascript on the question page * Allowed to disable the big "ask" button. * Some support for the media compression (Tyler Mandry) diff --git a/askbot/media/js/post.js b/askbot/media/js/post.js index 8386ce5b..02aa02d6 100644 --- a/askbot/media/js/post.js +++ b/askbot/media/js/post.js @@ -1433,15 +1433,27 @@ EditCommentForm.prototype.setWaitingStatus = function(isWaiting) { this._editor.getElement().hide(); this._submit_btn.hide(); this._cancel_btn.hide(); + this._minorEditBox.hide(); + this._element.hide(); } else { + this._element.show(); this._editor.getElement().show(); this._submit_btn.show(); this._cancel_btn.show(); + this._minorEditBox.show(); } }; -EditCommentForm.prototype.startEditor = function() { - var editorId = 'comment-editor-' + getNewInt(); +EditCommentForm.prototype.getEditorType = function() { + if (askbot['settings']['commentsEditorType'] === 'rich-text') { + return askbot['settings']['editorType']; + } else { + return 'plain-text'; + } +}; + +EditCommentForm.prototype.startTinyMCEEditor = function() { + var editorId = this.makeId('comment-editor'); var opts = { mode: 'exact', content_css: mediaUrl('media/style/tinymce/comments-content.css'), @@ -1460,23 +1472,49 @@ EditCommentForm.prototype.startEditor = function() { var editor = new TinyMCE(opts); editor.setId(editorId); editor.setText(this._text); - //@todo: remove global variable maxCommentLength this._editorBox.prepend(editor.getElement()); editor.start(); - editor.focus(); this._editor = editor; +}; - return; +EditCommentForm.prototype.startWMDEditor = function() { + var editor = new WMD(); + editor.setEnabledButtons('bold italic link code ol ul'); + editor.setPreviewerEnabled(false); + editor.setText(this._text); + this._editorBox.prepend(editor.getElement());//attach DOM before start + editor.start();//have to start after attaching DOM + this._editor = editor; +}; + +EditCommentForm.prototype.startSimpleEditor = function() { + this._editor = new SimpleEditor(); + this._editorBox.prepend(this._editor.getElement()); +}; - //todo: make this work for tinymce +EditCommentForm.prototype.startEditor = function() { + var editorType = this.getEditorType(); + if (editorType === 'tinymce') { + this.startTinyMCEEditor(); + //@todo: implement save on enter and character counter in tinyMCE + return; + } else if (editorType === 'markdown') { + this.startWMDEditor(); + } else { + this.startSimpleEditor(); + } + + //code below is common to SimpleEditor and WMD + var editorElement = this._editor.getElement(); var updateCounter = this.getCounterUpdater(); var escapeHandler = makeKeyHandler(27, this.getCancelHandler()); - //todo: try this on the div - editor.getElement().blur(updateCounter) - .focus(updateCounter) - .keyup(updateCounter) - .keyup(escapeHandler); + var editor = this._editor; + //this should be set on the textarea! + editorElement.blur(updateCounter); + editorElement.focus(updateCounter); + editorElement.keyup(updateCounter) + editorElement.keyup(escapeHandler); if (askbot['settings']['saveCommentOnEnter']){ var save_handler = makeKeyHandler(13, this.getSaveHandler()); @@ -1513,8 +1551,8 @@ EditCommentForm.prototype.attachTo = function(comment, mode){ this.enableForm(); this.startEditor(); this._editor.setText(this._text); - //this._editor.focus(); - //this._editor.putCursorAtEnd(); + this._editor.focus(); + this._editor.putCursorAtEnd(); setupButtonEventHandlers(this._submit_btn, this.getSaveHandler()); setupButtonEventHandlers(this._cancel_btn, this.getCancelHandler()); }; @@ -1556,6 +1594,7 @@ EditCommentForm.prototype.getCounterUpdater = function(){ } counter.html(feedback); counter.css('color', color); + return true; }; return handler; }; @@ -1573,8 +1612,7 @@ EditCommentForm.prototype.canCancel = function(){ var ctext = this._editor.getText(); if ($.trim(ctext) == $.trim(this._text)){ return true; - } - else if (this.confirmAbandon()){ + } else if (this.confirmAbandon()){ return true; } this._editor.focus(); @@ -1718,7 +1756,8 @@ EditCommentForm.prototype.getSaveHandler = function(){ var timestamp = commentData['comment_added_at'] || gettext('just now'); var userName = commentData['user_display_name'] || askbot['data']['userName']; me._comment.setContent({ - 'html': text, + 'html': editor.getHtml(), + 'text': text, 'user_display_name': userName, 'comment_added_at': timestamp }); @@ -2426,6 +2465,10 @@ SimpleEditor.prototype.getText = function() { return $.trim(this._textarea.val()); }; +SimpleEditor.prototype.getHtml = function() { + return '
' + this.getText() + '
'; +}; + SimpleEditor.prototype.setText = function(text) { this._text = text; if (this._textarea) { @@ -2442,6 +2485,7 @@ SimpleEditor.prototype.createDom = function() { this._element = this.makeElement('div'); var textarea = this.makeElement('textarea'); this._element.append(textarea); + this._textarea = textarea; if (this._text) { textarea.val(this._text); }; @@ -2466,6 +2510,8 @@ var WMD = function(){ }; inherits(WMD, SimpleEditor); +//@todo: implement getHtml method that runs text through showdown renderer + WMD.prototype.setEnabledButtons = function(buttons){ this._enabled_buttons = buttons; }; @@ -2487,12 +2533,12 @@ WMD.prototype.createDom = function(){ this._element.append(wmd_container); var wmd_buttons = this.makeElement('div') - .attr('id', 'wmd-button-bar') + .attr('id', this.makeId('wmd-button-bar')) .addClass('wmd-panel'); wmd_container.append(wmd_buttons); var editor = this.makeElement('textarea') - .attr('id', 'editor'); + .attr('id', this.makeId('editor')); wmd_container.append(editor); this._textarea = editor; @@ -2501,7 +2547,7 @@ WMD.prototype.createDom = function(){ } var previewer = this.makeElement('div') - .attr('id', 'previewer') + .attr('id', this.makeId('previewer')) .addClass('wmd-preview'); wmd_container.append(previewer); this._previewer = previewer; @@ -2511,7 +2557,7 @@ WMD.prototype.createDom = function(){ }; WMD.prototype.start = function(){ - Attacklab.Util.startEditor(true, this._enabled_buttons); + Attacklab.Util.startEditor(true, this._enabled_buttons, this.getIdSeed()); }; /** @@ -2577,6 +2623,8 @@ TinyMCE.prototype.getText = function() { return tinyMCE.activeEditor.getContent(); }; +TinyMCE.prototype.getHtml = TinyMCE.prototype.getText; + TinyMCE.prototype.isLoaded = function() { return (tinymce.get(this._id) !== undefined); }; diff --git a/askbot/media/js/utils.js b/askbot/media/js/utils.js index a26c1ec9..0c056f44 100644 --- a/askbot/media/js/utils.js +++ b/askbot/media/js/utils.js @@ -28,7 +28,16 @@ var animateHashes = function(){ } }; -var getNewInt = function() { +/** + * @param {string} id_token - any token + * @param {string} unique_seed - the unique part + * @returns {string} unique id that can be used in DOM + */ +var askbotMakeId = function(id_token, unique_seed) { + return id_token + '-' + unique_seed; +}; + +var getNewUniqueInt = function() { var num = askbot['data']['uniqueInt'] || 0; num = num + 1; askbot['data']['uniqueInt'] = num; @@ -322,11 +331,32 @@ var inherits = function(childCtor, parentCtor) { var WrappedElement = function(){ this._element = null; this._in_document = false; + this._idSeed = null; }; /* note that we do not call inherits() here * See TippedInput as an example of a subclass */ +/** + * returns a unique integer for any instance of WrappedElement + * which can be used to construct a unique id for use in the DOM + * @return {string} + */ +WrappedElement.prototype.getIdSeed = function() { + var seed = this._idSeed || parseInt(getNewUniqueInt()); + this._idSeed = seed; + return seed; +}; + +/** + * returns unique ide based on the prefix and the id seed + * @param {string} prefix + * @return {string} + */ +WrappedElement.prototype.makeId = function(prefix) { + return askbotMakeId(prefix, this.getIdSeed()); +}; + /** * notice that we use ObjCls.prototype.someMethod = function() * notation - as we use Javascript's prototypal inheritance diff --git a/askbot/media/js/wmd/wmd.css b/askbot/media/js/wmd/wmd.css index 678d70f3..eeb6adec 100644 --- a/askbot/media/js/wmd/wmd.css +++ b/askbot/media/js/wmd/wmd.css @@ -3,46 +3,39 @@ background-color: White } */ -.wmd-panel -{ +.wmd-panel { } -#wmd-button-bar -{ +.wmd-button-bar { background: url(images/editor-toolbar-background.png) repeat-x bottom; - height: 30px; + height: 25px; border: 0; display: block; } -#wmd-input -{ +.wmd-input { height: 500px; background-color: Gainsboro; border: 1px solid DarkGray; margin-top: -20px; } -#wmd-preview -{ - background-color: LightSkyBlue; +.wmd-preview { + background-color: #f5f5f5; } -#wmd-output -{ +.wmd-output { background-color: Pink; } -#wmd-button-row -{ +.wmd-button-row { position: relative; - margin: 10px 2px 0 2px; + margin: 5px 2px; padding: 0px; height: 20px; } -.wmd-spacer -{ +.wmd-spacer { width: 1px; height: 20px; margin-left: 2px; @@ -53,8 +46,7 @@ list-style: none; } -.wmd-button -{ +.wmd-button { width: 20px; height: 20px; margin-left: 5px; @@ -68,8 +60,7 @@ list-style: none; } -.wmd-button > a -{ +.wmd-button > a { width: 20px; height: 20px; margin-left: 5px; @@ -81,23 +72,23 @@ /* sprite button slicing style information */ -#wmd-button-bar #wmd-bold-button {left: 0px; background-position: 0px 0;} -#wmd-button-bar #wmd-italic-button {left: 25px; background-position: -20px 0;} -#wmd-button-bar #wmd-spacer1 {left: 50px;} -#wmd-button-bar #wmd-link-button {left: 75px; background-position: -40px 0;} -#wmd-button-bar #wmd-quote-button {left: 100px; background-position: -60px 0;} -#wmd-button-bar #wmd-code-button {left: 125px; background-position: -80px 0;} -#wmd-button-bar #wmd-image-button {left: 150px; background-position: -100px 0;} -#wmd-button-bar #wmd-attachment-button {left: 175px; background-position: -120px 0;} -#wmd-button-bar #wmd-spacer2 {left: 200px;} -#wmd-button-bar #wmd-olist-button {left: 225px; background-position: -140px 0;} -#wmd-button-bar #wmd-ulist-button {left: 250px; background-position: -160px 0;} -#wmd-button-bar #wmd-heading-button {left: 275px; background-position: -180px 0;} -#wmd-button-bar #wmd-hr-button {left: 300px; background-position: -200px 0;} -#wmd-button-bar #wmd-spacer3 {left: 325px;} -#wmd-button-bar #wmd-undo-button {left: 350px; background-position: -220px 0;} -#wmd-button-bar #wmd-redo-button {left: 375px; background-position: -240px 0;} -#wmd-button-bar #wmd-help-button {right: 0px; background-position: -260px 0;} +.wmd-button-bar .wmd-bold-button {left: 0px; background-position: 0px 0;} +.wmd-button-bar .wmd-italic-button {left: 25px; background-position: -20px 0;} +.wmd-button-bar .wmd-spacer1 {left: 50px;} +.wmd-button-bar .wmd-link-button {left: 75px; background-position: -40px 0;} +.wmd-button-bar .wmd-quote-button {left: 100px; background-position: -60px 0;} +.wmd-button-bar .wmd-code-button {left: 125px; background-position: -80px 0;} +.wmd-button-bar .wmd-image-button {left: 150px; background-position: -100px 0;} +.wmd-button-bar .wmd-attachment-button {left: 175px; background-position: -120px 0;} +.wmd-button-bar .wmd-spacer2 {left: 200px;} +.wmd-button-bar .wmd-olist-button {left: 225px; background-position: -140px 0;} +.wmd-button-bar .wmd-ulist-button {left: 250px; background-position: -160px 0;} +.wmd-button-bar .wmd-heading-button {left: 275px; background-position: -180px 0;} +.wmd-button-bar .wmd-hr-button {left: 300px; background-position: -200px 0;} +.wmd-button-bar .wmd-spacer3 {left: 325px;} +.wmd-button-bar .wmd-undo-button {left: 350px; background-position: -220px 0;} +.wmd-button-bar .wmd-redo-button {left: 375px; background-position: -240px 0;} +.wmd-button-bar .wmd-help-button {right: 0px; background-position: -260px 0;} .wmd-prompt-background diff --git a/askbot/media/js/wmd/wmd.js b/askbot/media/js/wmd/wmd.js index ddbc91ed..b3ed3f38 100644 --- a/askbot/media/js/wmd/wmd.js +++ b/askbot/media/js/wmd/wmd.js @@ -79,10 +79,10 @@ Attacklab.wmdBase = function(){ // A collection of the important regions on the page. // Cached so we don't have to keep traversing the DOM. wmd.PanelCollection = function(){ - this.buttonBar = doc.getElementById("wmd-button-bar"); - this.preview = doc.getElementById("previewer"); - this.output = doc.getElementById("wmd-output"); - this.input = doc.getElementById("editor"); + this.buttonBar = doc.getElementById(util.makeId("wmd-button-bar")); + this.preview = doc.getElementById(util.makeId("previewer")); + this.output = doc.getElementById(util.makeId("wmd-output")); + this.input = doc.getElementById(util.makeId("editor")); }; // This PanelCollection object can't be filled until after the page @@ -195,7 +195,7 @@ Attacklab.wmdBase = function(){ var imgPath = imageDirectory + img; var elem = doc.createElement("img"); - elem.className = "wmd-button"; + elem.className = "wmd-button wmd-image-button"; elem.src = imgPath; return elem; @@ -276,24 +276,21 @@ util.prompt = function(text, defaultInputText, makeLinkMarkdown, dialogType){ // so we make the whole window transparent. // // Is this necessary on modern konqueror browsers? - if (global.isKonqueror){ + if (global.isKonqueror) { style.backgroundColor = "transparent"; - } - else if (global.isIE){ + } else if (global.isIE) { style.filter = "alpha(opacity=50)"; - } - else { + } else { style.opacity = "0.5"; } var pageSize = position.getPageSize(); style.height = pageSize[1] + "px"; - if(global.isIE){ + if(global.isIE) { style.left = doc.documentElement.scrollLeft; style.width = doc.documentElement.clientWidth; - } - else { + } else { style.left = "0"; style.width = "100%"; } @@ -333,7 +330,7 @@ util.prompt = function(text, defaultInputText, makeLinkMarkdown, dialogType){ // The input text box input = doc.createElement("input"); if(dialogType == 'image' || dialogType == 'file'){ - input.id = "image-url"; + input.id = util.makeId("image-url"); } input.type = "text"; if (dialogType == 'file'){ @@ -932,8 +929,8 @@ util.prompt = function(text, defaultInputText, makeLinkMarkdown, dialogType){ var setUndoRedoButtonStates = function(){ if(undoMgr){ - setupButton(document.getElementById("wmd-undo-button"), undoMgr.canUndo()); - setupButton(document.getElementById("wmd-redo-button"), undoMgr.canRedo()); + setupButton(document.getElementById(util.makeId("wmd-undo-button")), undoMgr.canUndo()); + setupButton(document.getElementById(util.makeId("wmd-redo-button")), undoMgr.canRedo()); } }; @@ -981,19 +978,21 @@ util.prompt = function(text, defaultInputText, makeLinkMarkdown, dialogType){ }; var makeSpritedButtonRow = function(){ - var buttonBar = document.getElementById("wmd-button-bar"); + var buttonBar = document.getElementById(util.makeId("wmd-button-bar")); + buttonBar.className = 'wmd-button-bar'; var normalYShift = "0px"; var disabledYShift = "-20px"; var highlightYShift = "-40px"; var buttonRow = document.createElement("ul"); - buttonRow.id = "wmd-button-row"; + buttonRow.className = 'wmd-button-row'; + buttonRow.id = util.makeId("wmd-button-row"); buttonRow = buttonBar.appendChild(buttonRow); if (isButtonUsed('bold')){ var boldButton = document.createElement("li"); - boldButton.className = "wmd-button"; - boldButton.id = "wmd-bold-button"; + boldButton.className = "wmd-button wmd-bold-button"; + boldButton.id = util.makeId("wmd-bold-button"); boldButton.title = toolbar_strong_label; boldButton.XShift = "0px"; boldButton.textOp = command.doBold; @@ -1003,8 +1002,8 @@ util.prompt = function(text, defaultInputText, makeLinkMarkdown, dialogType){ if (isButtonUsed('italic')){ var italicButton = document.createElement("li"); - italicButton.className = "wmd-button"; - italicButton.id = "wmd-italic-button"; + italicButton.className = "wmd-button wmd-italic-button"; + italicButton.id = util.makeId("wmd-italic-button"); italicButton.title = toolbar_emphasis_label; italicButton.XShift = "-20px"; italicButton.textOp = command.doItalic; @@ -1020,15 +1019,15 @@ util.prompt = function(text, defaultInputText, makeLinkMarkdown, dialogType){ isButtonUsed('attachment') ) { var spacer1 = document.createElement("li"); - spacer1.className = "wmd-spacer"; - spacer1.id = "wmd-spacer1"; + spacer1.className = "wmd-spacer wmd-spacer1"; + spacer1.id = util.makeId("wmd-spacer1"); buttonRow.appendChild(spacer1); } if (isButtonUsed('link')){ var linkButton = document.createElement("li"); - linkButton.className = "wmd-button"; - linkButton.id = "wmd-link-button"; + linkButton.className = "wmd-button wmd-link-button"; + linkButton.id = util.makeId("wmd-link-button"); linkButton.title = toolbar_hyperlink_label; linkButton.XShift = "-40px"; linkButton.textOp = function(chunk, postProcessing){ @@ -1040,8 +1039,8 @@ util.prompt = function(text, defaultInputText, makeLinkMarkdown, dialogType){ if (isButtonUsed('blockquote')){ var quoteButton = document.createElement("li"); - quoteButton.className = "wmd-button"; - quoteButton.id = "wmd-quote-button"; + quoteButton.className = "wmd-button wmd-quote-button"; + quoteButton.id = util.makeId("wmd-quote-button"); quoteButton.title = toolbar_blockquote_label; quoteButton.XShift = "-60px"; quoteButton.textOp = command.doBlockquote; @@ -1051,8 +1050,8 @@ util.prompt = function(text, defaultInputText, makeLinkMarkdown, dialogType){ if (isButtonUsed('code')){ var codeButton = document.createElement("li"); - codeButton.className = "wmd-button"; - codeButton.id = "wmd-code-button"; + codeButton.className = "wmd-button wmd-code-button"; + codeButton.id = util.makeId("wmd-code-button"); codeButton.title = toolbar_code_label; codeButton.XShift = "-80px"; codeButton.textOp = command.doCode; @@ -1062,8 +1061,8 @@ util.prompt = function(text, defaultInputText, makeLinkMarkdown, dialogType){ if (isButtonUsed('image')){ var imageButton = document.createElement("li"); - imageButton.className = "wmd-button"; - imageButton.id = "wmd-image-button"; + imageButton.className = "wmd-button wmd-image-button"; + imageButton.id = util.makeId("wmd-image-button"); imageButton.title = toolbar_image_label; imageButton.XShift = "-100px"; imageButton.textOp = function(chunk, postProcessing){ @@ -1075,8 +1074,8 @@ util.prompt = function(text, defaultInputText, makeLinkMarkdown, dialogType){ if (isButtonUsed('attachment')){ var attachmentButton = document.createElement("li"); - attachmentButton.className = "wmd-button"; - attachmentButton.id = "wmd-attachment-button"; + attachmentButton.className = "wmd-button wmd-attachment-button"; + attachmentButton.id = util.makeId("wmd-attachment-button"); attachmentButton.title = toolbar_attachment_label; attachmentButton.XShift = "-120px"; attachmentButton.textOp = function(chunk, postProcessing){ @@ -1093,15 +1092,15 @@ util.prompt = function(text, defaultInputText, makeLinkMarkdown, dialogType){ isButtonUsed('hr') ) { var spacer2 = document.createElement("li"); - spacer2.className = "wmd-spacer"; - spacer2.id = "wmd-spacer2"; + spacer2.className = "wmd-spacer wmd-spacer2"; + spacer2.id = util.makeId("wmd-spacer2"); buttonRow.appendChild(spacer2); } if (isButtonUsed('ol')) { var olistButton = document.createElement("li"); - olistButton.className = "wmd-button"; - olistButton.id = "wmd-olist-button"; + olistButton.className = "wmd-button wmd-olist-button"; + olistButton.id = util.makeId("wmd-olist-button"); olistButton.title = toolbar_numbered_label; olistButton.XShift = "-140px"; olistButton.textOp = function(chunk, postProcessing){ @@ -1113,8 +1112,8 @@ util.prompt = function(text, defaultInputText, makeLinkMarkdown, dialogType){ if (isButtonUsed('ul')) { var ulistButton = document.createElement("li"); - ulistButton.className = "wmd-button"; - ulistButton.id = "wmd-ulist-button"; + ulistButton.className = "wmd-button wmd-ulist-button"; + ulistButton.id = util.makeId("wmd-ulist-button"); ulistButton.title = toolbar_bulleted_label; ulistButton.XShift = "-160px"; ulistButton.textOp = function(chunk, postProcessing){ @@ -1126,8 +1125,8 @@ util.prompt = function(text, defaultInputText, makeLinkMarkdown, dialogType){ if (isButtonUsed('heading')) { var headingButton = document.createElement("li"); - headingButton.className = "wmd-button"; - headingButton.id = "wmd-heading-button"; + headingButton.className = "wmd-button wmd-heading-button"; + headingButton.id = util.makeId("wmd-heading-button"); headingButton.title = toolbar_heading_label; headingButton.XShift = "-180px"; headingButton.textOp = command.doHeading; @@ -1137,8 +1136,8 @@ util.prompt = function(text, defaultInputText, makeLinkMarkdown, dialogType){ if (isButtonUsed('hr')) { var hrButton = document.createElement("li"); - hrButton.className = "wmd-button"; - hrButton.id = "wmd-hr-button"; + hrButton.className = "wmd-button wmd-hr-button"; + hrButton.id = util.makeId("wmd-hr-button"); hrButton.title = toolbar_horizontal_label; hrButton.XShift = "-200px"; hrButton.textOp = command.doHorizontalRule; @@ -1148,13 +1147,13 @@ util.prompt = function(text, defaultInputText, makeLinkMarkdown, dialogType){ if (isButtonUsed('undo')){ var spacer3 = document.createElement("li"); - spacer3.className = "wmd-spacer"; - spacer3.id = "wmd-spacer3"; + spacer3.className = "wmd-spacer wmd-spacer3"; + spacer3.id = util.makeId("wmd-spacer3"); buttonRow.appendChild(spacer3); var undoButton = document.createElement("li"); - undoButton.className = "wmd-button"; - undoButton.id = "wmd-undo-button"; + undoButton.className = "wmd-button wmd-undo-button"; + undoButton.id = util.makeId("wmd-undo-button"); undoButton.title = toolbar_undo_label; undoButton.XShift = "-220px"; undoButton.execute = function(manager){ @@ -1164,8 +1163,8 @@ util.prompt = function(text, defaultInputText, makeLinkMarkdown, dialogType){ buttonRow.appendChild(undoButton); var redoButton = document.createElement("li"); - redoButton.className = "wmd-button"; - redoButton.id = "wmd-redo-button"; + redoButton.className = "wmd-button wmd-redo-button"; + redoButton.id = util.makeId("wmd-redo-button"); redoButton.title = toolbar_redo_label; if (/win/.test(nav.platform.toLowerCase())) { redoButton.title = toolbar_redo_label; @@ -1184,8 +1183,8 @@ util.prompt = function(text, defaultInputText, makeLinkMarkdown, dialogType){ } /* var helpButton = document.createElement("li"); - helpButton.className = "wmd-button"; - helpButton.id = "wmd-help-button"; + helpButton.className = "wmd-button wmd-help-button"; + helpButton.id = util.makeId("wmd-help-button"); helpButton.XShift = "-240px"; helpButton.isHelp = true; @@ -1239,44 +1238,44 @@ util.prompt = function(text, defaultInputText, makeLinkMarkdown, dialogType){ switch(keyCodeStr) { case "b": - doClick(document.getElementById("wmd-bold-button")); + doClick(document.getElementById(util.makeId("wmd-bold-button"))); break; case "i": - doClick(document.getElementById("wmd-italic-button")); + doClick(document.getElementById(util.makeId("wmd-italic-button"))); break; case "l": - doClick(document.getElementById("wmd-link-button")); + doClick(document.getElementById(util.makeId("wmd-link-button"))); break; case ".": - doClick(document.getElementById("wmd-quote-button")); + doClick(document.getElementById(util.makeId("wmd-quote-button"))); break; case "k": - doClick(document.getElementById("wmd-code-button")); + doClick(document.getElementById(util.makeId("wmd-code-button"))); break; case "g": - doClick(document.getElementById("wmd-image-button")); + doClick(document.getElementById(util.makeId("wmd-image-button"))); break; case "o": - doClick(document.getElementById("wmd-olist-button")); + doClick(document.getElementById(util.makeId("wmd-olist-button"))); break; case "u": - doClick(document.getElementById("wmd-ulist-button")); + doClick(document.getElementById(util.makeId("wmd-ulist-button"))); break; case "h": - doClick(document.getElementById("wmd-heading-button")); + doClick(document.getElementById(util.makeId("wmd-heading-button"))); break; case "r": - doClick(document.getElementById("wmd-hr-button")); + doClick(document.getElementById(util.makeId("wmd-hr-button"))); break; case "y": - doClick(document.getElementById("wmd-redo-button")); + doClick(document.getElementById(util.makeId("wmd-redo-button"))); break; case "z": if(key.shiftKey) { - doClick(document.getElementById("wmd-redo-button")); + doClick(document.getElementById(util.makeId("wmd-redo-button"))); } else { - doClick(document.getElementById("wmd-undo-button")); + doClick(document.getElementById(util.makeId("wmd-undo-button"))); } break; default: @@ -1877,8 +1876,17 @@ util.prompt = function(text, defaultInputText, makeLinkMarkdown, dialogType){ wmd.wmd.editor = wmd.editor; wmd.wmd.previewManager = wmd.previewManager; }; + + util.makeId = function(idToken) { + if (wmd.wmd_env['idSeed']) { + return askbotMakeId(idToken, wmd.wmd_env['idSeed']); + } + return idToken; + }; - util.startEditor = function(start_now, buttons){ + util.startEditor = function(start_now, buttons, idSeed){ + + wmd.wmd_env['idSeed'] = idSeed; if (wmd.wmd_env.autostart === false) { util.makeAPI(); @@ -2473,7 +2481,7 @@ Attacklab.account_options = {}; Attacklab.wmd_defaults = {version:1, output:"Markdown", lineLength:40, delayLoad:false}; //@todo: this needs to be moved out of the wmd.js as we have askbot-specific code here -if(!Attacklab.wmd && askbot['data']['editorType'] === 'markdown') { +if(!Attacklab.wmd && askbot['settings']['editorType'] === 'markdown') { Attacklab.wmd = function() { Attacklab.loadEnv = function() { var mergeEnv = function(env) { diff --git a/askbot/media/style/style.less b/askbot/media/style/style.less index 3c50090e..55b99ddb 100644 --- a/askbot/media/style/style.less +++ b/askbot/media/style/style.less @@ -2427,15 +2427,22 @@ ul#related-tags li { textarea { box-sizing: border-box; border: #cce6ec 3px solid; + color: #666; font-family: @body-font; font-size: 13px; height: 54px; line-height: 1.3; - margin: -1px 0 7px 1px; + margin: -1px 0 0 1px; outline: none; overflow:auto; - padding: 0px 19px 2px 3px; - width:100%; + padding: 5px 19px 2px 3px; + width: 99.6%; + } + .wmd-container textarea { + border: none; + } + .transient-comment { + margin-bottom: 3px; /* match paragraph style */ } input { margin-left: 10px; diff --git a/askbot/models/post.py b/askbot/models/post.py index e6c26154..10003e57 100644 --- a/askbot/models/post.py +++ b/askbot/models/post.py @@ -3,7 +3,6 @@ import datetime import operator import logging -from django.utils.html import strip_tags from django.contrib.sitemaps import ping_google from django.utils import html from django.conf import settings as django_settings @@ -421,26 +420,8 @@ class Post(models.Model): removed_mentions - list of mention objects - for removed ones """ - if self.post_type in ('question', 'answer', 'tag_wiki', 'reject_reason'): - _urlize = False - _use_markdown = (askbot_settings.EDITOR_TYPE == 'markdown') - elif self.is_comment(): - _urlize = True - _use_markdown = (askbot_settings.EDITOR_TYPE == 'markdown') - else: - raise NotImplementedError - - text = self.text - - if _urlize: - text = html.urlize(text) - - if _use_markdown: - text = sanitize_html(markup.get_parser().convert(text)) - - if askbot_settings.EDITOR_TYPE == 'tinymce': - #todo: see what can be done with the "object" tag - text = strip_tags(text, ['script', 'style', 'link']) + text_converter = self.get_text_converter() + text = text_converter(self.text) #todo, add markdown parser call conditional on #self.use_markdown flag @@ -614,6 +595,23 @@ class Post(models.Model): return answer + def get_text_converter(self): + have_simple_comment = ( + self.is_comment() and + askbot_settings.COMMENTS_EDITOR_TYPE == 'plain-text' + ) + if have_simple_comment: + parser_type = 'plain-text' + else: + parser_type = askbot_settings.EDITOR_TYPE + + if parser_type == 'plain-text': + return markup.plain_text_input_converter + elif parser_type == 'markdown': + return markup.markdown_input_converter + elif parser_type == 'tynymce': + return markup.tinymce_input_converter + def has_group(self, group): """true if post belongs to the group""" return self.groups.filter(id=group.id).exists() diff --git a/askbot/templates/meta/bottom_scripts.html b/askbot/templates/meta/bottom_scripts.html index 78e23d89..e7ccb822 100644 --- a/askbot/templates/meta/bottom_scripts.html +++ b/askbot/templates/meta/bottom_scripts.html @@ -30,6 +30,16 @@ askbot['settings']['minSearchWordLength'] = {{ min_search_word_length }}; askbot['settings']['mathjaxEnabled'] = {{ settings.ENABLE_MATHJAX|as_js_bool }}; askbot['settings']['sharingSuffixText'] = '{{ settings.SHARING_SUFFIX_TEXT|escape }}'; + askbot['data']['maxCommentLength'] = {{ settings.MAX_COMMENT_LENGTH }}; + askbot['settings']['editorType'] = '{{ settings.EDITOR_TYPE }}'; + askbot['settings']['commentsEditorType'] = '{{ settings.COMMENTS_EDITOR_TYPE }}'; + {% if settings.ALLOWED_UPLOAD_FILE_TYPES %} + askbot['settings']['allowedUploadFileTypes'] = [ + "{{ settings.ALLOWED_UPLOAD_FILE_TYPES|join('", "')|replace('.','') }}" + ]; + {% else %} + askbot['settings']['allowedUploadFileTypes'] = []; + {% endif %} askbot['data']['haveFlashNotifications'] = {{ user_messages|as_js_bool }}; askbot['data']['activeTab'] = '{{ active_tab }}'; {% if search_state %} diff --git a/askbot/templates/meta/html_head_javascript.html b/askbot/templates/meta/html_head_javascript.html index 07a39f80..965dd350 100644 --- a/askbot/templates/meta/html_head_javascript.html +++ b/askbot/templates/meta/html_head_javascript.html @@ -13,17 +13,8 @@ {% else %} askbot['data']['userReputation'] = 0; {% endif %} - askbot['data']['maxCommentLength'] = {{ settings.MAX_COMMENT_LENGTH }}; askbot['urls'] = {}; askbot['settings'] = {}; - askbot['settings']['editorType'] = '{{ settings.EDITOR_TYPE }}'; - {% if settings.ALLOWED_UPLOAD_FILE_TYPES %} - askbot['settings']['allowedUploadFileTypes'] = [ - "{{ settings.ALLOWED_UPLOAD_FILE_TYPES|join('", "')|replace('.','') }}" - ]; - {% else %} - askbot['settings']['allowedUploadFileTypes'] = []; - {% endif %} askbot['messages'] = {}; diff --git a/askbot/utils/markup.py b/askbot/utils/markup.py index ac96ec74..33d0522e 100644 --- a/askbot/utils/markup.py +++ b/askbot/utils/markup.py @@ -7,6 +7,8 @@ import re import logging from askbot import const from askbot.conf import settings as askbot_settings +from askbot.utils.html import sanitize_html, strip_tags +from django.utils.html import urlize from markdown2 import Markdown #url taken from http://regexlib.com/REDetails.aspx?regexp_id=501 by Brian Bothwell URL_RE = re.compile("((?' + text + '

')) + +def markdown_input_converter(text): + """markdown to html converter""" + text = urlize(text) + text = get_parser().convert(text) + return sanitize_html(text) + +def tinymce_input_parser(text): + """tinymce input to production html converter""" + text = urlize(text) + return strip_tags(text, ['script', 'style', 'link']) diff --git a/askbot/views/writers.py b/askbot/views/writers.py index deec46cc..e8d50cea 100644 --- a/askbot/views/writers.py +++ b/askbot/views/writers.py @@ -62,9 +62,6 @@ ANSWERS_PAGE_SIZE = 10 def upload(request):#ajax upload file to a question or answer """view that handles file upload via Ajax """ - import pdb - pdb.set_trace() - # check upload permission result = '' error = '' @@ -744,7 +741,6 @@ def post_comments(request):#generic ajax handler to load comments to an object @decorators.ajax_only #@decorators.check_spam('comment') def edit_comment(request): - if request.user.is_anonymous(): raise exceptions.PermissionDenied(_('Sorry, anonymous users cannot edit comments')) -- cgit v1.2.3-1-g7c22