Index: trunk/extensions/CommunityVoice/CommunityVoice.alias.php |
— | — | @@ -0,0 +1,14 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Aliases for special pages |
| 5 | + * |
| 6 | + * @file |
| 7 | + * @ingroup Extensions |
| 8 | + */ |
| 9 | + |
| 10 | +$aliases = array(); |
| 11 | + |
| 12 | +/** English */ |
| 13 | +$aliases['en'] = array( |
| 14 | + 'CommunityVoice' => array( 'CommunityVoice' ), |
| 15 | +); |
Property changes on: trunk/extensions/CommunityVoice/CommunityVoice.alias.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 16 | + native |
Index: trunk/extensions/CommunityVoice/CommunityVoice.sql |
— | — | @@ -2,6 +2,8 @@ |
3 | 3 | -- SQL for CommunityVoice Extension |
4 | 4 | -- |
5 | 5 | -- Table for ratings |
| 6 | + |
| 7 | +-- FIXME: is it a good idea to drop exisiting tables? Other scripts for MediaWiki extensions do not contain this. |
6 | 8 | DROP TABLE IF EXISTS /*$wgDBprefix*/cv_ratings_votes; |
7 | 9 | CREATE TABLE /*$wgDBPrefix*/cv_ratings_votes ( |
8 | 10 | -- Category of item being rated |
— | — | @@ -19,6 +21,7 @@ |
20 | 22 | ) /*$wgDBTableOptions*/; |
21 | 23 | -- |
22 | 24 | -- Table for articles which include ratings |
| 25 | +-- FIXME: is it a good idea to drop exisiting tables? Other scripts for MediaWiki extensions do not contain this. |
23 | 26 | DROP TABLE IF EXISTS /*$wgDBprefix*/cv_ratings_usage; |
24 | 27 | CREATE TABLE /*$wgDBPrefix*/cv_ratings_usage ( |
25 | 28 | -- Category of item being rated |
Property changes on: trunk/extensions/CommunityVoice/CommunityVoice.sql |
___________________________________________________________________ |
Added: svn:eol-style |
26 | 29 | + native |
Index: trunk/extensions/CommunityVoice/Modules/Ratings.php |
— | — | @@ -1,530 +1,489 @@ |
2 | | -<?php |
3 | | - |
4 | | -abstract class CommunityVoiceRatings { |
5 | | - |
6 | | - /* Private Static Functions */ |
7 | | - |
8 | | - private static function getScaleFraction( |
9 | | - $rating, |
10 | | - $star |
11 | | - ) { |
12 | | - if ( floor( $rating ) > $star ) { |
13 | | - return 6; |
14 | | - } else if ( floor( $rating ) < $star ) { |
15 | | - return 0; |
16 | | - } else { |
17 | | - return round( ( 6 / 10 ) * ( ( $rating - floor( $rating ) ) * 10 ) ); |
18 | | - } |
19 | | - } |
20 | | - |
21 | | - private static function getArticlesUsing( |
22 | | - $category, |
23 | | - $title |
24 | | - ) { |
25 | | - $dbr = wfGetDB( DB_SLAVE ); |
26 | | - $result = $dbr->select( |
27 | | - 'cv_ratings_usage', |
28 | | - 'usg_article', |
29 | | - array( |
30 | | - 'usg_category' => $category, |
31 | | - 'usg_title' => $title, |
32 | | - ) |
33 | | - ); |
34 | | - $articles = array(); |
35 | | - while ( $row = $result->fetchRow() ) { |
36 | | - $articles[] = (string)$row['usg_article']; |
37 | | - } |
38 | | - return $articles; |
39 | | - } |
40 | | - |
41 | | - private static function getCategories() { |
42 | | - $dbr = wfGetDB( DB_SLAVE ); |
43 | | - $result = $dbr->select( |
44 | | - 'cv_ratings_votes', |
45 | | - 'DISTINCT vot_category' |
46 | | - ); |
47 | | - $categories = array(); |
48 | | - while ( $row = $result->fetchRow() ) { |
49 | | - $categories[] = (string)$row['vot_category']; |
50 | | - } |
51 | | - return $categories; |
52 | | - } |
53 | | - |
54 | | - private static function getTitles( |
55 | | - $category |
56 | | - ) { |
57 | | - $dbr = wfGetDB( DB_SLAVE ); |
58 | | - $result = $dbr->select( |
59 | | - 'cv_ratings_votes', |
60 | | - 'DISTINCT vot_title', |
61 | | - array( 'vot_category' => $category ) |
62 | | - ); |
63 | | - $titles = array(); |
64 | | - while ( $row = $result->fetchRow() ) { |
65 | | - $titles[] = (string)$row['vot_title']; |
66 | | - } |
67 | | - return $titles; |
68 | | - } |
69 | | - |
70 | | - private static function getTotalVotes( |
71 | | - $category, |
72 | | - $title |
73 | | - ) { |
74 | | - $dbr = wfGetDB( DB_SLAVE ); |
75 | | - return (integer)$dbr->selectField( |
76 | | - 'cv_ratings_votes', |
77 | | - 'COUNT(*)', |
78 | | - array( |
79 | | - 'vot_category' => $category, |
80 | | - 'vot_title' => $title, |
81 | | - ) |
82 | | - ); |
83 | | - } |
84 | | - |
85 | | - private static function getUserVoted( |
86 | | - $category, |
87 | | - $title |
88 | | - ) { |
89 | | - global $wgUser; |
90 | | - $dbr = wfGetDB( DB_SLAVE ); |
91 | | - return (bool)$dbr->selectField( |
92 | | - 'cv_ratings_votes', |
93 | | - 'COUNT(*)', |
94 | | - array( |
95 | | - 'vot_category' => $category, |
96 | | - 'vot_title' => $title, |
97 | | - 'vot_user' => $wgUser->getId(), |
98 | | - ) |
99 | | - ); |
100 | | - } |
101 | | - |
102 | | - private static function getAverageRating( |
103 | | - $category, |
104 | | - $title |
105 | | - ) { |
106 | | - $dbr = wfGetDB( DB_SLAVE ); |
107 | | - return (float)$dbr->selectField( |
108 | | - 'cv_ratings_votes', |
109 | | - 'AVG(vot_rating)', |
110 | | - array( |
111 | | - 'vot_category' => $category, |
112 | | - 'vot_title' => $title, |
113 | | - ) |
114 | | - ); |
115 | | - } |
116 | | - |
117 | | - private static function addVote( |
118 | | - $category, |
119 | | - $title, |
120 | | - $rating |
121 | | - ) { |
122 | | - global $wgUser; |
123 | | - // Checks if... |
124 | | - if ( |
125 | | - // User is logged in |
126 | | - $wgUser->isLoggedIn() && |
127 | | - // User has not yet voted |
128 | | - !self::getUserVoted( $category, $title ) |
129 | | - ) { |
130 | | - // Get database connection |
131 | | - $dbw = wfGetDB( DB_MASTER ); |
132 | | - // Insert new vote for user |
133 | | - $dbw->insert( |
134 | | - 'cv_ratings_votes', |
135 | | - array( |
136 | | - 'vot_category' => $category, |
137 | | - 'vot_title' => $title, |
138 | | - 'vot_user' => $wgUser->getId(), |
139 | | - 'vot_rating' => $rating, |
140 | | - ) |
141 | | - ); |
142 | | - $dbw->commit(); |
143 | | - return true; |
144 | | - } |
145 | | - return false; |
146 | | - } |
147 | | - |
148 | | - /* Static Functions */ |
149 | | - |
150 | | - public static function register() { |
151 | | - global $wgParser, $wgAjaxExportList, $wgHooks; |
152 | | - // Register the hook with the parser |
153 | | - $wgParser->setHook( 'ratings:scale', array( __CLASS__, 'renderScale' ) ); |
154 | | - // Register ajax response hook |
155 | | - $wgAjaxExportList[] = __CLASS__ . '::handleScaleVoteCall'; |
156 | | - // Register article save hook |
157 | | - $wgHooks['ArticleSave'][] = __CLASS__ . '::updateArticleUsage'; |
158 | | - |
159 | | - } |
160 | | - |
161 | | - public static function updateArticleUsage( |
162 | | - $article, |
163 | | - $user, |
164 | | - $text, |
165 | | - $summary, |
166 | | - $minor, |
167 | | - $watch, |
168 | | - $sectionanchor, |
169 | | - $flags |
170 | | - ) { |
171 | | - $usedRatings = array(); |
172 | | - // Extract all ratings:scale tags |
173 | | - preg_match_all( |
174 | | - "/<ratings:scale[^>]*[\\/]*>/i", $text, $matches, PREG_PATTERN_ORDER |
175 | | - ); |
176 | | - // Loop over each match |
177 | | - foreach( $matches[0] as $match ) { |
178 | | - $rating = array(); |
179 | | - foreach ( array( 'category', 'title' ) as $attribute ) { |
180 | | - // Extract value of attribute |
181 | | - preg_match( |
182 | | - "/{$attribute}=['\"]*(?<value>[^'\"]*)['\"]*/i", |
183 | | - $match, |
184 | | - $values |
185 | | - ); |
186 | | - if ( isset( $values['value'] ) ) { |
187 | | - $rating[$attribute] = $values['value']; |
188 | | - } |
189 | | - } |
190 | | - if ( isset( $rating['category'], $rating['title'] ) ) { |
191 | | - $usedRatings[] = $rating; |
192 | | - } |
193 | | - } |
194 | | - // Gets name of article |
195 | | - $articleDbKey = $article->getTitle()->getPrefixedDBkey(); |
196 | | - // Get database connection |
197 | | - $dbw = wfGetDB( DB_MASTER ); |
198 | | - // Remove all usage for this article |
199 | | - $dbw->delete( |
200 | | - 'cv_ratings_usage', |
201 | | - array( 'usg_article' => $articleDbKey ) |
202 | | - ); |
203 | | - // Loop over each rating |
204 | | - foreach ( $usedRatings as $rating ) { |
205 | | - // Add usage for rating for this article |
206 | | - $dbw->insert( |
207 | | - 'cv_ratings_usage', |
208 | | - array( |
209 | | - 'usg_category' => $rating['category'], |
210 | | - 'usg_title' => $rating['title'], |
211 | | - 'usg_article' => $articleDbKey, |
212 | | - ) |
213 | | - ); |
214 | | - } |
215 | | - return true; |
216 | | - } |
217 | | - |
218 | | - public static function renderScale( |
219 | | - $input, |
220 | | - $args, |
221 | | - $parser |
222 | | - ) { |
223 | | - global $wgUser, $wgTitle; |
224 | | - global $egCommunityVoiceResourcesPath; |
225 | | - // Validate and sanitize incoming arguments |
226 | | - $errors = array(); |
227 | | - $error = false; |
228 | | - foreach ( array( 'category', 'title' ) as $argument ) { |
229 | | - if ( isset( $args[$argument] ) ) { |
230 | | - $args[$argument] = htmlspecialchars( $args[$argument] ); |
231 | | - } else { |
232 | | - $error = true; |
233 | | - if ( $parser->getOptions()->getIsPreview() ) { |
234 | | - $errors[] = CommunityVoice::getMessage( |
235 | | - 'ratings', 'error-missing-argument', $argument |
236 | | - ); |
237 | | - } |
238 | | - } |
239 | | - } |
240 | | - // Checks if an error ocurred |
241 | | - if ( $error ) { |
242 | | - // Checks if there are any error messages to return |
243 | | - if ( count( $errors ) ) { |
244 | | - return Html::div( |
245 | | - array( 'class' => 'error' ), implode( ' ', $errors ) |
246 | | - ); |
247 | | - } |
248 | | - // Continues without rendering |
249 | | - return true; |
250 | | - } |
251 | | - // Collects data |
252 | | - $totalVotes = self::getTotalVotes( $args['category'], $args['title'] ); |
253 | | - $rating = self::getAverageRating( $args['category'], $args['title'] ); |
254 | | - $userVoted = self::getUserVoted( $args['category'], $args['title'] ); |
255 | | - // Builds sanitized HTML id with prepended module naming |
256 | | - $id = Html::toId( |
257 | | - 'cv_ratings_scale_' . $args['category'] . '_' . $args['title'] |
258 | | - ); |
259 | | - // Gets stats message |
260 | | - $stats = CommunityVoice::getMessage( |
261 | | - 'ratings', 'scale-stats', array( round( $rating, 1 ), $totalVotes ) |
262 | | - ); |
263 | | - // Begins rating scale |
264 | | - $htmlOut = Html::open( |
265 | | - 'div', |
266 | | - array( 'class' => 'communityvoice-ratings-scale', 'id' => $id ) |
267 | | - ); |
268 | | - // Checks for input |
269 | | - if ( $input != '' ) { |
270 | | - // Adds content of tag as parsed wiki-text |
271 | | - $htmlOut .= $parser->recursiveTagParse( $input ); |
272 | | - } |
273 | | - // Checks if the user has not voted yet and is logged in |
274 | | - if ( !$userVoted && $wgUser->isLoggedIn() ) { |
275 | | - |
276 | | - /* Ajax Interaction */ |
277 | | - |
278 | | - // Adds scale script |
279 | | - $htmlOut .= Html::script( |
280 | | - Js::callFunction( |
281 | | - 'communityVoice.ratings.scales.add', |
282 | | - Js::buildInstance( |
283 | | - 'CommunityVoiceRatingsScale', |
284 | | - array( |
285 | | - Js::toScalar( $id ), |
286 | | - Js::toScalar( $args['category'] ), |
287 | | - Js::toScalar( $args['title'] ), |
288 | | - Js::toScalar( $rating ), |
289 | | - Js::toObject( |
290 | | - array( |
291 | | - 'stats' => $stats, |
292 | | - 'status' => array( |
293 | | - 'null' => ' ', |
294 | | - 'sending' => CommunityVoice::getMessage( |
295 | | - 'ratings', 'scale-status-sending' |
296 | | - ), |
297 | | - 'error' => CommunityVoice::getMessage( |
298 | | - 'ratings', 'scale-status-error' |
299 | | - ), |
300 | | - 'thanks' => CommunityVoice::getMessage( |
301 | | - 'ratings', 'scale-status-thanks' |
302 | | - ), |
303 | | - ) |
304 | | - ) |
305 | | - ), |
306 | | - Js::toScalar( $wgTitle->getPrefixedText() ) |
307 | | - ) |
308 | | - ) |
309 | | - ) |
310 | | - ); |
311 | | - |
312 | | - /* HTML Form Interaction */ |
313 | | - |
314 | | - // Begins non-javascript fallback |
315 | | - $htmlOut .= Html::open( 'noscript' ); |
316 | | - // Begins form |
317 | | - $specialPageTitle = Title::newFromText( 'Special:CommunityVoice' ); |
318 | | - $htmlOut .= Html::open( |
319 | | - 'form', |
320 | | - array( |
321 | | - 'action' => $specialPageTitle->getFullUrl(), |
322 | | - 'method' => 'post', |
323 | | - ) |
324 | | - ); |
325 | | - // Builds list of hidden fields |
326 | | - $hiddenFields = array( |
327 | | - 'token' => $wgUser->editToken(), |
328 | | - 'module' => 'Ratings', |
329 | | - 'action' => 'ScaleVoteSubmission', |
330 | | - 'scale[article]' => $wgTitle->getPrefixedText(), |
331 | | - 'scale[category]' => $args['category'], |
332 | | - 'scale[title]' => $args['title'], |
333 | | - ); |
334 | | - // Loops over each field |
335 | | - foreach ( $hiddenFields as $name => $value ) { |
336 | | - // Adds hidden field |
337 | | - $htmlOut .= Html::input( |
338 | | - array( |
339 | | - 'type' => 'hidden', 'name' => $name, 'value' => $value |
340 | | - ) |
341 | | - ); |
342 | | - } |
343 | | - // Loops 5 times (once per star) |
344 | | - for ( $i = 0; $i < 5; $i++ ) { |
345 | | - // Adds star as image input |
346 | | - $htmlOut .= Html::input( |
347 | | - array( |
348 | | - 'type' => 'image', |
349 | | - 'name' => 'scale[rating_' . $i . ']', |
350 | | - 'src' => sprintf( |
351 | | - '%s/Icons/star-%d.png', |
352 | | - $egCommunityVoiceResourcesPath, |
353 | | - self::getScaleFraction( $rating, $i ) |
354 | | - ), |
355 | | - 'border' => 0, |
356 | | - 'alt' => '', |
357 | | - 'class' => 'star', |
358 | | - 'align' => 'absmiddle', |
359 | | - ) |
360 | | - ); |
361 | | - } |
362 | | - // Adds stats message |
363 | | - $htmlOut .= Html::tag( 'span', array( 'class' => 'stats' ), $stats ); |
364 | | - // Ends form |
365 | | - $htmlOut .= Html::close( 'form' ); |
366 | | - // Ends non-javascript fallback |
367 | | - $htmlOut .= Html::close( 'noscript' ); |
368 | | - } else { |
369 | | - |
370 | | - /* No Interaction */ |
371 | | - |
372 | | - // Loops 5 times (once per star) |
373 | | - for ( $i = 0; $i < 5; $i++ ) { |
374 | | - // Adds star as image |
375 | | - $htmlOut .= Html::tag( |
376 | | - 'img', |
377 | | - array( |
378 | | - 'src' => sprintf( |
379 | | - '%s/Icons/star-%d.png', |
380 | | - $egCommunityVoiceResourcesPath, |
381 | | - self::getScaleFraction( $rating, $i ) |
382 | | - ), |
383 | | - 'border' => 0, |
384 | | - 'alt' => '', |
385 | | - 'class' => 'star', |
386 | | - 'align' => 'absmiddle', |
387 | | - ) |
388 | | - ); |
389 | | - } |
390 | | - // Adds stats message |
391 | | - $htmlOut .= Html::tag( 'span', array( 'class' => 'stats' ), $stats ); |
392 | | - } |
393 | | - // Ends scale |
394 | | - $htmlOut .= Xml::closeElement( 'div' ); |
395 | | - // Returns output |
396 | | - return $htmlOut; |
397 | | - } |
398 | | - |
399 | | - /* Processing Functions */ |
400 | | - |
401 | | - /** |
402 | | - * Hanlder for ratings scale vote via ajax call |
403 | | - */ |
404 | | - public static function handleScaleVoteCall( |
405 | | - $category, |
406 | | - $title, |
407 | | - $rating, |
408 | | - $article |
409 | | - ) { |
410 | | - global $wgUser; |
411 | | - // Adds vote and checks for success |
412 | | - if ( self::addVote( $category, $title, $rating ) ) { |
413 | | - // Gets new rating data |
414 | | - $rating = self::getAverageRating( $category, $title ); |
415 | | - // Builds result |
416 | | - $result = array( |
417 | | - 'rating' => $rating, |
418 | | - 'stats' => CommunityVoice::getMessage( |
419 | | - 'ratings', |
420 | | - 'scale-stats', |
421 | | - array( |
422 | | - round( $rating, 1 ), |
423 | | - self::getTotalVotes( $category, $title ) |
424 | | - ) |
425 | | - ), |
426 | | - ); |
427 | | - // Gets articles that use this rating |
428 | | - $articles = self::getArticlesUsing( $category, $title ); |
429 | | - // Loops over each article |
430 | | - foreach($articles as $article ) { |
431 | | - // Invalidates the cache of the article |
432 | | - CommunityVoice::touchArticle( $article ); |
433 | | - } |
434 | | - // Ensure database commits take place (since this is an ajax call) |
435 | | - $dbw = wfGetDB( DB_MASTER ); |
436 | | - $dbw->commit(); |
437 | | - // Returns result |
438 | | - return Js::toObject( $result ); |
439 | | - } |
440 | | - // Returns error information |
441 | | - return Js::toObject( array( 'rating' => -1, 'stats' => null ) ); |
442 | | - } |
443 | | - |
444 | | - /** |
445 | | - * Hanlder for ratings scale vote via HTML form submission |
446 | | - */ |
447 | | - public static function handleScaleVoteSubmission() { |
448 | | - global $wgOut, $wgRequest; |
449 | | - // Gets scale data |
450 | | - $scale = $wgRequest->getArray( 'scale' ); |
451 | | - // Checks if an article was given |
452 | | - if ( isset( $scale['article'], $scale['title'], $scale['category'] ) ) { |
453 | | - // Looks for rating value |
454 | | - foreach ( $scale as $key => $value ) { |
455 | | - // Breaks key into parts |
456 | | - $parts = explode( '_', $key ); |
457 | | - // Checks if... |
458 | | - if ( |
459 | | - // There's at least 2 parts |
460 | | - ( count( $parts ) > 1 ) && |
461 | | - // The first part is 'rating' |
462 | | - ( $parts[0] == 'rating' ) && |
463 | | - // The second part is a number |
464 | | - ( is_numeric( $parts[1] ) ) |
465 | | - ) { |
466 | | - // Uses number as rating |
467 | | - $rating = $parts[1]; |
468 | | - // Finishes loop |
469 | | - break; |
470 | | - } |
471 | | - } |
472 | | - // Checks if a rating was found |
473 | | - if ( isset( $rating ) ) { |
474 | | - // Adds vote and checks for success |
475 | | - if ( |
476 | | - self::addVote( |
477 | | - $scale['category'], $scale['title'], $rating |
478 | | - ) |
479 | | - ) { |
480 | | - // Gets articles that use this rating |
481 | | - $articles = self::getArticlesUsing( |
482 | | - $scale['category'], $scale['title'] |
483 | | - ); |
484 | | - // Loops over each article |
485 | | - foreach($articles as $article ) { |
486 | | - // Invalidates the cache of the article |
487 | | - CommunityVoice::touchArticle( $article ); |
488 | | - } |
489 | | - // Redirects user back to article |
490 | | - $wgOut->redirect( |
491 | | - Title::newFromText( $scale['article'] )->getFullUrl() |
492 | | - ); |
493 | | - } else { |
494 | | - throw new MWException( 'Voting failed!' ); |
495 | | - } |
496 | | - } else { |
497 | | - throw new MWException( 'No rating parameter!' ); |
498 | | - } |
499 | | - } else { |
500 | | - throw new MWException( 'Missing parameters!' ); |
501 | | - } |
502 | | - } |
503 | | - |
504 | | - /* UI Functions */ |
505 | | - |
506 | | - /** |
507 | | - * Outputs a summary UI for the module |
508 | | - */ |
509 | | - public static function showSummary() { |
510 | | - global $wgOut; |
511 | | - // |
512 | | - $wgOut->addWikiText( '==== Categories ====' ); |
513 | | - $xmlCategories = Html::open( 'ul' ); |
514 | | - foreach( self::getCategories() as $category ) { |
515 | | - $xmlCategories .= Html::tag( 'li', $category ); |
516 | | - } |
517 | | - $xmlCategories .= Html::close( 'ul' ); |
518 | | - $wgOut->addHtml( $xmlCategories ); |
519 | | - } |
520 | | - |
521 | | - /** |
522 | | - * Outputs main UI for module |
523 | | - */ |
524 | | - public static function showMain( |
525 | | - $path |
526 | | - ) { |
527 | | - global $wgOut; |
528 | | - // |
529 | | - $wgOut->addWikiText( '==== Detailed Information ====' ); |
530 | | - } |
531 | | -} |
\ No newline at end of file |
| 2 | +<?php
|
| 3 | +
|
| 4 | +abstract class CommunityVoiceRatings {
|
| 5 | +
|
| 6 | + /* Private Static Functions */
|
| 7 | +
|
| 8 | + private static function getScaleFraction( $rating, $star ) {
|
| 9 | + if ( floor( $rating ) > $star ) {
|
| 10 | + return 6;
|
| 11 | + } else if ( floor( $rating ) < $star ) {
|
| 12 | + return 0;
|
| 13 | + } else {
|
| 14 | + return round( ( 6 / 10 ) * ( ( $rating - floor( $rating ) ) * 10 ) );
|
| 15 | + }
|
| 16 | + }
|
| 17 | +
|
| 18 | + private static function getArticlesUsing( $category, $title ) {
|
| 19 | + $dbr = wfGetDB( DB_SLAVE );
|
| 20 | + $result = $dbr->select(
|
| 21 | + 'cv_ratings_usage',
|
| 22 | + 'usg_article',
|
| 23 | + array(
|
| 24 | + 'usg_category' => $category,
|
| 25 | + 'usg_title' => $title,
|
| 26 | + )
|
| 27 | + );
|
| 28 | + $articles = array();
|
| 29 | + while ( $row = $result->fetchRow() ) {
|
| 30 | + $articles[] = (string)$row['usg_article'];
|
| 31 | + }
|
| 32 | + return $articles;
|
| 33 | + }
|
| 34 | +
|
| 35 | + private static function getCategories() {
|
| 36 | + $dbr = wfGetDB( DB_SLAVE );
|
| 37 | + $result = $dbr->select(
|
| 38 | + 'cv_ratings_votes',
|
| 39 | + 'DISTINCT vot_category'
|
| 40 | + );
|
| 41 | + $categories = array();
|
| 42 | + while ( $row = $result->fetchRow() ) {
|
| 43 | + $categories[] = (string)$row['vot_category'];
|
| 44 | + }
|
| 45 | + return $categories;
|
| 46 | + }
|
| 47 | +
|
| 48 | + private static function getTitles( $category ) {
|
| 49 | + $dbr = wfGetDB( DB_SLAVE );
|
| 50 | + $result = $dbr->select(
|
| 51 | + 'cv_ratings_votes',
|
| 52 | + 'DISTINCT vot_title',
|
| 53 | + array( 'vot_category' => $category )
|
| 54 | + );
|
| 55 | + $titles = array();
|
| 56 | + while ( $row = $result->fetchRow() ) {
|
| 57 | + $titles[] = (string)$row['vot_title'];
|
| 58 | + }
|
| 59 | + return $titles;
|
| 60 | + }
|
| 61 | +
|
| 62 | + private static function getTotalVotes( $category, $title ) {
|
| 63 | + $dbr = wfGetDB( DB_SLAVE );
|
| 64 | + return (integer)$dbr->selectField(
|
| 65 | + 'cv_ratings_votes',
|
| 66 | + 'COUNT(*)',
|
| 67 | + array(
|
| 68 | + 'vot_category' => $category,
|
| 69 | + 'vot_title' => $title,
|
| 70 | + )
|
| 71 | + );
|
| 72 | + }
|
| 73 | +
|
| 74 | + private static function getUserVoted( $category, $title ) {
|
| 75 | + global $wgUser;
|
| 76 | + $dbr = wfGetDB( DB_SLAVE );
|
| 77 | + return (bool)$dbr->selectField(
|
| 78 | + 'cv_ratings_votes',
|
| 79 | + 'COUNT(*)',
|
| 80 | + array(
|
| 81 | + 'vot_category' => $category,
|
| 82 | + 'vot_title' => $title,
|
| 83 | + 'vot_user' => $wgUser->getId(),
|
| 84 | + )
|
| 85 | + );
|
| 86 | + }
|
| 87 | +
|
| 88 | + private static function getAverageRating( $category, $title ) {
|
| 89 | + $dbr = wfGetDB( DB_SLAVE );
|
| 90 | + return (float)$dbr->selectField(
|
| 91 | + 'cv_ratings_votes',
|
| 92 | + 'AVG(vot_rating)',
|
| 93 | + array(
|
| 94 | + 'vot_category' => $category,
|
| 95 | + 'vot_title' => $title,
|
| 96 | + )
|
| 97 | + );
|
| 98 | + }
|
| 99 | +
|
| 100 | + private static function addVote( $category, $title, $rating ) {
|
| 101 | + global $wgUser;
|
| 102 | + // Checks if...
|
| 103 | + if (
|
| 104 | + // User is logged in
|
| 105 | + $wgUser->isLoggedIn() &&
|
| 106 | + // User has not yet voted
|
| 107 | + !self::getUserVoted( $category, $title )
|
| 108 | + ) {
|
| 109 | + // Get database connection
|
| 110 | + $dbw = wfGetDB( DB_MASTER );
|
| 111 | + // Insert new vote for user
|
| 112 | + $dbw->insert(
|
| 113 | + 'cv_ratings_votes',
|
| 114 | + array(
|
| 115 | + 'vot_category' => $category,
|
| 116 | + 'vot_title' => $title,
|
| 117 | + 'vot_user' => $wgUser->getId(),
|
| 118 | + 'vot_rating' => $rating,
|
| 119 | + )
|
| 120 | + );
|
| 121 | + $dbw->commit();
|
| 122 | + return true;
|
| 123 | + }
|
| 124 | + return false;
|
| 125 | + }
|
| 126 | +
|
| 127 | + /* Static Functions */
|
| 128 | +
|
| 129 | + public static function register() {
|
| 130 | + global $wgParser, $wgAjaxExportList, $wgHooks;
|
| 131 | + // Register the hook with the parser
|
| 132 | + $wgParser->setHook( 'ratings:scale', array( __CLASS__, 'renderScale' ) );
|
| 133 | + // Register ajax response hook
|
| 134 | + $wgAjaxExportList[] = __CLASS__ . '::handleScaleVoteCall';
|
| 135 | + // Register article save hook
|
| 136 | + $wgHooks['ArticleSave'][] = __CLASS__ . '::updateArticleUsage';
|
| 137 | +
|
| 138 | + }
|
| 139 | +
|
| 140 | + public static function updateArticleUsage( $article, $user, $text, $summary, $minor, $watch, $sectionanchor, $flags ) {
|
| 141 | + $usedRatings = array();
|
| 142 | + // Extract all ratings:scale tags
|
| 143 | + preg_match_all(
|
| 144 | + "/<ratings:scale[^>]*[\\/]*>/i", $text, $matches, PREG_PATTERN_ORDER
|
| 145 | + );
|
| 146 | + // Loop over each match
|
| 147 | + foreach ( $matches[0] as $match ) {
|
| 148 | + $rating = array();
|
| 149 | + foreach ( array( 'category', 'title' ) as $attribute ) {
|
| 150 | + // Extract value of attribute
|
| 151 | + preg_match(
|
| 152 | + "/{$attribute}=['\"]*(?<value>[^'\"]*)['\"]*/i",
|
| 153 | + $match,
|
| 154 | + $values
|
| 155 | + );
|
| 156 | + if ( isset( $values['value'] ) ) {
|
| 157 | + $rating[$attribute] = $values['value'];
|
| 158 | + }
|
| 159 | + }
|
| 160 | + if ( isset( $rating['category'], $rating['title'] ) ) {
|
| 161 | + $usedRatings[] = $rating;
|
| 162 | + }
|
| 163 | + }
|
| 164 | + // Gets name of article
|
| 165 | + $articleDbKey = $article->getTitle()->getPrefixedDBkey();
|
| 166 | + // Get database connection
|
| 167 | + $dbw = wfGetDB( DB_MASTER );
|
| 168 | + // Remove all usage for this article
|
| 169 | + $dbw->delete(
|
| 170 | + 'cv_ratings_usage',
|
| 171 | + array( 'usg_article' => $articleDbKey )
|
| 172 | + );
|
| 173 | + // Loop over each rating
|
| 174 | + foreach ( $usedRatings as $rating ) {
|
| 175 | + // Add usage for rating for this article
|
| 176 | + $dbw->insert(
|
| 177 | + 'cv_ratings_usage',
|
| 178 | + array(
|
| 179 | + 'usg_category' => $rating['category'],
|
| 180 | + 'usg_title' => $rating['title'],
|
| 181 | + 'usg_article' => $articleDbKey,
|
| 182 | + )
|
| 183 | + );
|
| 184 | + }
|
| 185 | + return true;
|
| 186 | + }
|
| 187 | +
|
| 188 | + public static function renderScale( $input, $args, $parser ) {
|
| 189 | + global $wgUser, $wgTitle;
|
| 190 | + global $egCommunityVoiceResourcesPath;
|
| 191 | + // Validate and sanitize incoming arguments
|
| 192 | + $errors = array();
|
| 193 | + $error = false;
|
| 194 | + foreach ( array( 'category', 'title' ) as $argument ) {
|
| 195 | + if ( isset( $args[$argument] ) ) {
|
| 196 | + $args[$argument] = htmlspecialchars( $args[$argument] );
|
| 197 | + } else {
|
| 198 | + $error = true;
|
| 199 | + if ( $parser->getOptions()->getIsPreview() ) {
|
| 200 | + $errors[] = CommunityVoice::getMessage(
|
| 201 | + 'ratings', 'error-missing-argument', $argument
|
| 202 | + );
|
| 203 | + }
|
| 204 | + }
|
| 205 | + }
|
| 206 | + // Checks if an error ocurred
|
| 207 | + if ( $error ) {
|
| 208 | + // Checks if there are any error messages to return
|
| 209 | + if ( count( $errors ) ) {
|
| 210 | + return Html::div(
|
| 211 | + array( 'class' => 'error' ), implode( ' ', $errors )
|
| 212 | + );
|
| 213 | + }
|
| 214 | + // Continues without rendering
|
| 215 | + return true;
|
| 216 | + }
|
| 217 | + // Collects data
|
| 218 | + $totalVotes = self::getTotalVotes( $args['category'], $args['title'] );
|
| 219 | + $rating = self::getAverageRating( $args['category'], $args['title'] );
|
| 220 | + $userVoted = self::getUserVoted( $args['category'], $args['title'] );
|
| 221 | + // Builds sanitized HTML id with prepended module naming
|
| 222 | + $id = Html::toId(
|
| 223 | + 'cv_ratings_scale_' . $args['category'] . '_' . $args['title']
|
| 224 | + );
|
| 225 | + // Gets stats message
|
| 226 | + $stats = CommunityVoice::getMessage(
|
| 227 | + 'ratings', 'scale-stats', array( round( $rating, 1 ), $totalVotes )
|
| 228 | + );
|
| 229 | + // Begins rating scale
|
| 230 | + $htmlOut = Html::open(
|
| 231 | + 'div',
|
| 232 | + array( 'class' => 'communityvoice-ratings-scale', 'id' => $id )
|
| 233 | + );
|
| 234 | + // Checks for input
|
| 235 | + if ( $input != '' ) {
|
| 236 | + // Adds content of tag as parsed wiki-text
|
| 237 | + $htmlOut .= $parser->recursiveTagParse( $input );
|
| 238 | + }
|
| 239 | + // Checks if the user has not voted yet and is logged in
|
| 240 | + if ( !$userVoted && $wgUser->isLoggedIn() ) {
|
| 241 | +
|
| 242 | + /* Ajax Interaction */
|
| 243 | +
|
| 244 | + // Adds scale script
|
| 245 | + $htmlOut .= Html::script(
|
| 246 | + Js::callFunction(
|
| 247 | + 'communityVoice.ratings.scales.add',
|
| 248 | + Js::buildInstance(
|
| 249 | + 'CommunityVoiceRatingsScale',
|
| 250 | + array(
|
| 251 | + Js::toScalar( $id ),
|
| 252 | + Js::toScalar( $args['category'] ),
|
| 253 | + Js::toScalar( $args['title'] ),
|
| 254 | + Js::toScalar( $rating ),
|
| 255 | + Js::toObject(
|
| 256 | + array(
|
| 257 | + 'stats' => $stats,
|
| 258 | + 'status' => array(
|
| 259 | + 'null' => ' ',
|
| 260 | + 'sending' => CommunityVoice::getMessage(
|
| 261 | + 'ratings', 'scale-status-sending'
|
| 262 | + ),
|
| 263 | + 'error' => CommunityVoice::getMessage(
|
| 264 | + 'ratings', 'scale-status-error'
|
| 265 | + ),
|
| 266 | + 'thanks' => CommunityVoice::getMessage(
|
| 267 | + 'ratings', 'scale-status-thanks'
|
| 268 | + ),
|
| 269 | + )
|
| 270 | + )
|
| 271 | + ),
|
| 272 | + Js::toScalar( $wgTitle->getPrefixedText() )
|
| 273 | + )
|
| 274 | + )
|
| 275 | + )
|
| 276 | + );
|
| 277 | +
|
| 278 | + /* HTML Form Interaction */
|
| 279 | +
|
| 280 | + // Begins non-javascript fallback
|
| 281 | + $htmlOut .= Html::open( 'noscript' );
|
| 282 | + // Begins form
|
| 283 | + $specialPageTitle = Title::newFromText( 'Special:CommunityVoice' );
|
| 284 | + $htmlOut .= Html::open(
|
| 285 | + 'form',
|
| 286 | + array(
|
| 287 | + 'action' => $specialPageTitle->getFullUrl(),
|
| 288 | + 'method' => 'post',
|
| 289 | + )
|
| 290 | + );
|
| 291 | + // Builds list of hidden fields
|
| 292 | + $hiddenFields = array(
|
| 293 | + 'token' => $wgUser->editToken(),
|
| 294 | + 'module' => 'Ratings',
|
| 295 | + 'action' => 'ScaleVoteSubmission',
|
| 296 | + 'scale[article]' => $wgTitle->getPrefixedText(),
|
| 297 | + 'scale[category]' => $args['category'],
|
| 298 | + 'scale[title]' => $args['title'],
|
| 299 | + );
|
| 300 | + // Loops over each field
|
| 301 | + foreach ( $hiddenFields as $name => $value ) {
|
| 302 | + // Adds hidden field
|
| 303 | + $htmlOut .= Html::input(
|
| 304 | + array(
|
| 305 | + 'type' => 'hidden', 'name' => $name, 'value' => $value
|
| 306 | + )
|
| 307 | + );
|
| 308 | + }
|
| 309 | + // Loops 5 times (once per star)
|
| 310 | + for ( $i = 0; $i < 5; $i++ ) {
|
| 311 | + // Adds star as image input
|
| 312 | + $htmlOut .= Html::input(
|
| 313 | + array(
|
| 314 | + 'type' => 'image',
|
| 315 | + 'name' => 'scale[rating_' . $i . ']',
|
| 316 | + 'src' => sprintf(
|
| 317 | + '%s/Icons/star-%d.png',
|
| 318 | + $egCommunityVoiceResourcesPath,
|
| 319 | + self::getScaleFraction( $rating, $i )
|
| 320 | + ),
|
| 321 | + 'border' => 0,
|
| 322 | + 'alt' => '',
|
| 323 | + 'class' => 'star',
|
| 324 | + 'align' => 'absmiddle',
|
| 325 | + )
|
| 326 | + );
|
| 327 | + }
|
| 328 | + // Adds stats message
|
| 329 | + $htmlOut .= Html::tag( 'span', array( 'class' => 'stats' ), $stats );
|
| 330 | + // Ends form
|
| 331 | + $htmlOut .= Html::close( 'form' );
|
| 332 | + // Ends non-javascript fallback
|
| 333 | + $htmlOut .= Html::close( 'noscript' );
|
| 334 | + } else {
|
| 335 | +
|
| 336 | + /* No Interaction */
|
| 337 | +
|
| 338 | + // Loops 5 times (once per star)
|
| 339 | + for ( $i = 0; $i < 5; $i++ ) {
|
| 340 | + // Adds star as image
|
| 341 | + $htmlOut .= Html::tag(
|
| 342 | + 'img',
|
| 343 | + array(
|
| 344 | + 'src' => sprintf(
|
| 345 | + '%s/Icons/star-%d.png',
|
| 346 | + $egCommunityVoiceResourcesPath,
|
| 347 | + self::getScaleFraction( $rating, $i )
|
| 348 | + ),
|
| 349 | + 'border' => 0,
|
| 350 | + 'alt' => '',
|
| 351 | + 'class' => 'star',
|
| 352 | + 'align' => 'absmiddle',
|
| 353 | + )
|
| 354 | + );
|
| 355 | + }
|
| 356 | + // Adds stats message
|
| 357 | + $htmlOut .= Html::tag( 'span', array( 'class' => 'stats' ), $stats );
|
| 358 | + }
|
| 359 | + // Ends scale
|
| 360 | + $htmlOut .= Xml::closeElement( 'div' );
|
| 361 | + // Returns output
|
| 362 | + return $htmlOut;
|
| 363 | + }
|
| 364 | +
|
| 365 | + /* Processing Functions */
|
| 366 | +
|
| 367 | + /**
|
| 368 | + * Hanlder for ratings scale vote via ajax call
|
| 369 | + */
|
| 370 | + public static function handleScaleVoteCall( $category, $title, $rating, $article ) {
|
| 371 | + global $wgUser;
|
| 372 | + // Adds vote and checks for success
|
| 373 | + if ( self::addVote( $category, $title, $rating ) ) {
|
| 374 | + // Gets new rating data
|
| 375 | + $rating = self::getAverageRating( $category, $title );
|
| 376 | + // Builds result
|
| 377 | + $result = array(
|
| 378 | + 'rating' => $rating,
|
| 379 | + 'stats' => CommunityVoice::getMessage(
|
| 380 | + 'ratings',
|
| 381 | + 'scale-stats',
|
| 382 | + array(
|
| 383 | + round( $rating, 1 ),
|
| 384 | + self::getTotalVotes( $category, $title )
|
| 385 | + )
|
| 386 | + ),
|
| 387 | + );
|
| 388 | + // Gets articles that use this rating
|
| 389 | + $articles = self::getArticlesUsing( $category, $title );
|
| 390 | + // Loops over each article
|
| 391 | + foreach ( $articles as $article ) {
|
| 392 | + // Invalidates the cache of the article
|
| 393 | + CommunityVoice::touchArticle( $article );
|
| 394 | + }
|
| 395 | + // Ensure database commits take place (since this is an ajax call)
|
| 396 | + $dbw = wfGetDB( DB_MASTER );
|
| 397 | + $dbw->commit();
|
| 398 | + // Returns result
|
| 399 | + return Js::toObject( $result );
|
| 400 | + }
|
| 401 | + // Returns error information
|
| 402 | + return Js::toObject( array( 'rating' => - 1, 'stats' => null ) );
|
| 403 | + }
|
| 404 | +
|
| 405 | + /**
|
| 406 | + * Hanlder for ratings scale vote via HTML form submission
|
| 407 | + */
|
| 408 | + public static function handleScaleVoteSubmission() {
|
| 409 | + global $wgOut, $wgRequest;
|
| 410 | + // Gets scale data
|
| 411 | + $scale = $wgRequest->getArray( 'scale' );
|
| 412 | + // Checks if an article was given
|
| 413 | + if ( isset( $scale['article'], $scale['title'], $scale['category'] ) ) {
|
| 414 | + // Looks for rating value
|
| 415 | + foreach ( $scale as $key => $value ) {
|
| 416 | + // Breaks key into parts
|
| 417 | + $parts = explode( '_', $key );
|
| 418 | + // Checks if...
|
| 419 | + if (
|
| 420 | + // There's at least 2 parts
|
| 421 | + ( count( $parts ) > 1 ) &&
|
| 422 | + // The first part is 'rating'
|
| 423 | + ( $parts[0] == 'rating' ) &&
|
| 424 | + // The second part is a number
|
| 425 | + ( is_numeric( $parts[1] ) )
|
| 426 | + ) {
|
| 427 | + // Uses number as rating
|
| 428 | + $rating = $parts[1];
|
| 429 | + // Finishes loop
|
| 430 | + break;
|
| 431 | + }
|
| 432 | + }
|
| 433 | + // Checks if a rating was found
|
| 434 | + if ( isset( $rating ) ) {
|
| 435 | + // Adds vote and checks for success
|
| 436 | + if (
|
| 437 | + self::addVote(
|
| 438 | + $scale['category'], $scale['title'], $rating
|
| 439 | + )
|
| 440 | + ) {
|
| 441 | + // Gets articles that use this rating
|
| 442 | + $articles = self::getArticlesUsing(
|
| 443 | + $scale['category'], $scale['title']
|
| 444 | + );
|
| 445 | + // Loops over each article
|
| 446 | + foreach ( $articles as $article ) {
|
| 447 | + // Invalidates the cache of the article
|
| 448 | + CommunityVoice::touchArticle( $article );
|
| 449 | + }
|
| 450 | + // Redirects user back to article
|
| 451 | + $wgOut->redirect(
|
| 452 | + Title::newFromText( $scale['article'] )->getFullUrl()
|
| 453 | + );
|
| 454 | + } else {
|
| 455 | + throw new MWException( 'Voting failed!' );
|
| 456 | + }
|
| 457 | + } else {
|
| 458 | + throw new MWException( 'No rating parameter!' );
|
| 459 | + }
|
| 460 | + } else {
|
| 461 | + throw new MWException( 'Missing parameters!' );
|
| 462 | + }
|
| 463 | + }
|
| 464 | +
|
| 465 | + /* UI Functions */
|
| 466 | +
|
| 467 | + /**
|
| 468 | + * Outputs a summary UI for the module
|
| 469 | + */
|
| 470 | + public static function showSummary() {
|
| 471 | + global $wgOut;
|
| 472 | + //
|
| 473 | + $wgOut->addWikiText( '==== Categories ====' );
|
| 474 | + $xmlCategories = Html::open( 'ul' );
|
| 475 | + foreach ( self::getCategories() as $category ) {
|
| 476 | + $xmlCategories .= Html::tag( 'li', $category );
|
| 477 | + }
|
| 478 | + $xmlCategories .= Html::close( 'ul' );
|
| 479 | + $wgOut->addHtml( $xmlCategories );
|
| 480 | + }
|
| 481 | +
|
| 482 | + /**
|
| 483 | + * Outputs main UI for module
|
| 484 | + */
|
| 485 | + public static function showMain( $path ) {
|
| 486 | + global $wgOut;
|
| 487 | + //
|
| 488 | + $wgOut->addWikiText( '==== Detailed Information ====' );
|
| 489 | + }
|
| 490 | +}
|
Index: trunk/extensions/CommunityVoice/Resources/CommunityVoice.js |
— | — | @@ -6,14 +6,14 @@ |
7 | 7 | * Generic global access system |
8 | 8 | */ |
9 | 9 | function CommunityVoicePool() { |
10 | | - |
| 10 | + |
11 | 11 | /* Private Members */ |
12 | 12 | |
13 | 13 | var self = this; |
14 | 14 | var objects = {}; |
15 | | - |
| 15 | + |
16 | 16 | /* Public Functions */ |
17 | | - |
| 17 | + |
18 | 18 | /** |
19 | 19 | * Adds an object to pool, and returns it's unique ID |
20 | 20 | * @param object Object reference to add |
— | — | @@ -66,7 +66,7 @@ |
67 | 67 | article |
68 | 68 | ) { |
69 | 69 | /* Members */ |
70 | | - |
| 70 | + |
71 | 71 | var self = this; |
72 | 72 | // Gets object references |
73 | 73 | var element = document.getElementById( id ); |
— | — | @@ -75,13 +75,13 @@ |
76 | 76 | // Sets state |
77 | 77 | var status = null; |
78 | 78 | var locked = true; |
79 | | - |
| 79 | + |
80 | 80 | /* Functions */ |
81 | | - |
| 81 | + |
82 | 82 | this.getId = function() { |
83 | 83 | return id; |
84 | 84 | } |
85 | | - |
| 85 | + |
86 | 86 | this.rate = function( |
87 | 87 | newRating |
88 | 88 | ) { |
— | — | @@ -150,21 +150,21 @@ |
151 | 151 | // Updates UI |
152 | 152 | self.update(); |
153 | 153 | } |
154 | | - |
| 154 | + |
155 | 155 | this.lock = function() { |
156 | 156 | locked = true; |
157 | 157 | for ( star in stars ) { |
158 | 158 | stars[star].style.cursor = 'default'; |
159 | 159 | } |
160 | 160 | } |
161 | | - |
| 161 | + |
162 | 162 | this.unlock = function() { |
163 | 163 | locked = false; |
164 | 164 | for ( star in stars ) { |
165 | 165 | stars[star].style.cursor = 'pointer'; |
166 | 166 | } |
167 | 167 | } |
168 | | - |
| 168 | + |
169 | 169 | this.update = function( |
170 | 170 | hoveredStar |
171 | 171 | ) { |
— | — | @@ -192,7 +192,7 @@ |
193 | 193 | labels.status.innerHTML = messages.status[status]; |
194 | 194 | labels.status.className = status; |
195 | 195 | } |
196 | | - |
| 196 | + |
197 | 197 | // Loops 5 times (once per star) |
198 | 198 | for ( var i = 0; i < 5; i++ ) { |
199 | 199 | // Creates a new image |
— | — | @@ -240,4 +240,4 @@ |
241 | 241 | |
242 | 242 | var communityVoice = {}; |
243 | 243 | communityVoice.ratings = {}; |
244 | | -communityVoice.ratings.scales = new CommunityVoicePool(); |
\ No newline at end of file |
| 244 | +communityVoice.ratings.scales = new CommunityVoicePool(); |
Property changes on: trunk/extensions/CommunityVoice/Resources/CommunityVoice.js |
___________________________________________________________________ |
Added: svn:eol-style |
245 | 245 | + native |
Property changes on: trunk/extensions/CommunityVoice/Resources/Icons/star-0.png |
___________________________________________________________________ |
Modified: svn:mime-type |
246 | 246 | - application/octet-stream |
247 | 247 | + image/png |
Property changes on: trunk/extensions/CommunityVoice/Resources/Icons/star-0-hover.png |
___________________________________________________________________ |
Modified: svn:mime-type |
248 | 248 | - application/octet-stream |
249 | 249 | + image/png |
Property changes on: trunk/extensions/CommunityVoice/Resources/Icons/star-1.png |
___________________________________________________________________ |
Modified: svn:mime-type |
250 | 250 | - application/octet-stream |
251 | 251 | + image/png |
Property changes on: trunk/extensions/CommunityVoice/Resources/Icons/star-1-hover.png |
___________________________________________________________________ |
Modified: svn:mime-type |
252 | 252 | - application/octet-stream |
253 | 253 | + image/png |
Property changes on: trunk/extensions/CommunityVoice/Resources/Icons/star-2.png |
___________________________________________________________________ |
Modified: svn:mime-type |
254 | 254 | - application/octet-stream |
255 | 255 | + image/png |
Property changes on: trunk/extensions/CommunityVoice/Resources/Icons/star-2-hover.png |
___________________________________________________________________ |
Modified: svn:mime-type |
256 | 256 | - application/octet-stream |
257 | 257 | + image/png |
Property changes on: trunk/extensions/CommunityVoice/Resources/Icons/star-3.png |
___________________________________________________________________ |
Modified: svn:mime-type |
258 | 258 | - application/octet-stream |
259 | 259 | + image/png |
Property changes on: trunk/extensions/CommunityVoice/Resources/Icons/star-3-hover.png |
___________________________________________________________________ |
Modified: svn:mime-type |
260 | 260 | - application/octet-stream |
261 | 261 | + image/png |
Property changes on: trunk/extensions/CommunityVoice/Resources/Icons/star-4.png |
___________________________________________________________________ |
Modified: svn:mime-type |
262 | 262 | - application/octet-stream |
263 | 263 | + image/png |
Property changes on: trunk/extensions/CommunityVoice/Resources/Icons/star-4-hover.png |
___________________________________________________________________ |
Modified: svn:mime-type |
264 | 264 | - application/octet-stream |
265 | 265 | + image/png |
Property changes on: trunk/extensions/CommunityVoice/Resources/Icons/star-5.png |
___________________________________________________________________ |
Modified: svn:mime-type |
266 | 266 | - application/octet-stream |
267 | 267 | + image/png |
Property changes on: trunk/extensions/CommunityVoice/Resources/Icons/star-5-hover.png |
___________________________________________________________________ |
Modified: svn:mime-type |
268 | 268 | - application/octet-stream |
269 | 269 | + image/png |
Property changes on: trunk/extensions/CommunityVoice/Resources/Icons/star-6.png |
___________________________________________________________________ |
Modified: svn:mime-type |
270 | 270 | - application/octet-stream |
271 | 271 | + image/png |
Property changes on: trunk/extensions/CommunityVoice/Resources/Icons/star-6-hover.png |
___________________________________________________________________ |
Modified: svn:mime-type |
272 | 272 | - application/octet-stream |
273 | 273 | + image/png |
Property changes on: trunk/extensions/CommunityVoice/Resources/Icons/star.svg |
___________________________________________________________________ |
Added: svn:eol-style |
274 | 274 | + native |
Property changes on: trunk/extensions/CommunityVoice/Resources/CommunityVoice.css |
___________________________________________________________________ |
Added: svn:eol-style |
275 | 275 | + native |
Index: trunk/extensions/CommunityVoice/CommunityVoice.page.php |
— | — | @@ -16,15 +16,13 @@ |
17 | 17 | |
18 | 18 | // Initializes special page |
19 | 19 | parent::__construct( 'CommunityVoice' ); |
20 | | - // Loads extension messages |
21 | | - wfLoadExtensionMessages( 'CommunityVoice' ); |
22 | 20 | } |
23 | 21 | |
24 | | - public function execute( |
25 | | - $sub |
26 | | - ) { |
| 22 | + public function execute( $sub ) { |
27 | 23 | global $wgOut, $wgRequest, $wgUser; |
28 | 24 | |
| 25 | + wfLoadExtensionMessages( 'CommunityVoice' ); |
| 26 | + |
29 | 27 | /* Control */ |
30 | 28 | |
31 | 29 | // Gets edit token |
— | — | @@ -72,7 +70,7 @@ |
73 | 71 | return true; |
74 | 72 | } |
75 | 73 | // Modules summary view |
76 | | - foreach( CommunityVoice::getModules() as $module ) { |
| 74 | + foreach ( CommunityVoice::getModules() as $module ) { |
77 | 75 | // Adds heading |
78 | 76 | $wgOut->addWikiText( |
79 | 77 | '== ' . wfMsg( 'communityvoice-' . $module ) . ' ==' |
— | — | @@ -84,4 +82,4 @@ |
85 | 83 | return true; |
86 | 84 | } |
87 | 85 | |
88 | | -} |
\ No newline at end of file |
| 86 | +} |
Property changes on: trunk/extensions/CommunityVoice/CommunityVoice.page.php |
___________________________________________________________________ |
Added: svn:eol-style |
89 | 87 | + native |
Index: trunk/extensions/CommunityVoice/CommunityVoice.i18n.php |
— | — | @@ -21,7 +21,7 @@ |
22 | 22 | 'communityvoice-ratings-scale-status-sending' => 'Sending...', |
23 | 23 | 'communityvoice-ratings-scale-status-error' => 'Error sending!', |
24 | 24 | 'communityvoice-ratings-scale-status-thanks' => 'Thanks for voting!', |
25 | | - 'communityvoice-ratings-scale-stats' =>'$1 / 5 ($2 {{PLURAL:$2|vote|votes}} cast)', |
| 25 | + 'communityvoice-ratings-scale-stats' => '$1 / 5 ($2 {{PLURAL:$2|vote|votes}} cast)', |
26 | 26 | 'communityvoice-ratings-error-no-category' => 'Category attribute missing in rating tag.', |
27 | 27 | 'communityvoice-ratings-error-no-title' => 'Title attribute missing in rating tag.', |
28 | | -); |
\ No newline at end of file |
| 28 | +); |
Property changes on: trunk/extensions/CommunityVoice/CommunityVoice.i18n.php |
___________________________________________________________________ |
Added: svn:eol-style |
29 | 29 | + native |
Index: trunk/extensions/CommunityVoice/CommunityVoice.php |
— | — | @@ -37,25 +37,48 @@ |
38 | 38 | 'name' => 'CommunityVoice', |
39 | 39 | 'author' => 'Trevor Parscal', |
40 | 40 | 'url' => 'http://www.mediawiki.org/wiki/Extension:CommunityVoice', |
41 | | - 'description-msg' => 'communityvoice-desc', |
| 41 | + 'descriptionmsg' => 'communityvoice-desc', |
| 42 | + 'version' => '0.1.0', |
42 | 43 | ); |
| 44 | + |
43 | 45 | // Shortcut to this extension directory |
44 | 46 | $dir = dirname( __FILE__ ) . '/'; |
| 47 | + |
45 | 48 | // Internationalization |
46 | 49 | $wgExtensionMessagesFiles['CommunityVoice'] = $dir . 'CommunityVoice.i18n.php'; |
| 50 | +$wgExtensionAliasesFiles['CommunityVoice'] = $dir . 'CommunityVoice.alias.php'; |
| 51 | + |
47 | 52 | // Class Autoloading |
48 | 53 | $wgAutoloadClasses['CommunityVoice'] = $dir . 'CommunityVoice.php'; |
49 | 54 | $wgAutoloadClasses['CommunityVoicePage'] = $dir . 'CommunityVoice.page.php'; |
50 | 55 | $wgAutoloadClasses['CommunityVoiceRatings'] = $dir . 'Modules/Ratings.php'; |
51 | | -// Spacial Pages |
| 56 | + |
| 57 | +// Special Pages |
52 | 58 | $wgSpecialPages['CommunityVoice'] = 'CommunityVoicePage'; |
| 59 | +$wgSpecialPageGroups['CommunityVoice'] = 'wiki'; |
| 60 | + |
53 | 61 | // Setup Hooks |
54 | 62 | $wgExtensionFunctions[] = 'CommunityVoice::registerModules'; |
55 | 63 | $wgHooks['AjaxAddScript'][] = 'CommunityVoice::addScripts'; |
56 | 64 | $wgHooks['BeforePageDisplay'][] = 'CommunityVoice::addStyles'; |
| 65 | +$wgHooks['LoadExtensionSchemaUpdates'][] = 'efCheckSchema'; |
57 | 66 | |
| 67 | +function efCheckSchema() { |
| 68 | + // Get a connection |
| 69 | + $db = wfGetDB( DB_MASTER ); |
| 70 | + // Create table if it doesn't exist |
| 71 | + if ( !$db->tableExists( 'cv_ratings_votes' ) ) { |
| 72 | + $db->sourceFile( dirname( __FILE__ ) . '/CommunityVoice.sql' ); |
| 73 | + } |
| 74 | + if ( !$db->tableExists( 'cv_ratings_usage' ) ) { |
| 75 | + $db->sourceFile( dirname( __FILE__ ) . '/CommunityVoice.sql' ); |
| 76 | + } |
| 77 | + // Continue |
| 78 | + return true; |
| 79 | +} |
| 80 | + |
58 | 81 | /* Classes */ |
59 | | - |
| 82 | +// FIXME: classes should be put in their own files |
60 | 83 | abstract class CommunityVoice { |
61 | 84 | |
62 | 85 | /* Static Members */ |
— | — | @@ -71,11 +94,7 @@ |
72 | 95 | return array_keys( self::$modules ); |
73 | 96 | } |
74 | 97 | |
75 | | - public static function callModuleAction( |
76 | | - $module, |
77 | | - $type, |
78 | | - $action = '' |
79 | | - ) { |
| 98 | + public static function callModuleAction( $module, $type, $action = '' ) { |
80 | 99 | // Checks for class |
81 | 100 | if ( isset( self::$modules[$module] ) ) { |
82 | 101 | if ( class_exists( self::$modules[$module]['class'] ) ) { |
— | — | @@ -111,17 +130,13 @@ |
112 | 131 | */ |
113 | 132 | public static function registerModules() { |
114 | 133 | // Loops over each module |
115 | | - foreach( self::getModules() as $module ) { |
| 134 | + foreach ( self::getModules() as $module ) { |
116 | 135 | self::callModuleAction( $module, 'register' ); |
117 | 136 | } |
118 | 137 | return true; |
119 | 138 | } |
120 | 139 | |
121 | | - public static function getMessage( |
122 | | - $module, |
123 | | - $message, |
124 | | - $parameter = null |
125 | | - ) { |
| 140 | + public static function getMessage( $module, $message, $parameter = null ) { |
126 | 141 | // Checks if extension messages have been loaded already |
127 | 142 | if ( !self::$messagesLoaded ) { |
128 | 143 | // Loads extension messages |
— | — | @@ -132,9 +147,7 @@ |
133 | 148 | return wfMsg( 'communityvoice-' . $module . '-' . $message, $parameter ); |
134 | 149 | } |
135 | 150 | |
136 | | - public static function touchArticle( |
137 | | - $article |
138 | | - ) { |
| 151 | + public static function touchArticle( $article ) { |
139 | 152 | // Gets the title of the article which included the scale |
140 | 153 | $articleTitle = Title::newFromText( $article ); |
141 | 154 | // Invalidates the cache of the article |
— | — | @@ -144,11 +157,10 @@ |
145 | 158 | /** |
146 | 159 | * Adds scripts to document |
147 | 160 | */ |
148 | | - public static function addScripts( |
149 | | - $out |
150 | | - ) { |
| 161 | + public static function addScripts( $out ) { |
151 | 162 | global $wgJsMimeType; |
152 | 163 | global $egCommunityVoiceResourcesPath; |
| 164 | + |
153 | 165 | $out->addInlineScript( |
154 | 166 | sprintf( |
155 | 167 | "var egCommunityVoiceResourcesPath = '%s';\n" , |
— | — | @@ -186,4 +198,4 @@ |
187 | 199 | ); |
188 | 200 | return true; |
189 | 201 | } |
190 | | -} |
\ No newline at end of file |
| 202 | +} |
Property changes on: trunk/extensions/CommunityVoice/CommunityVoice.php |
___________________________________________________________________ |
Added: svn:eol-style |
191 | 203 | + native |