Changeset 586992
- Timestamp:
- 08/17/2012 08:48:28 PM (14 years ago)
- Location:
- preserved-html-editor-markup/trunk
- Files:
-
- 3 edited
-
admin.js (modified) (3 diffs)
-
readme.txt (modified) (4 diffs)
-
sb_preserved_markup.php (modified) (11 diffs)
Legend:
- Unmodified
- Added
- Removed
-
preserved-html-editor-markup/trunk/admin.js
r456532 r586992 1 1 (function($) { 2 3 //Copied from tinymce source because it was privately scoped 4 function cloneFormats(node) { 5 var clone, temp, inner; 6 7 do { 8 if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(node.nodeName)) { 9 if (clone) { 10 temp = node.cloneNode(false); 11 temp.appendChild(clone); 12 clone = temp; 13 } else { 14 clone = inner = node.cloneNode(false); 15 } 16 17 clone.removeAttribute('id'); 18 } 19 } while (node = node.parentNode); 20 21 if (clone) 22 return {wrapper : clone, inner : inner}; 23 }; 24 25 26 window.emc2_tinymce_init = function(ed) { 27 28 ed.onNodeChange.add(function(ed, cm, n, co, ob) { 29 //This fixes a bug in the Format dropdown 30 //now when the cursor lands on an unwrapped piece 31 function getParent(name) { 32 //copied from tinymce source because it was privately scoped 33 var i, parents = ob.parents, func = name; 34 35 if (typeof(name) == 'string') { 36 func = function(node) { 37 return node.nodeName == name; 38 }; 39 } 40 41 for (i = 0; i < parents.length; i++) { 42 if (func(parents[i])) 43 return parents[i]; 44 } 45 }; 46 47 if (c = cm.get('formatselect')) { 48 p = getParent(tinymce.DOM.isBlock); 49 if (p) { 50 c.select(p.nodeName.toLowerCase()); 51 } 52 else { 53 c.select(''); //BUGFIX: select the 'format' element 54 } 55 } 56 }, ed); 57 58 if (ed.settings.force_p_newlines == false) { 59 //When force_p_newlines is disabled we recreate the functionality from tinymce here to allow for br only inserts. 60 var prev_key = false; 61 62 ed.onKeyDown.add(function(ed, e) { 63 //keyPress doesn't capture backspace so use keydown isntead 64 if (e.keyCode != 13 || e.shiftKey) prev_key = false; 65 66 if (tinymce.isGecko) { 67 if ((e.keyCode == 8 || e.keyCode == 46) && !e.shiftKey) 68 ed.forceBlocks.backspaceDelete(e, e.keyCode == 8); 69 } 70 }); 71 72 if (tinymce.isIE) { 73 tinymce.addUnload(function() { 74 ed.forceBlocks._previousFormats = 0; // Fix IE leak 75 }); 76 } 77 78 ed.onKeyPress.add(function(ed, e) { 79 //Bug fix, since we removed the p tag 80 if (e.keyCode == 13 && !e.shiftKey) { 81 if (prev_key && ed.settings.force_hybrid_newlines == true) { 82 //double return 83 //when a user enters two consecutive newlines in the wysiwyg editor, inject a new paragraph tag instead of the br tags* 84 //*Except in firefox where that functionality could not be implemented in a timely manner due to some browser bugs 85 if (!tinymce.isIE && !tinymce.isGecko) { 86 if (!ed.forceBlocks.insertPara(e)) { 87 tinymce.dom.Event.cancel(e); 88 } 89 } 90 else if (tinymce.isIE) { 91 ed.forceBlocks._previousFormats = 0; 92 93 // Clone the current formats, this will later be applied to the new block contents 94 if (ed.selection.isCollapsed() && ed.settings.keep_styles) { 95 ed.forceBlocks._previousFormats = cloneFormats(ed.selection.getStart()); 96 } 97 98 //remove the last br tag that was inserted by the first enter key press 99 var sel = ed.selection.getStart(); 100 sel.innerHTML = sel.innerHTML.replace(/<br\/?>([^<br\/?>]*)$/i, ''); 101 } 102 103 prev_key = false; 104 } 105 else { 106 prev_key = true; 107 108 if ((tinymce.isIE || tinymce.isGecko) && e.keyCode == 13 && ed.selection.getNode().nodeName != 'LI') { 109 ed.selection.setContent('<br id="__" /> ', {format : 'raw'}); 110 var n = ed.dom.get('__'); 111 n.removeAttribute('id'); 112 ed.selection.select(n); 113 ed.selection.collapse(); 114 return tinymce.dom.Event.cancel(e); 115 } 116 117 if (tinymce.isWebKit) { 118 119 function insertBr(ed) { 120 var rng = ed.selection.getRng(), br, div = ed.dom.create('div', null, ' '), divYPos, vpHeight = ed.dom.getViewPort(ed.getWin()).h; 121 122 // Insert BR element 123 rng.insertNode(br = ed.dom.create('br')); 124 125 // Place caret after BR 126 rng.setStartAfter(br); 127 rng.setEndAfter(br); 128 ed.selection.setRng(rng); 129 130 // Could not place caret after BR then insert an nbsp entity and move the caret 131 if (ed.selection.getSel().focusNode == br.previousSibling) { 132 ed.selection.select(ed.dom.insertAfter(ed.dom.doc.createTextNode('\u00a0'), br)); 133 ed.selection.collapse(true); 134 } 135 136 // Create a temporary DIV after the BR and get the position as it 137 // seems like getPos() returns 0 for text nodes and BR elements. 138 ed.dom.insertAfter(div, br); 139 divYPos = ed.dom.getPos(div).y; 140 ed.dom.remove(div); 141 142 // Scroll to new position, scrollIntoView can't be used due to bug: http://bugs.webkit.org/show_bug.cgi?id=16117 143 if (divYPos > vpHeight) // It is not necessary to scroll if the DIV is inside the view port. 144 ed.getWin().scrollTo(0, divYPos); 145 } 146 147 if (e.keyCode == 13 && !ed.dom.getParent(ed.selection.getNode(), 'h1,h2,h3,h4,h5,h6,ol,ul')) { 148 insertBr(ed); 149 tinymce.dom.Event.cancel(e); 150 } 151 } 152 } 153 } 154 }); 155 156 if (tinymce.isIE) { 157 ed.onKeyUp.add(function(ed, e) { 158 // Let IE break the element and the wrap the new caret location in the previous formats 159 if (e.keyCode == 13 && !e.shiftKey) { 160 var parent = ed.selection.getStart(), fmt = ed.forceBlocks._previousFormats; 161 162 // Parent is an empty block 163 if (!parent.hasChildNodes() && fmt) { 164 parent = ed.dom.getParent(parent, ed.dom.isBlock); 165 166 if (parent && parent.nodeName != 'LI') { 167 parent.innerHTML = ''; 168 169 if (ed.forceBlocks._previousFormats) { 170 parent.appendChild(fmt.wrapper); 171 fmt.inner.innerHTML = '\uFEFF'; 172 } else { 173 parent.innerHTML = '\uFEFF'; 174 } 175 176 selection.select(parent, 1); 177 selection.collapse(true); 178 ed.getDoc().execCommand('Delete', false, null); 179 ed.forceBlocks._previousFormats = 0; 180 } 181 } 182 } 183 }); 184 } 185 } 186 }; 187 188 window.emc2pm_fix_content = function(post_type) { 189 //used on the writting settings page for fixing legacy content 190 if (confirm('Are you sure? This process is not reversable')) { 191 $.get( 192 ajaxurl, 193 { 194 nonce : emc2pm.fix_content_nonce, 195 action : 'emc2pm_fix_posts', 196 post_type : post_type 197 }, 198 function(r) { 199 alert(r); 200 } 201 ); 202 } 203 return false; 204 }; 205 2 206 //on dom load 3 207 $(function() { 4 208 //first_switch_html & orig_title are used in the bugfix in the afterPreWpautop override below 209 var first_switch_html = true; 210 var orig_title = $('#post #title').val(); 211 5 212 //disable evil... yeah that's right I said EVIL! 6 213 if (window.switchEditors) { … … 16 223 17 224 $('body').bind('afterPreWpautop', function(e, o) { 18 //On Switch to HTML 19 225 //On Switch to HTML & On save/update from Visual tab 226 20 227 //Now we replace all those temporary html comments with spaces and newlines 21 228 o.data = o.unfiltered.replace(/<\!--mep-nl-->/g, "\r\n").replace(/<\!--mep-tab-->/g, " "); 229 230 //And finally remove the code tag hack from the markup 231 o.data = o.data.replace(/<code style=['"]display: none;['"]><!--[\s\S]*?--><\/code>/g, function(s) { 232 return s.substring(s.indexOf('<!--'), s.indexOf('-->') + 3); 233 }); 234 235 if (first_switch_html) { 236 first_switch_html = false; 237 /*BUGFIX: in autosave.js, on document load, the textarea value is cached in a js var 'autosaveLast' and 238 is used to determine if changes were made in onbeforeunload. The content is correct when we start in 239 HTML mode. But when we load the page in Visual mode it's "mep" encoded (properly so to render correctly.) 240 So loading in Visual mode and switching to HTML mode will result in comparing the "mep" encoded version 241 with the proper clean HTML version. So on the very first transition we update the "autosaveLast" var 242 value to the clean HTML content. It's incredibly fortunate that not only is this variable publicly scoped 243 against JS best practices, but also that in Visual mode tinymce uses a different variable to check for 244 changes. So adjusting autosaveLast will not affect the onbeforeunload event in Visual mode. 245 */ 246 247 //only adjust the html if the visual tab isn't "dirty" because then it doesn't matter either way 248 //but if they made changes on the visual tab a tab switch would use those updates and the editor 249 //wouldn't alert the user to any changes on refresh. 250 if (tinyMCE && tinyMCE.activeEditor && tinyMCE.activeEditor.isDirty() == false) { 251 autosaveLast = orig_title + o.data.replace(/\r/g, ''); //seems \r's are stripped out of textarea#content 252 //maybe I shouldn't be including them, thought they 253 //were necessary for windows platforms 254 } 255 } 22 256 }).bind('afterWpautop', function(e, o) { 23 257 //On Switch to Visual 24 25 //first preserve newlines and whitespace in pre & code tags because the browser will not mess with those 258 first_switch_html = false; //this var is only important if we start on the visual tab 259 //TODO: but a bug exists in wordpress that could be fixed here. Basically if you 260 //load with HTML tab active, make a change, switch to Visual, and click Refresh 261 //you won't be prompted to save changes because the Visual tab sets originalContent 262 //to the current html value on tab switch. :( So we could overwrite it with the 263 //true originalContent here on first switch. 264 265 //first: hack fix for preserving multi-line html comments 266 o.unfiltered = o.unfiltered.replace(/<!--[\s\S]*?-->/g, function(s) { 267 return "<code style='display: none;'>" + s + "</code>"; 268 }); 269 270 //next: preserve newlines and whitespace in pre & code tags because the browser will not mess with those 26 271 if ( o.unfiltered.indexOf('<pre') != -1 || o.unfiltered.indexOf('<code') != -1 ) { 27 272 o.unfiltered = o.unfiltered.replace(/<(pre|code)[^>]*>[\s\S]+?<\/\1>/g, function(s) { … … 30 275 } 31 276 32 // replace any newline characters with a custom mep html comment as a marker for where277 //now: replace any newline characters with a custom mep html comment as a marker for where 33 278 //newline chars should be added back in when we're done 34 279 o.data = o.unfiltered.replace(/(\r\n|\n)/g, "<!--mep-nl-->").replace(/(\t|\s\s\s\s)/g, "<!--mep-tab-->"); 35 280 36 // andrestore the whitespace back in pre & code tags281 //finally: restore the whitespace back in pre & code tags 37 282 o.data = o.data.replace(/<mep-preserve-nl>/g, "\n").replace(/<mep-preserve-tab>/g, "\t").replace(/<mep-preserve-space>/g, " "); 38 283 }); -
preserved-html-editor-markup/trunk/readme.txt
r584935 r586992 4 4 Tags: wpautop, editor, markup, html, white space, HTML5, WYSIWYG, visual, developer 5 5 Requires at least: 3.2.1 6 Tested up to: 3. 2.16 Tested up to: 3.4 7 7 Stable tag: trunk 8 8 … … 15 15 It also supports HTML5 Block Anchor tags, something that is currently not supported in WP even via any existing plugins. 16 16 17 There are a couple caveats to editing in this mode, but most of them are understood by developers and are even repeatedly asked for in forums and on Wordpress dev lists. They are as follows: 17 With version 1.2, you now have a little more control over how content is created. And most of the previous caveats to using this plugin are now resolved. 18 18 19 1. wpautop is disabled. And I mean really disabled, not just disabled when editing in HTML, or when viewing the content, but the TinyMCE editor will never wrap your content in a p tag again (unless you tell it to of course.) 19 1. You can now choose whether to use BR tags OR P tags for newlines. Even better you can use both, where one return key press injects a BR tag, and two return key presses will wrap a Paragraph tag. This is great for being able to wrap headers at specific break points all while enjoying the semantic perks of paragraphs. 20 21 1. In addition to choosing what type of tags to use, you can also change the behavior depending on the type of post, including custom post types. So Pages can default to BR tags, and Blog Posts can default to Paragraph tags. 22 23 1. If you have existing content that was created before activating this plugin, you can now use the Fixit feature to convert your existing content in a way that makes it render the same as before. Only use this feature (located under Admin > Settings > Writing: Fixing Existing Content) if you are installing this plugin for the first time, otherwise it will remove all of the formatted white space in your posts. 24 25 1. Multi-line HTML comments are now supported (Thanks to [@cwlee_klagroup](http://wordpress.org/support/profile/cwlee_klagroup) for suggesting the working fix!) 26 27 1. The Format drop down in the TinyMCE editor had a bug which is now fixed. It will now select "Format" if you place the cursor on a section of bare text. Currently the editor just leaves the previously selected format option in place. It's minor but it's good to know when you have bare text in your content. 28 29 1. There was a fairly problematic bug in the old version where in some browsers you couldn't change the formatting of a single line in the Visual editor if you started from scratch. Choosing a different Format option would change the entire document, with the only work around being to edit the document in HTML mode. That was bad, and somehow went unnoticed for far too long. Anyway, that is fixed now. 20 30 21 1. In Visual mode, the "enter key" will inject BR tags instead of newlines in the HTML source. It will still appear correct in the WYSIWYG result. 31 The caveats that still remains are: 22 32 23 1. Orphaned text content will not be wrapped in any tag - so you will want to make sure your theme inherently wraps the post content in a div tag (or some other block element) for valid HTML markup. 33 1. If you use the Paragraph tag setting for newlines there is a minor bug where it will only wrap your content in Paragraph tags if you specify Paragraph in the Format drop down or if you enter more than one paragraph of text. So if you just type one sentence and click save it will not wrap the content in Paragraph tags. I tried to fix this but ran out of my allotted time working on other core issues. Should be fixed in the next release. 34 35 1. For performance reasons, it will only preserve spaces if 4 spaces are used consecutively - i.e. an expanded tab in developer terms. It will not preserve intra-tag white space like <p >. 24 36 25 1. White space is preserved; newlines, spaces and tabs will remain in your markup for the most part. For performance reasons, it will only preserve spaces if 4 spaces are used consecutively - i.e. an expanded tab in developer terms. It will not preserve intra-tag white space like <p >. 26 27 There are a couple of bugs in the parsing logic that I'm aware of and I will work to iron them out over the coming weeks. 28 29 Known issues: 30 31 1. Multi-line comment blocks, and comments with 4 or more consecutive spaces are not supported at the moment. Because my plugin relies on embedding html comments into the dom, any html comment with a new-line or tab will be embedded with a nested html comment which is not supported by the html spec. The only solution I can suggest is to rely on revision history and simply remove whatever content you were intending to hide. 32 33 1. If you do add 4 or more consecutive spaces inside of an element tag it will corrupt the markup and mangle the output. But as this is intended for developer edits, this should be an extreme rarity given the habit is virtually non-existent in development communities. 34 35 1. If you use two or three spaces on a line they will be reduced to one. 37 1. If you do add 4 or more spaces inside of an element tag it will corrupt the markup and mangle the output. But as this is intended for developer edits, this should be an extreme rarity given the habit is virtually non-existent in development communities. 36 38 37 39 1. PRE tags are not affected and behave as you would expect, however due to how browsers parse tags, the first newline in the content of a PRE tag will be wiped out unless it is padded with either another new line or multiple spaces. 38 40 39 1. CODE tags are not preserving white space at all, and when wrapped around PRE tags they are being removed entirely. When PRE tags are wrapped around CODE tags they are not removed, however white space isremoved. I'm working to resolve this problem.41 1. CODE tags are not preserving white space at all, and when wrapped with PRE tags white space is still removed. I'm working to resolve this problem. 40 42 41 43 == Installation == … … 43 45 1. Upload the plugin contents to the `/wp-content/plugins/` directory 44 46 1. Activate the plugin through the 'Plugins' menu in Wordpress Admin 47 1. If you have existing content that needs fixing, use the "Fix Posts" feature under Admin > Settings > Writing: Fix Existing Content. 45 48 1. You're done! 46 49 47 50 == Frequently Asked Questions == 48 51 49 = Wh y doesn't the visual editor wrap my text content in paragraph <p> tags? =52 = When will code tag issues be resolve? = 50 53 51 This is a side effect of disabling the poorly designed code formatter that is built into Wordpress. However, it should be possible to add this feature at some point down the road. 54 This is a tough one. Not only do I have no idea why they're being trumped, but I also have a daughter that will be born pretty soon :D, and a project at work that is about to get hectic :( I'll try to fix it when I can but if you have the skills to help debug the help would be greatly appreciated. 55 56 = What exactly do the "Fix Posts" or "Fix XXX" buttons do to my content? = 57 58 Firstly, only use this feature if you are starting new with version 1.2. And definitely backup your database before running these tools, they have only been tested on two sites so far. And although in theory it is safe, you should still protect yourself. 59 60 The fix actually just runs wpautop one final time on the posts in the database. By default WordPress runs that function every time it displays content, so the raw data in the database is free of any paragraph tags & other formatting tweaks. The Fix buttons update the raw content in the database with the formatted version wpautop produces. And fortunately wpautop was designed in a way that it can be run multiple times so it shouldn't mangle your content. 61 62 All of your post content will be converted, including past revisions. So if you need to revert a page or post after you activate this feature, you won't have to reformat the previous version by hand. 63 64 The plugin also keeps track of when it was activated, so it will only modify content that was edited before the plugin was activated. So if you created some new content after activating the plugin and later realized all of your other content wasn't displaying correctly it's safe to use the Fix buttons without ruining your new content. 52 65 53 66 == Upgrade Notice == 54 67 55 1. No upgrade notices 68 If you used version 1.0 or 1.1 to create content, do not use the Fix it features unless you are ok with losing the white space preservation of those posts. 56 69 57 70 == Screenshots == … … 61 74 == Changelog == 62 75 76 = 1.2 = 77 * Added support for user-specified newline behavior per post type 78 * Added support for multi-line html comments (Thanks cwlee_klagroup!) 79 * Fixed a bug found in TinyMCE related to Format drop down 80 * Added tools to convert existing site content programmatically by post type. 63 81 = 1.1 = 64 82 * Refactored for support of < php5.3 by replacing function references with static function array refs -
preserved-html-editor-markup/trunk/sb_preserved_markup.php
r584935 r586992 8 8 Author: Marcus E. Pope, marcuspope 9 9 Author URI: http://www.marcuspope.com 10 Version: 1. 010 Version: 1.2 11 11 12 12 Copyright 2011 Marcus E. Pope (email : me@marcuspope.com) … … 29 29 add_action('plugins_loaded', array('MP_WP_Preserved_Markup', 'init'), 1); 30 30 31 register_activation_hook( __FILE__, array('MP_WP_Preserved_Markup', 'set_activation_time')); 32 33 if (is_admin()) { 34 add_action('wp_ajax_emc2pm_fix_posts', array('MP_WP_Preserved_Markup', 'fix_database_content')); 35 } 36 31 37 class MP_WP_Preserved_Markup { 32 38 39 private static $valid_types = array(); 40 33 41 public static function remove_evil() { 34 42 //remove evil: wpautop will break html5 markup! 35 43 remove_filter('the_content', 'wpautop'); 44 45 //massage evil-ish wp-texturize 46 if (has_filter('the_content', 'wptexturize')) { 47 remove_filter('the_content', 'wptexturize'); 48 add_filter('the_content', array('MP_WP_Preserved_Markup', 'better_wptexturize')); 49 } 50 } 51 52 public static function better_wptexturize($s) { 53 //this is probably the first of many tweaks we'll fix in texturize, 54 //but for now it's just targeting html comment tags 55 56 //replace all closing html tags with temp placeholders so texturize doesn't turn them into endash's 57 $s = preg_replace("/-->/m", "-mep-temp-closing-comment-tag->", $s); 58 59 //now let wptexturize do its work 60 $s = wptexturize($s); 61 62 //and restore original values in our temp placeholders 63 $s = preg_replace("/-mep-temp-closing-comment-tag->/m", "-->", $s); 64 65 return $s; 36 66 } 37 67 38 68 public static function init_tiny_mce($init) { 69 70 $post_type = self::get_cur_post_type(); 71 $insert_p = 'br'; 72 73 if (!empty($post_type)) { 74 //check post type page width specified by user setting 75 $options = get_option('emc2_editor_insert_p'); 76 //default to 'br' for backwards compatibility/forwards consistency 77 $insert_p = isset($options[$post_type]) ? $options[$post_type] : "br"; 78 } 79 39 80 //Setup tinymce editor with necessary settings for better general editing 40 81 $tags = "pre[*],iframe[*],object[*],param[*]"; //add pre and iframe to allowable tags for … … 46 87 $init['force_p_newlines'] = false; 47 88 $init['remove_linebreaks'] = false; 48 $init['force_br_newlines'] = true; 89 $init['force_br_newlines'] = false; 90 91 if ($insert_p == "br") { 92 //default behavior for this plugin 93 } 94 else if ($insert_p == "p") { 95 $init['force_p_newlines'] = true; 96 } 97 else if ($insert_p == "both") { 98 //insert p tag after two consecutive new lines 99 $init['force_hybrid_newlines'] = true; 100 } 101 49 102 $init['remove_trailing_nbsp'] = false; 50 103 $init['relative_urls'] = true; … … 57 110 $init['fix_table_elements'] = false; 58 111 $init['verify_html'] = false; 112 $init['setup'] = 'emc2_tinymce_init'; 59 113 60 114 /* … … 70 124 71 125 public static function fix_editor_content($html) { 126 //this filter is added dynamically by 'the_editor' event, but it will wreck our hidden markup 72 127 remove_filter('the_editor_content', "wp_richedit_pre"); 73 128 return $html; … … 81 136 return $s; 82 137 } 138 139 public static function comment_hack_callback($a) { 140 return "<code style='display: none;'>" . $a[0] . "</code>"; 141 } 142 143 public static function comment_unhack_callback($a) { 144 $s = $a[0]; 145 $start = strpos($s, '<!--'); 146 $stop = strrpos($s, '-->') + 3; 147 $s = substr($s, $start, $start - $stop); 148 return $s; 149 } 83 150 84 151 public static function fix_wysiwyg_content($c) { 85 152 //If the page is rendered with the WYSIWYG editor selected by default, content is processed in PHP land 86 153 //instead of using the JS land "equivalent" logic (I quote equivalent because there are sooooo many 87 //discrepancies between what JS wpautop and PHP wpautop functions do it's laughable. 154 //discrepancies between what JS wpautop and PHP wpautop functions do it's laughable.) 88 155 if (wp_default_editor() == "tinymce") { 156 157 //Our whitespace preservation logic breaks existing multiline html comments. 158 //By wrapping them in hidden code blocks, we can preserve whitespace and hide the rendered content 159 $c = preg_replace_callback( 160 '/<!--[\s\S]*-->/m', 161 array( 162 'MP_WP_Preserved_Markup', 163 'comment_hack_callback' 164 ), 165 $c); 166 89 167 //First we inject temporary whitespace markers in pre and code elements because they won't 90 168 //be corrupted when the user switches to html mode.* (actually IE9 will remove the first … … 113 191 $c = convert_chars($c); 114 192 $c = htmlspecialchars($c, ENT_NOQUOTES); 193 $c = apply_filters('richedit_pre', $c); 115 194 } 116 195 … … 121 200 //If the user clicks save while in the Visual (WYSIWYG) tab, we'll need to strip the whitespace placeholders 122 201 //before inserting the data into the database to prevent duplication of whitespace 123 //WTF: ok so I ran into a problem of duplicating newlines when saving from the Visual tab, so I added this 124 // code to strip what I thought was my whitespace markers not being converted back in JS land before being 125 // sent to the server. (in the same way that if you load the page on the Visual tab, the parsing logic 126 // occurs on the server instead of the client!) But after add this code I couldn't reproduce the issue 127 // the post_content never contained any mep-xxx comments. I'm leaving this code in here, because it's 128 // the probable explanation for duplicated newlines, but I'm also not sure how the Visual content is 129 // transfered from the iframe to the textarea. 202 203 //INFO: This should not be necessary because when the user clicks save from the Visual Tab the content is passed 204 //through the afterPreWpautop javascript event which we already use to handle tab switching. I think my previous 205 //issue was caused by a js error in that function that resulted in nothing being stripped out before it was 206 //posted to the server here: 130 207 if (isset($post['post_content'])) { 131 208 $post['post_content'] = preg_replace('/<\!--mep-nl-->/m', "\r\n", $post['post_content']); 132 209 $post['post_content'] = preg_replace('/<\!--mep-tab-->/m', " ", $post['post_content']); 210 $post['post_content'] = preg_replace_callback( 211 '/<code style=[\'"]display: none;[\'"]><!--[\s\S]*?--><\/code>/m', 212 array( 213 'MP_WP_Preserved_Markup', 214 'comment_unhack_callback' 215 ), 216 $post['post_content'] 217 ); 133 218 } 134 219 return $post; 220 } 221 222 public static function set_activation_time() { 223 update_option('emc2pm_activate_date', time()); 224 } 225 226 public static function fix_database_content() { 227 /* 228 Iterate over every post in the database by specified post_type and 229 update the content with wpautop. This is essentially what WordPress 230 did each time it rendered content from the database. Since we're 231 leaving the content alone from now on, this gives the user the ability 232 to fix previously created content. 233 */ 234 global $wpdb; 235 236 //verify nonce and validate post type value 237 if (wp_verify_nonce(@$_GET['nonce'], "emc2pm_fix_content") && 238 post_type_exists(@$_GET['post_type'])) { 239 240 //get everything, revisions and all. I hesitated on this, but thought if a week later a user reverts a 241 //post to a previous revision, they shouldn't have to re-fix the post by hand. And since the modified date 242 //would be after the plugin activation date, the 'Fix XXX' feature wouldn't automatically fix it. 243 $posts = $wpdb->get_results($wpdb->prepare(" 244 SELECT ID, post_content, post_title, post_modified FROM $wpdb->posts 245 WHERE post_type = %s", $_GET['post_type'])); 246 247 $errors = array(); 248 249 $limit = get_option('emc2pm_activate_date'); 250 251 foreach ($posts as $post) { 252 //Don't clobber whitespace in any content created after the plugin was activated. 253 //ISSUE: if the user activated the plugin, then modified content, then disabled the plugin and re-enabled it 254 //we would accidently unformat some whitespace... meh, there are worse problems in the world. 255 $modified = strtotime($post->post_modified); 256 if (strlen($modified) != 0 && //don't think this is possible, but just in case 257 $modified > $limit) { 258 continue; 259 } 260 261 $new_content = wpautop($post->post_content); 262 263 if ($new_content != "") { 264 265 $ob = ob_start(); //capture html db errors for cleaner alert 266 $wpdb->query( $wpdb->prepare(" 267 UPDATE $wpdb->posts SET post_content = %s 268 WHERE ID = %d 269 ", $new_content, $post->ID) ); 270 $res = ob_get_clean(); 271 272 if (strstr($res, "error")) { 273 $errors[] = $post->post_title; 274 } 275 } 276 } 277 278 //present the processing results to user 279 $res = "Content Fixed"; 280 if (count($errors)) { 281 $res .= "\n\nBut the following posts could not be updated:\n"; 282 $res .= join("\n", $errors); 283 } 284 285 die($res); 286 } 287 else { 288 die("ERROR: Access Denied"); 289 } 135 290 } 136 291 … … 146 301 // Create a filter for overriding so I don't have to copy this plugin from 147 302 // the svn repo to my own hg/git repos 148 wp_enqueue_script('admin-js', WP_PLUGIN_URL.'/'.str_replace(basename( __FILE__),"",plugin_basename(__FILE__))."admin.js"); 149 //wp_enqueue_script('admin-js', WP_PLUGIN_URL.'/sb_preserved_markup/admin.js'); 303 wp_enqueue_script('emc2-pm-admin-js', WP_PLUGIN_URL.'/'.str_replace(basename( __FILE__),"",plugin_basename(__FILE__))."admin.js"); 304 //wp_enqueue_script('emc2-pm-admin-js', WP_PLUGIN_URL.'/sb_preserved_markup/admin.js'); 305 306 //provide nonce for ajax calls 307 wp_localize_script('emc2-pm-admin-js', 'emc2pm', array( 308 'fix_content_nonce' => wp_create_nonce('emc2pm_fix_content') 309 )); 150 310 151 311 add_filter('the_editor', array( … … 163 323 'fix_post_content' 164 324 ), 1); 325 326 self::admin_settings_init(); 165 327 } 166 328 … … 176 338 )); 177 339 } 340 341 /* 342 * The following set of functions mostly apply to the settings page under Admin > Settings > Writing 343 */ 344 static function get_cur_post_type() { 345 global $pagenow; 346 347 //Get type of post we're currently editing (are even editing something?) 348 $post_id = (int) @$_GET['post']; 349 350 //Yep, consistency would be nice here, but oh well 351 if (!empty($post_id)) $post_type = get_post_type($post_id); 352 if (empty($post_type)) $post_type = sanitize_key(@$_GET['post_type']); 353 if (empty($post_type)) $post_type = $pagenow == "post-new.php" ? "post" : ""; 354 355 return $post_type; 356 } 357 358 static function admin_settings_init() { 359 global $pagenow; 360 361 //Unlike the wp.org code sample, we'll only waste resources when necessary 362 if ($pagenow == 'options-writing.php' || //register when viewing the writing options page 363 $pagenow == 'options.php') { //and when clicking save (required or it won't know to save the settings) 364 365 register_setting('writing', 'emc2_editor_insert_p', array('MP_WP_Preserved_Markup', 'validate_settings')); 366 367 //give this setting its own section 368 add_settings_section('emc2_editor_insert_p', 'WYSIWYG New Line Behavior', array('MP_WP_Preserved_Markup', 'render_setting_section'), 'writing'); 369 370 //Add settings fields for each gui'd post type that supports a wysiwyg editor 371 $types = get_post_types(array('show_ui' => true), "objects"); 372 373 foreach ($types as $id => $type) { 374 if (post_type_supports($id, 'editor')) { 375 376 //cache valid types for fixit buttons 377 array_push(self::$valid_types, array( 378 'id' => $id, 379 'label' => $type->label)); 380 381 add_settings_field( 382 'emc2_editor_insert_p_' . $id, //setting id 383 $type->label, //setting title 384 array('MP_WP_Preserved_Markup', 'render_setting_input'), //render callback 385 'writing', //page 386 'emc2_editor_insert_p', //section (default = top) 387 array( 388 'label_for' => 'emc2_editor_insert_p_' . $id, 389 'id' => $id //pass id into render callback 390 ) 391 ); 392 } 393 } 394 } 395 } 396 397 static function render_setting_section($t) { 398 //add description of section to page 399 echo 400 "<p>By default the WordPress editor will automatically inject a new paragraph tag when the enter key is pressed. 401 The Preserved HTML Editor Markup plugin now gives you three options to chose from when a new line is created:</p> 402 403 <ul><li><b>P-Tag</b>: This option will continue using the default behavior of WordPress, while still preserving HTML whitespace & allowing for block-level anchor tags.</li> 404 <li><b>BR-Tag</b>: This option will inject HTML line-breaks (<BR> tags) instead of new paragraph tags.<b>*</b></li> 405 <li><b>Both</b>: This option will inject HTML line-breaks by default, but will start a new paragraph tag if two consecutive enter keys are pressed. <b>**</b></li></ul> 406 407 <p><i>*This was the default behavior of this plugin prior to version 1.2. You may continue to use this setting if you prefer, but I recommend the 'Both' setting as a compromise for Visual Tab and HTML Tab users.</i></p> 408 <p><i>**This feature is currently not 100% compatible with FireFox browsers. It will not cause problems with the editor, but it will only insert a new paragraph tag after two consecutive returns if the current line is already wrapped in a paragraph tag. However FireFox does allow you to change the Format option for a single line of unwrapped text in the Visual Editor, so users can easily add paragraph tags around unformatted text via the toolbar.</i></p> 409 <h3>Fixing Existing Content</h3> 410 <p><b style='color: red;'>BE SURE TO BACK UP YOUR DATABASE</b>, as the changes are permanent otherwise.</p> 411 <p>If you have existing content that isn't displaying correctly with this plugin enabled, you should use the 'Fix ...' buttons below. This will modify the content in the database with <a href='http://codex.wordpress.org/Function_Reference/wpautop'>wpautop</a>, including past revisions.</p> 412 <p>Fixing a content type multiple times should be harmless as WordPress does this by default. Content that you have modified via the editor after the plugin activation date will not be affected. But it is recommended that you fix your existing content soon after enabling the plugin.</p> 413 "; 414 415 echo "<p class='pressthis'>"; 416 foreach (self::$valid_types as $o) { 417 echo "<a style='width: auto; padding-right: 8px; cursor: pointer;' onclick='emc2pm_fix_content(\"{$o['id']}\"); return false;' href='#'><span>Fix {$o['label']}</span></a> "; 418 } 419 echo "</p>"; 420 421 echo "<h3>Configure New Line Behavior Per Post Type</h3>"; 422 } 423 424 static function render_setting_input($attr) { 425 //Display a list of option boxes for specifying the new line insertion behavior 426 $options = get_option('emc2_editor_insert_p'); 427 $value = isset($options[$attr['id']]) ? $options[$attr['id']] : 'br'; 428 ?> 429 <input id="<?php echo $attr['label_for'] . "_p"; ?>" name="emc2_editor_insert_p[<?php echo $attr['id']; ?>]" type="radio" value="p" <?php echo ($value == "p" ? "checked" : ""); ?> class="small-text" /> P-Tag<br> 430 <input id="<?php echo $attr['label_for'] . "_br"; ?>" name="emc2_editor_insert_p[<?php echo $attr['id']; ?>]" type="radio" value="br" <?php echo ($value == "br" ? "checked" : ""); ?> class="small-text" /> BR-Tag<br> 431 <input id="<?php echo $attr['label_for'] . "_both"; ?>" name="emc2_editor_insert_p[<?php echo $attr['id']; ?>]" type="radio" value="both" <?php echo ($value == "both" ? "checked" : ""); ?> class="small-text" /> Both 432 <?php 433 } 434 435 static function validate_settings($in) { 436 return $in; //it's an option box, I don't think people can mess that up 437 } 178 438 }
Note: See TracChangeset
for help on using the changeset viewer.