Index: trunk/extensions/ArticleComments/ArticleComments.php |
— | — | @@ -2,13 +2,13 @@ |
3 | 3 | /* |
4 | 4 | * ArticleComments.php - A MediaWiki extension for adding comment sections to articles. |
5 | 5 | * @author Jim R. Wilson |
6 | | - * @version 0.1 |
| 6 | + * @version 0.2 |
7 | 7 | * @copyright Copyright (C) 2007 Jim R. Wilson |
8 | 8 | * @license The MIT License - http://www.opensource.org/licenses/mit-license.php |
9 | 9 | * ----------------------------------------------------------------------- |
10 | 10 | * Description: |
11 | 11 | * This is a MediaWiki (http://www.mediawiki.org/) extension which adds support |
12 | | - * for comment sections within article pages |
| 12 | + * for comment sections within article pages, or directly into all pages. |
13 | 13 | * Requirements: |
14 | 14 | * This extension is made to work with MediaWiki 1.6.x, 1.8.x or 1.9.x running against |
15 | 15 | * PHP 4.3.x, 5.x or higher. |
— | — | @@ -21,6 +21,18 @@ |
22 | 22 | * Once installed, you may utilize ArticleComments by adding the following flag in the article text: |
23 | 23 | * <comments /> |
24 | 24 | * Note: Typically this would be placed at the end of the article text. |
| 25 | + * Version Notes: |
| 26 | + * version 0.2: |
| 27 | + * Fixed form post method to use localized version of "Special" |
| 28 | + * Added option for making the form automatically visible (no "Leave a comment..." link) |
| 29 | + * Added option of diabling the "Website" field |
| 30 | + * Added system message for prepopulating the comment box. |
| 31 | + * Added system message for structuring comment submission text. |
| 32 | + * Added abstracted method for form creation (for insertion into skins) |
| 33 | + * Added option to "Whitelist" Namespaces for comment submission (as by skin-level form). |
| 34 | + * Added check for user blocked status prior to comment submission. |
| 35 | + * version 0.1: |
| 36 | + * Initial release. |
25 | 37 | * ----------------------------------------------------------------------- |
26 | 38 | * Copyright (c) 2007 Jim R. Wilson |
27 | 39 | * |
— | — | @@ -54,7 +66,7 @@ |
55 | 67 | 'author'=>'Jim R. Wilson - wilson.jim.r <at> gmail.com', |
56 | 68 | 'url'=>'http://jimbojw.com/wiki/index.php?title=ArticleComments', |
57 | 69 | 'description'=>'Enables comment sections on article pages.', |
58 | | - 'version'=>'0.1' |
| 70 | + 'version'=>'0.2' |
59 | 71 | ); |
60 | 72 | |
61 | 73 | # Add Extension Functions |
— | — | @@ -67,51 +79,162 @@ |
68 | 80 | } |
69 | 81 | function wfArticleCommentsParserHook( $text, $params = array(), &$parser ) { |
70 | 82 | |
71 | | - global $wgScript; |
| 83 | + # Generate a comment form for display |
| 84 | + $commentForm = wfArticleCommentForm( $parser->mTitle, $params ); |
| 85 | + |
| 86 | + # Hide content from the Parser using base64 to avoid mangling. |
| 87 | + # Note: Content will be decoded after Tidy has finished its processing of the page. |
| 88 | + return '<pre>@ENCODED@'.base64_encode($commentForm).'@ENCODED@</pre>'; |
| 89 | +} |
72 | 90 | |
73 | | - $articleTitle = $parser->mTitle; |
| 91 | +/** |
| 92 | + * Echos out a comment form depending on the page action and namespace. |
| 93 | + * @param mixed $nsList An Namespace (int) or array of such values for which this method will dispaly the form. |
| 94 | + * @param Title $title The title of the article on which the form will appear. |
| 95 | + * @param Array $params A hash of parameters containing rendering options. |
| 96 | + */ |
| 97 | +function displayArticleCommentForm( $nsList = null, $title = null, $params = array() ) { |
74 | 98 | |
| 99 | + global $wgRequest; |
| 100 | + |
| 101 | + # Short circuit for anything other than action=view or action=purge |
| 102 | + if ($wgRequest->getVal('action') && |
| 103 | + $wgRequest->getVal('action')!='view' && |
| 104 | + $wgRequest->getVal('action')!='purge' |
| 105 | + ) return; |
| 106 | + |
| 107 | + # Use wgTitle if title is not specified |
| 108 | + if ($title==null) { |
| 109 | + global $wgTitle; |
| 110 | + $title = $wgTitle; |
| 111 | + } |
| 112 | + |
| 113 | + # Use wgArticleCommentsNSDisplayList if nsList is not specified |
| 114 | + if ($nsList==null) { |
| 115 | + global $wgArticleCommentsNSDisplayList; |
| 116 | + $nsList = $wgArticleCommentsNSDisplayList; |
| 117 | + $nsList = ($nsList==null?array(NS_MAIN):$nsList); |
| 118 | + } |
| 119 | + |
| 120 | + # Ensure that the namespace list is an actual list |
| 121 | + if (!is_array($nsList)) $nsList = array($nsList); |
| 122 | + |
| 123 | + # Display the form |
| 124 | + if (in_array($title->getNamespace(), $nsList)) { |
| 125 | + echo(wfArticleCommentForm($title, $params)); |
| 126 | + } |
| 127 | + |
| 128 | +} |
| 129 | + |
| 130 | +/** |
| 131 | + * Generates and returns an ArticleComment form. |
| 132 | + * @param Title $title The title of the article on which the form will appear. |
| 133 | + * @param Array $params A hash of parameters containing rendering options. |
| 134 | + */ |
| 135 | +function wfArticleCommentForm( $title = null, $params = array() ) { |
| 136 | + |
| 137 | + global $wgScript, $wgContLang, $wgArticleCommentDefaults; |
| 138 | + |
| 139 | + # Merge in global defaults if specified |
| 140 | + if (is_array($wgArticleCommentDefaults) && |
| 141 | + !empty($wgArticleCommentDefaults)) { |
| 142 | + $tmp = array(); |
| 143 | + foreach ($wgArticleCommentDefaults as $k=>$v) { |
| 144 | + $tmp[strtolower($k)] = $v; |
| 145 | + } |
| 146 | + $params = array_merge($tmp, $params); |
| 147 | + } |
| 148 | + |
| 149 | + # Use wgTitle if title is not specified |
| 150 | + if ($title==null) { |
| 151 | + global $wgTitle; |
| 152 | + $title = $wgTitle; |
| 153 | + } |
| 154 | + |
| 155 | + $ac = 'article-comments-'; |
| 156 | + $formAction = $wgScript.'/'.$wgContLang->getNsText(NS_SPECIAL).':ProcessComment'; |
| 157 | + |
75 | 158 | # Build out the comment form. |
76 | | - $content = '<div id="commentForm">'; |
77 | | - $content .= '<form method="post" action="'.$wgScript.'?title=Special:ProcessComment">'; |
78 | | - $content .= '<input type="hidden" id="titleKey" name="titleKey" value="'.$articleTitle->getDBKey().'" />'; |
79 | | - $content .= '<input type="hidden" id="titleNS" name="titleNS" value="'.$articleTitle->getNamespace().'" />'; |
80 | | - $content .= '<p>'.wfMsgForContent('article-comments-name-field').'<br /><input type="text" id="commenterName" name="commenterName" /></p>'; |
81 | | - $content .= '<p>'.wfMsgForContent('article-comments-url-field').'<br /><input type="text" id="commenterURL" name="commenterURL" /></p>'; |
82 | | - $content .= '<p>'.wfMsgForContent('article-comments-comment-field').'<br /><textarea id="comment" name="comment" style="width:30em" rows="5"></textarea></p>'; |
83 | | - $content .= '<p><input id="submit" type="submit" value="'.wfMsgForContent('article-comments-submit-button').'" /></p>'; |
84 | | - $content .= '</form>'; |
85 | | - $content .= '</div>'; |
| 159 | + $content = |
| 160 | + '<div id="commentForm">'. |
| 161 | + '<form method="post" action="'.$formAction.'">'. |
| 162 | + '<input type="hidden" id="titleKey" name="titleKey" '. |
| 163 | + 'value="'.$title->getDBKey().'" />'. |
| 164 | + '<input type="hidden" id="titleNS" name="titleNS" '. |
| 165 | + 'value="'.$title->getNamespace().'" />'. |
| 166 | + '<p>'.wfMsgForContent($ac.'name-field').'<br />'. |
| 167 | + '<input type="text" id="commenterName" name="commenterName" /></p>'. |
| 168 | + ($params['showurlfield']=='false' || $params['showurlfield']===false?'': |
| 169 | + '<p>'.wfMsgForContent($ac.'url-field').'<br />'. |
| 170 | + '<input type="text" id="commenterURL" name="commenterURL" /></p>' |
| 171 | + ). |
| 172 | + '<p>'.wfMsgForContent($ac.'comment-field').'<br />'. |
| 173 | + '<textarea id="comment" name="comment" style="width:30em" rows="5">'. |
| 174 | + '</textarea></p>'. |
| 175 | + '<p><input id="submit" type="submit" '. |
| 176 | + 'value="'.wfMsgForContent($ac.'submit-button').'" /></p>'. |
| 177 | + '</form></div>'; |
| 178 | + |
| 179 | + # Short-circuit if noScript has been set to anything other than false |
| 180 | + if (isset($params['noscript']) && |
| 181 | + $params['noscript']!=='false' && |
| 182 | + $params['noscript']) { |
| 183 | + return $content; |
| 184 | + } |
86 | 185 | |
87 | | - # Inline JavaScript to make form behavior more rich (must degrade gracefully in JS-disabled browsers) |
88 | | - $content .= '<script type="text/javascript">//<![CDATA['."\n"; |
89 | | - $content .= '(function(){'."\n"; |
| 186 | + # Inline JavaScript to make form behavior more rich (must degrade well in JS-disabled browsers) |
| 187 | + $content .= "<script type='text/javascript'>//<![CDATA[\n(function(){\n"; |
90 | 188 | |
91 | 189 | # Prefill the name field if the user is logged in. |
92 | | - $content .= 'var prefillUserName = function(){'."\n"; |
93 | | - $content .= 'var ptu=document.getElementById("pt-userpage");'."\n"; |
94 | | - $content .= 'if (ptu) document.getElementById("commenterName").value='; |
95 | | - $content .= 'ptu.getElementsByTagName("a")[0].innerHTML;'."\n"; |
96 | | - $content .= '};'."\n"; |
97 | | - $content .= 'if (window.addEventListener) window.addEventListener("load",prefillUserName,false);'."\n"; |
98 | | - $content .= 'else if (window.attachEvent) window.attachEvent("onload",prefillUserName);'."\n"; |
| 190 | + $content .= |
| 191 | + 'var prefillUserName = function(){'."\n". |
| 192 | + 'var ptu=document.getElementById("pt-userpage");'."\n". |
| 193 | + 'if (ptu) document.getElementById("commenterName").value='. |
| 194 | + 'ptu.getElementsByTagName("a")[0].innerHTML;};'."\n". |
| 195 | + 'if (window.addEventListener) window.addEventListener'. |
| 196 | + '("load",prefillUserName,false);'."\n". |
| 197 | + 'else if (window.attachEvent) window.attachEvent'. |
| 198 | + '("onload",prefillUserName);'."\n"; |
99 | 199 | |
| 200 | + # Prefill comment text if it has been specified by a system message |
| 201 | + # Note: This is done dynamically with JavaScript since it would be annoying |
| 202 | + # for JS-disabled browsers to have the prefilled text (since they'd have |
| 203 | + # to manually delete it). |
| 204 | + $pretext = wfMsgForContent($ac.'prefilled-comment-text'); |
| 205 | + if ($pretext) { |
| 206 | + $content .= |
| 207 | + 'var comment = document.getElementById("comment");'."\n". |
| 208 | + 'comment._everFocused=false;'."\n". |
| 209 | + 'comment.innerHTML="'.htmlspecialchars($pretext).'";'."\n". |
| 210 | + 'var clearCommentOnFirstFocus = function() {'."\n". |
| 211 | + 'var c=document.getElementById("comment");'."\n". |
| 212 | + 'if (!c._everFocused) {'."\n". |
| 213 | + 'c._everFocused=true;'."\n". |
| 214 | + 'c.value="";}}'."\n". |
| 215 | + 'if (comment.addEventListener) comment.addEventListener'. |
| 216 | + '("focus",clearCommentOnFirstFocus,false);'."\n". |
| 217 | + 'else if (comment.attachEvent) comment.attachEvent'. |
| 218 | + '("onfocus",clearCommentOnFirstFocus);'."\n"; |
| 219 | + } |
| 220 | + |
100 | 221 | # Hides the commentForm until the "Make a comment" link is clicked |
101 | | - $content .= 'var cf=document.getElementById("commentForm");'."\n"; |
102 | | - $content .= 'cf.style.display="none";'."\n"; |
103 | | - $content .= 'var p=document.createElement("p");'."\n"; |
104 | | - $content .= 'p.innerHTML="<a href=\'javascript:void(0)\' onclick=\''; |
105 | | - $content .= 'document.getElementById(\\"commentForm\\").style.display=\\"block\\";'; |
106 | | - $content .= 'this.style.display=\\"none\\";false'; |
107 | | - $content .= '\'>'.wfMsgForContent('article-comments-leave-comment-link').'</a>";'."\n"; |
108 | | - $content .= 'cf.parentNode.insertBefore(p,cf);'."\n"; |
| 222 | + # Note: To disable, set $wgArticleCommentDefaults['hideForm']=false in LocalSettings.php |
| 223 | + if (!isset($params['hideform']) || |
| 224 | + ($params['hideform']!='false' && |
| 225 | + !$params['hideform']===false)) { |
| 226 | + $content .= |
| 227 | + 'var cf=document.getElementById("commentForm");'."\n". |
| 228 | + 'cf.style.display="none";'."\n". |
| 229 | + 'var p=document.createElement("p");'."\n". |
| 230 | + 'p.innerHTML="<a href=\'javascript:void(0)\' onclick=\''. |
| 231 | + 'document.getElementById(\\"commentForm\\").style.display=\\"block\\";'. |
| 232 | + 'this.style.display=\\"none\\";false'. |
| 233 | + '\'>'.wfMsgForContent($ac.'leave-comment-link').'</a>";'."\n". |
| 234 | + 'cf.parentNode.insertBefore(p,cf);'."\n"; |
| 235 | + } |
109 | 236 | |
110 | | - $content .= '})();'; |
111 | | - $content .= '//]]></script>'; |
112 | | - |
113 | | - # Hide content from the Parser using base64 to avoid mangling. |
114 | | - # Note: Content will be decoded after Tidy has finished it's processing of the page. |
115 | | - return '<pre>@ENCODED@'.base64_encode($content).'@ENCODED@</pre>'; |
| 237 | + $content .= "})();\n//]]></script>"; |
| 238 | + return $content; |
116 | 239 | } |
117 | 240 | |
118 | 241 | # Attach Hooks |
— | — | @@ -125,9 +248,9 @@ |
126 | 249 | */ |
127 | 250 | function wfProcessEncodedContent($out, $text) { |
128 | 251 | $text = preg_replace( |
129 | | - '/<pre>@ENCODED@([0-9a-zA-Z\\+\\/]+=*)@ENCODED@<\\/pre>/e', |
130 | | - 'base64_decode("$1")', |
131 | | - $text |
| 252 | + '/<pre>@ENCODED@([0-9a-zA-Z\\+\\/]+=*)@ENCODED@<\\/pre>/e', |
| 253 | + 'base64_decode("$1")', |
| 254 | + $text |
132 | 255 | ); |
133 | 256 | return true; |
134 | 257 | } |
— | — | @@ -136,7 +259,7 @@ |
137 | 260 | $wgExtensionFunctions[] = 'setupSpecialProcessComment'; |
138 | 261 | function setupSpecialProcessComment() { |
139 | 262 | global $IP, $wgMessageCache; |
140 | | - require_once($IP . '/includes/SpecialPage.php'); |
| 263 | + require_once($IP.'/includes/SpecialPage.php'); |
141 | 264 | SpecialPage::addPage(new SpecialPage('ProcessComment', '', true, 'specialProcessComment', false)); |
142 | 265 | |
143 | 266 | # Messages used in this extension |
— | — | @@ -159,6 +282,9 @@ |
160 | 283 | $wgMessageCache->addMessage('article-comments-submission-succeeded', 'Comment submission succeeded'); |
161 | 284 | $wgMessageCache->addMessage('article-comments-submission-success', 'You have successfully submitted a comment for [[$1]]'); |
162 | 285 | $wgMessageCache->addMessage('article-comments-submission-view-all', 'You may view all comments on that article [[$1|here]]'); |
| 286 | + $wgMessageCache->addMessage('article-comments-prefilled-comment-text', ''); |
| 287 | + $wgMessageCache->addMessage('article-comments-user-is-blocked', 'Your user account is currently blocked from editing [[$1]].'); |
| 288 | + $wgMessageCache->addMessage('article-comments-new-comment', "\n== \$1 ==\n\n<div class='commentBlock'>\n\$2\n\n--\$3 \$4\n</div>\n"); |
163 | 289 | $wgMessageCache->addMessage('processcomment', 'Process Article Comment'); |
164 | 290 | } |
165 | 291 | |
— | — | @@ -169,9 +295,6 @@ |
170 | 296 | |
171 | 297 | global $wgOut, $wgContLang, $wgParser, $wgUser; |
172 | 298 | |
173 | | - # Check whether user is allowed to add a comment |
174 | | - # $wgUser->getBlockedStatus() |
175 | | - |
176 | 299 | # Retrieve submitted values |
177 | 300 | $titleKey = $_POST['titleKey']; |
178 | 301 | $titleNS = intval($_POST['titleNS']); |
— | — | @@ -209,6 +332,16 @@ |
210 | 333 | $talkTitle->mNamespace = $titleNS + 1 - ($titleNS % 2); |
211 | 334 | $talkArticle = new Article($talkTitle); |
212 | 335 | |
| 336 | + # Check whether user is blocked from editing the talk page |
| 337 | + if ($wgUser->isBlockedFrom($talkTitle)) { |
| 338 | + $wgOut->setPageTitle(wfMsgForContent($ac.'submission-failed')); |
| 339 | + $wikiText = "<div class='errorbox'>"; |
| 340 | + $wikiText .= wfMsgForContent($ac.'failure-reasons')."\n\n"; |
| 341 | + $wikiText .= '* '.wfMsgForContent($ac.'user-is-blocked', $talkTitle->getPrefixedText())."\n"; |
| 342 | + $wgOut->addWikiText($wikiText . "</div>"); |
| 343 | + return; |
| 344 | + } |
| 345 | + |
213 | 346 | # Retrieve article content |
214 | 347 | $articleContent = ''; |
215 | 348 | if ( $article->exists() ) { |
— | — | @@ -221,10 +354,19 @@ |
222 | 355 | $talkContent = $talkArticle->getContent(); |
223 | 356 | } |
224 | 357 | |
| 358 | + |
| 359 | + # Check if talk NS is in the Namespace whitelist |
| 360 | + global $wgArticleCommentsNSWhitelist; |
| 361 | + $skipCheck = ( |
| 362 | + is_array($wgArticleCommentsNSWhitelist) ? |
| 363 | + in_array($talkTitle->getNamespace(),$wgArticleCommentsNSWhitelist): |
| 364 | + false |
| 365 | + ); |
| 366 | + |
225 | 367 | # Check whether the article or its talk page contains a <comments /> flag |
226 | | - if ( |
227 | | - strpos($articleContent, '<comments />')===false |
228 | | - && strpos($talkContent, '<comments />')===false |
| 368 | + if (!$skipCheck && |
| 369 | + preg_match('/<comments( +[^>]*)?/>/', $articleContent)===0 && |
| 370 | + preg_match('/<comments( +[^>]*)?/>/', $talkContent)===0 |
229 | 371 | ) { |
230 | 372 | $wgOut->setPageTitle(wfMsgForContent($ac.'submission-failed')); |
231 | 373 | $wgOut->addWikiText( |
— | — | @@ -247,13 +389,16 @@ |
248 | 390 | else $sigText = $commenterName; |
249 | 391 | |
250 | 392 | # Append most recent comment |
251 | | - $talkContent .= "\n== ".wfMsgForContent($ac.'commenter-said', $commenterName)." ==\n\n"; |
252 | | - $talkContent .= "<div class='commentBlock'>\n"; |
253 | | - $talkContent .= $comment."\n\n"; |
254 | | - $talkContent .= "--$sigText $d\n"; |
255 | | - $talkContent .= "</div>"; |
| 393 | + $talkContent .= |
| 394 | + wfMsgForContent( |
| 395 | + $ac.'new-comment', |
| 396 | + wfMsgForContent($ac.'commenter-said', $commenterName), |
| 397 | + $comment, |
| 398 | + $sigText, |
| 399 | + $d |
| 400 | + ); |
256 | 401 | |
257 | | - # Update article |
| 402 | + # Update the talkArticle with the new comment |
258 | 403 | $summary = wfMsgForContent($ac.'summary', $commenterName); |
259 | 404 | if (method_exists($talkArticle, 'doEdit')) { |
260 | 405 | $talkArticle->doEdit($talkContent, $summary); |