http://www.mediawiki.org/wiki/Special:Code/MediaWiki/87294
Revision: 87294 Author: janpaul123 Date: 2011-05-02 20:34:52 +0000 (Mon, 02 May 2011) Log Message: ----------- Added first very experimental -- but working! -- version of the WikiLove extension. Modified Paths: -------------- trunk/extensions/WikiLove/WikiLove.i18n.php trunk/extensions/WikiLove/WikiLove.php Added Paths: ----------- trunk/extensions/WikiLove/WikiLove.api.php trunk/extensions/WikiLove/WikiLove.hooks.php trunk/extensions/WikiLove/images/heart-hover.png trunk/extensions/WikiLove/images/heart-icons.png trunk/extensions/WikiLove/images/spinner.gif trunk/extensions/WikiLove/images/tab-break.png trunk/extensions/WikiLove/jquery.elastic.js trunk/extensions/WikiLove/patches/ trunk/extensions/WikiLove/patches/WikiLoveLog.sql trunk/extensions/WikiLove/wikiLove.css trunk/extensions/WikiLove/wikiLove.js Removed Paths: ------------- trunk/extensions/WikiLove/WikiLove.sql Added: trunk/extensions/WikiLove/WikiLove.api.php =================================================================== --- trunk/extensions/WikiLove/WikiLove.api.php (rev 0) +++ trunk/extensions/WikiLove/WikiLove.api.php 2011-05-02 20:34:52 UTC (rev 87294) @@ -0,0 +1,126 @@ +<?php +class WikiLoveApi extends ApiBase { + public function execute() { + global $wgRequest; + + $params = $this->extractRequestParams(); + + $title = Title::newFromText( $params['title'] ); + if ( is_null( $title ) ) { + // error + } + + $talk = WikiLoveHooks::getUserTalkPage( $title ); + if ( is_null( $talk ) ) { + // error + } + + if ( stripos( $params['text'], $params['template'] ) === false ) { + // error + } + + $api = new ApiMain( new FauxRequest( array( + 'action' => 'edit', + 'title' => $talk->getFullText(), + 'section' => 'new', + 'text' => $params['text'], + 'token' => $params['token'], + 'summary' => $params['subject'], + 'notminor' => true, + ), false, array( 'wsEditToken' => $wgRequest->getSessionData( 'wsEditToken' ) ) ), true ); + $api->execute(); + + $result = $api->getResult(); + $data = $result->getData(); + + $talk->setFragment( '#' . $params['subject'] ); + $this->getResult()->addValue( 'redirect', 'pageName', $talk->getPrefixedDBkey() ); + $this->getResult()->addValue( 'redirect', 'fragment', $talk->getFragmentForURL() ); + + $this->saveInDb( $talk, $params['subject'], $params['text'], $params['type'], $params['template'] ); + } + + private function saveInDb( $talk, $subject, $text, $type, $template ) { + global $wgUser, $wgSitename; + $dbw = wfGetDB( DB_MASTER ); + $values = array( + 'wl_timestamp' => $dbw->timestamp(), + 'wl_sender_id' => $wgUser->getId(), + 'wl_receiver_id' => User::newFromName( $talk->getSubjectPage()->getBaseText() )->getId(), + 'wl_wiki' => $wgSitename, + 'wl_type' => $type, + 'wl_template' => $template, + 'wl_subject' => $subject, + 'wl_message' => $text, + 'wl_email' => 0, + ); + $dbw->insert( 'wikilove_log', $values, __METHOD__ ); + } + + public function getAllowedParams() { + return array( + 'title' => array( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_REQUIRED => true, + ), + 'text' => array( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_REQUIRED => true, + ), + 'token' => array( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_REQUIRED => true, + ), + 'subject' => array( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_REQUIRED => true, + ), + 'template' => array( + ApiBase::PARAM_TYPE => 'string', + ), + 'type' => array( + ApiBase::PARAM_TYPE => 'string', + ), + ); + } + + public function getParamDescription() { + return array( + 'title' => 'Title of the user or user talk page to send WikiLove to', + 'text' => 'Wikitext to add in the new section', + 'token' => 'Edit token. You can get one of these through prop=info', + 'subject' => 'Subject header of the new section', + 'template' => 'Template used in the wikitext (for statistics)', + 'type' => 'Type of WikiLove (for statistics)', + ); + } + + public function getDescription() { + return array( + 'Give WikiLove to another user.', + "WikiLove is a positive message posted to a user's talk page through a", + 'convenient interface with preset images and templates. For statistical', + 'purposes, the type and template (among the other data) are logged.', + ); + } + + /* + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'code' => 'permissiondenied', 'info' => 'You don\'t have permission to view code diffs' ), + array( 'code' => 'invalidrepo', 'info' => "Invalid repo ``repo''" ), + array( 'code' => 'nosuchrev', 'info' => 'There is no revision with ID \'rev\'' ), + ) ); + } + */ + + public function getExamples() { + return array( + 'api.php?action=wikiLove&title=User:Dummy&text=Love&subject=Hi&token=%2B\\', + ); + } + + public function getVersion() { + return __CLASS__ . ': $Id$'; + } +} Property changes on: trunk/extensions/WikiLove/WikiLove.api.php ___________________________________________________________________ Added: svn:keywords + Id Added: svn:eol-style + native Added: trunk/extensions/WikiLove/WikiLove.hooks.php =================================================================== --- trunk/extensions/WikiLove/WikiLove.hooks.php (rev 0) +++ trunk/extensions/WikiLove/WikiLove.hooks.php 2011-05-02 20:34:52 UTC (rev 87294) @@ -0,0 +1,126 @@ +<?php +/** + * Hooks for WikiLove extension + * + * @file + * @ingroup Extensions + */ + +class WikiLoveHooks { + /** + * LoadExtensionSchemaUpdates hook + */ + public static function loadExtensionSchemaUpdates( $updater = null ) { + if ( $updater === null ) { + global $wgExtNewTables; + $wgExtNewTables[] = array( 'wikilove_log', dirname( __FILE__ ) . '/patches/WikiLoveLog.sql' ); + } else { + $updater->addExtensionUpdate( array( 'addTable', 'wikilove_log', + dirname( __FILE__ ) . '/patches/WikiLoveLog.sql', true ) ); + } + return true; + } + + /** + * Add the preference in the user preferences with the GetPreferences hook. + * @param $user + * @param $preferences + */ + public static function getPreferences( $user, &$preferences ) { + global $wgWikiLoveGlobal; + if ( !$wgWikiLoveGlobal ) { + $preferences['wikilove-enabled'] = array( + 'type' => 'check', + 'section' => 'editing/labs', + 'label-message' => 'wikilove-enable-preference', + ); + } + return true; + } + + /** + * Adds the required module and edit token JS if we are on a user (talk) page. + */ + public static function beforePageDisplay( $out, $skin ) { + global $wgWikiLoveGlobal, $wgUser; + if ( !$wgWikiLoveGlobal && !$wgUser->getOption( 'wikilove-enabled' ) ) return true; + + $title = self::getUserTalkPage( $skin->getTitle() ); + if ( !is_null( $title ) ) { + $out->addModules( 'ext.wikiLove' ); + $out->addInlineScript( + 'jQuery( document ).ready( function() { + jQuery.wikiLove.editToken = ' . FormatJson::encode( $wgUser->edittoken() ) . '; + } );' + ); + } + return true; + } + + /** + * Adds a tab the old way (before MW 1.18) + */ + public static function skinTemplateTabs( $skin, &$contentActions ) { + self::skinConfigViewsLinks( $skin, $contentActions ); + return true; + } + + /** + * Adds a tab or an icon the new way (MW >1.18) + */ + public static function skinTemplateNavigation( &$skin, &$links ) { + if ( self::showIcon( $skin ) ) { + self::skinConfigViewsLinks( $skin, $links['views']); + } + else { + self::skinConfigViewsLinks( $skin, $links['actions']); + } + return true; + } + + /** + * Configure views links. + * Helper function for SkinTemplateTabs and SkinTemplateNavigation hooks + * to configure views links. + */ + private static function skinConfigViewsLinks( $skin, &$views ) { + global $wgWikiLoveGlobal, $wgUser; + if ( !$wgWikiLoveGlobal && !$wgUser->getOption( 'wikilove-enabled' ) ) return true; + + if ( !is_null( self::getUserTalkPage( $skin->getTitle() ) ) ) { + $views['wikilove'] = array( + 'text' => wfMsg( 'wikilove-tab-text' ), + 'href' => '#', + ); + if ( self::showIcon( $skin ) ) { + $views['wikilove']['class'] = 'icon'; + $views['wikilove']['primary'] = true; + } + } + } + + /** + * Only show an icon when the global preference is enabled and the current skin is Vector. + */ + private static function showIcon( $skin ) { + global $wgWikiLoveTabIcon; + return $wgWikiLoveTabIcon && $skin->getSkinName() == 'vector'; + } + + /** + * Find the editable talk page of the user we're looking at, or null + * if such page does not exist. + */ + public static function getUserTalkPage( $title ) { + global $wgUser; + if ( !$wgUser->isLoggedIn() ) return null; + + $ns = $title->getNamespace(); + if ( $ns == NS_USER_TALK && $title->quickUserCan( 'edit' ) ) return $title; + elseif ( $ns == NS_USER ) { + $talk = $title->getTalkPage(); + if ( $talk->quickUserCan( 'edit' ) ) return $talk; + } + return null; + } +} \ No newline at end of file Property changes on: trunk/extensions/WikiLove/WikiLove.hooks.php ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/extensions/WikiLove/WikiLove.i18n.php =================================================================== --- trunk/extensions/WikiLove/WikiLove.i18n.php 2011-05-02 20:30:38 UTC (rev 87293) +++ trunk/extensions/WikiLove/WikiLove.i18n.php 2011-05-02 20:34:52 UTC (rev 87294) @@ -9,10 +9,22 @@ $messages = array(); /** English - * @author Ryan Kaldari + * @author Ryan Kaldari, Jan Paul Posma */ $messages['en'] = array( 'wikilove-desc' => 'Adds an interface for facilitating positive user feedback to user talk pages', 'wikilove' => 'WikiLove', + 'wikilove-enable-preference' => 'Enable showing appreciation for other users with the WikiLove tab (experimental)', + 'wikilove-tab-text' => 'Show appreciation', + 'tooltip-ca-wikilove' => 'Post a message for this user showing your appreciation', + 'wikilove-dialog-title' => 'WikiLove', + 'wikilove-select-type' => 'Select Type', + 'wikilove-add-details' => 'Add Details', + 'wikilove-title' => 'Title:', + 'wikilove-enter-message' => 'Enter a message:', + 'wikilove-omit-sig' => '(without a signature)', + 'wikilove-button-preview' => 'Preview', + 'wikilove-preview' => 'Preview', + 'wikilove-button-send' => 'Send WikiLove', + 'wikilove-type-makeyourown' => 'Make your own', ); - Modified: trunk/extensions/WikiLove/WikiLove.php =================================================================== --- trunk/extensions/WikiLove/WikiLove.php 2011-05-02 20:30:38 UTC (rev 87293) +++ trunk/extensions/WikiLove/WikiLove.php 2011-05-02 20:34:52 UTC (rev 87294) @@ -21,7 +21,7 @@ /** * @file * @ingroup Extensions - * @author Ryan Kaldari + * @author Ryan Kaldari, Jan Paul Posma */ # Alert the user that this is not a valid entry point to MediaWiki if they try to access the file directly. @@ -40,29 +40,64 @@ 'version' => '0.1', 'url' => 'http://www.mediawiki.org/wiki/Extension:WikiLove', 'author' => array( - 'Ryan Kaldari' + 'Ryan Kaldari', 'Jan Paul Posma' ), 'descriptionmsg' => 'wikilove-desc', ); +// current directory including trailing slash $dir = dirname( __FILE__ ) . '/'; -$wgHooks['LoadExtensionSchemaUpdates'][] = 'efWikiLoveSchema'; +// add autoload classes +$wgAutoloadClasses['WikiLoveApi'] = $dir . 'WikiLove.api.php'; +$wgAutoloadClasses['WikiLoveHooks'] = $dir . 'WikiLove.hooks.php'; -$wgExtensionMessagesFiles['WikiLove'] = $dir . 'WikiLove.i18n.php'; +// i18n messages +$wgExtensionMessagesFiles['WikiLove'] = $dir . 'WikiLove.i18n.php'; -function efWikiLoveSchema( $updater = null ) { - $dir = dirname( __FILE__ ) . '/'; - if ( $updater === null ) { - global $wgDBtype, $wgExtNewTables; +// register hooks +$wgHooks['GetPreferences'][] = 'WikiLoveHooks::getPreferences'; +$wgHooks['SkinTemplateNavigation'][] = 'WikiLoveHooks::skinTemplateNavigation'; +$wgHooks['SkinTemplateTabs'][] = 'WikiLoveHooks::skinTemplateTabs'; +$wgHooks['BeforePageDisplay'][] = 'WikiLoveHooks::beforePageDisplay'; +//$wgHooks['LoadExtensionSchemaUpdates'][] = 'WikiLoveHooks::loadExtensionSchemaUpdates'; +// Not a final schema, please apply patches/WikiLoveLog.sql manually for now! - if ( $wgDBtype == 'mysql' ) { - $wgExtNewTables[] = array( 'wikilove_log', $dir . 'WikiLove.sql' ); - } - } else { - if ( $updater->getDB()->getType() == 'mysql' ) { - $updater->addExtensionUpdate( array( 'addTable', 'wikilove_log', $dir . 'WikiLove.sql', true ) ); - } - } - return true; -} +// api modules +$wgAPIModules['wikiLove'] = 'WikiLoveApi'; + +// default user options +$wgWikiLoveTabIcon = true; + +// resources +$wikiLoveTpl = array( + 'localBasePath' => dirname( __FILE__ ), + 'remoteExtPath' => 'WikiLove', + 'group' => 'ext.wikiLove', +); + +$wgResourceModules += array( + 'ext.wikiLove' => $wikiLoveTpl + array( + 'scripts' => 'wikiLove.js', + 'styles' => 'wikiLove.css', + 'messages' => array( + 'wikilove-dialog-title', + 'wikilove-select-type', + 'wikilove-add-details', + 'wikilove-title', + 'wikilove-enter-message', + 'wikilove-omit-sig', + 'wikilove-button-preview', + 'wikilove-preview', + 'wikilove-button-send', + 'wikilove-type-makeyourown', + ), + 'dependencies' => array( + 'jquery.ui.dialog', + 'jquery.elastic', + ), + ), + 'jquery.elastic' => $wikiLoveTpl + array( + 'scripts' => 'jquery.elastic.js', + ), +); Deleted: trunk/extensions/WikiLove/WikiLove.sql =================================================================== --- trunk/extensions/WikiLove/WikiLove.sql 2011-05-02 20:30:38 UTC (rev 87293) +++ trunk/extensions/WikiLove/WikiLove.sql 2011-05-02 20:34:52 UTC (rev 87294) @@ -1,11 +0,0 @@ -CREATE TABLE IF NOT EXISTS /*$wgDBprefix*/wikilove_log ( - `wl_id` int NOT NULL PRIMARY KEY auto_increment, - `wl_timestamp` char(14) NOT NULL, - `wl_sender_id` int(11) NOT NULL, - `wl_receiver_id` int(11) NOT NULL, - `wl_wiki` varchar(64) NOT NULL, - `wl_type` varchar(64) NOT NULL, - `wl_template` varchar(64) NOT NULL, - `wl_message` varchar(255) NOT NULL, - `wl_email` bool NOT NULL default '0' -) /*$wgDBTableOptions*/; Added: trunk/extensions/WikiLove/images/heart-hover.png =================================================================== (Binary files differ) Property changes on: trunk/extensions/WikiLove/images/heart-hover.png ___________________________________________________________________ Added: svn:mime-type + image/png Added: trunk/extensions/WikiLove/images/heart-icons.png =================================================================== (Binary files differ) Property changes on: trunk/extensions/WikiLove/images/heart-icons.png ___________________________________________________________________ Added: svn:mime-type + image/png Added: trunk/extensions/WikiLove/images/spinner.gif =================================================================== (Binary files differ) Property changes on: trunk/extensions/WikiLove/images/spinner.gif ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: trunk/extensions/WikiLove/images/tab-break.png =================================================================== (Binary files differ) Property changes on: trunk/extensions/WikiLove/images/tab-break.png ___________________________________________________________________ Added: svn:mime-type + image/png Added: trunk/extensions/WikiLove/jquery.elastic.js =================================================================== --- trunk/extensions/WikiLove/jquery.elastic.js (rev 0) +++ trunk/extensions/WikiLove/jquery.elastic.js 2011-05-02 20:34:52 UTC (rev 87294) @@ -0,0 +1,6 @@ +(function(jQuery){jQuery.fn.extend({elastic:function(){var mimics=['paddingTop','paddingRight','paddingBottom','paddingLeft','fontSize','lineHeight','fontFamily','width','fontWeight'];return this.each(function(){if(this.type!='textarea'){return false;} +var $textarea=jQuery(this),$twin=jQuery('<div />').css({'position':'absolute','display':'none','word-wrap':'break-word'}),lineHeight=parseInt($textarea.css('line-height'),10)||parseInt($textarea.css('font-size'),'10'),minheight=parseInt($textarea.css('height'),10)||lineHeight*3,maxheight=parseInt($textarea.css('max-height'),10)||Number.MAX_VALUE,goalheight=0,i=0;if(maxheight<0){maxheight=Number.MAX_VALUE;} +$twin.appendTo($textarea.parent());var i=mimics.length;while(i--){$twin.css(mimics[i].toString(),$textarea.css(mimics[i].toString()));} +function setHeightAndOverflow(height,overflow){curratedHeight=Math.floor(parseInt(height,10));if($textarea.height()!=curratedHeight){$textarea.css({'height':curratedHeight+'px','overflow':overflow});}} +function update(){var textareaContent=$textarea.val().replace(/&/g,'&').replace(/ /g,' ').replace(/<|>/g,'>').replace(/\n/g,'<br />');var twinContent=$twin.html();if(textareaContent+' '!=twinContent){$twin.html(textareaContent+' ');if(Math.abs($twin.height()+lineHeight-$textarea.height())>3){var goalheight=$twin.height()+lineHeight;if(goalheight>=maxheight){setHeightAndOverflow(maxheight,'auto');}else if(goalheight<=minheight){setHeightAndOverflow(minheight,'hidden');}else{setHeightAndOverflow(goalheight,'hidden');}}}} +$textarea.css({'overflow':'hidden'});$textarea.keyup(function(){update();});$textarea.live('input paste',function(e){setTimeout(update,250);});update();});}});})(jQuery); \ No newline at end of file Property changes on: trunk/extensions/WikiLove/jquery.elastic.js ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/extensions/WikiLove/patches/WikiLoveLog.sql =================================================================== --- trunk/extensions/WikiLove/patches/WikiLoveLog.sql (rev 0) +++ trunk/extensions/WikiLove/patches/WikiLoveLog.sql 2011-05-02 20:34:52 UTC (rev 87294) @@ -0,0 +1,17 @@ +-- +-- WikiLove logging schema +-- Not final, please apply this patch manually for now! +-- + +CREATE TABLE IF NOT EXISTS /*_*/wikilove_log ( + `wl_id` int NOT NULL PRIMARY KEY auto_increment, + `wl_timestamp` binary(14) NOT NULL, + `wl_sender_id` int(11) NOT NULL, + `wl_receiver_id` int(11) NOT NULL, + `wl_wiki` varchar(64) NOT NULL, + `wl_type` varchar(64) NOT NULL, + `wl_template` varchar(64) NOT NULL, + `wl_subject` blob NOT NULL, + `wl_message` blob NOT NULL, + `wl_email` bool NOT NULL default '0' +) /*$wgDBTableOptions*/; Property changes on: trunk/extensions/WikiLove/patches/WikiLoveLog.sql ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/extensions/WikiLove/wikiLove.css =================================================================== --- trunk/extensions/WikiLove/wikiLove.css (rev 0) +++ trunk/extensions/WikiLove/wikiLove.css 2011-05-02 20:34:52 UTC (rev 87294) @@ -0,0 +1,149 @@ +/* include fix from r87101 here for older versions */ +div.vectorTabs span { + display: inline-block; + /* @embed */ + background-image: url(images/tab-break.png); + background-position: bottom right; + background-repeat: no-repeat; +} + +div.vectorTabs li a { + background-image: none; +} + +#ca-unwatch.icon, +#ca-watch.icon { + margin-right: 0px; +} + +/* icon style */ +#ca-wikilove.icon a { + margin: 0; + padding: 0; + outline: none; + display: block; + width: 26px; + /* This hides the text but shows the background image */ + padding-top: 3.1em; + margin-top: 0; + /* Only applied in IE6 */ + margin-top: -0.8em !ie; + height: 0; + overflow: hidden; + /* @embed */ + background-image: url(images/heart-icons.png); +} +#ca-wikilove.icon a { + background-position: 5px 60%; +} +#ca-wikilove.icon a:hover, +#ca-wikilove.icon a:focus { + background-position: -19px 60%; +} + +/* dialog */ +#wikiLoveDialog { + margin: 10px; +} + +/* dialog type selection */ +#wikiLoveDialog #wlSelectType { + float: left; + width: 250px; +} + +#wikiLoveDialog #wlSelectType ul { + list-style: none; + margin: 0; + padding: 0; +} + +#wikiLoveDialog #wlSelectType ul li { + display: block; + background-color: #3060b0; + width: 250px; + padding: 10px 0; + + /* create a hand cursor, cross-browser hack: http://www.quirksmode.org/css/cursor.html */ + cursor: pointer; + cursor: hand; +} + +/* IGNORED BY IE6 */ +#wikiLoveDialog #wlSelectType ul > li { + display: inline-block; +} + +#wikiLoveDialog #wlSelectType ul li:hover, +#wikiLoveDialog #wlSelectType ul li:focus { + background-color: #3366bb; +} + +#wikiLoveDialog #wlSelectType ul li.selected { + background-color: #e46020; +} + +#wikiLoveDialog #wlSelectType ul li span { + font-size: 1.2em; + font-weight: bold; + text-decoration: none; + color: white; + margin-left: 10px; +} + +/* dialog add details */ +#wikiLoveDialog #wlAddDetails { + float: right; + width: 480px; +} + +#wikiLoveDialog #wlAddDetails label { + font-weight: bold; + font-size: 1.1em; +} + +#wikiLoveDialog #wlAddDetails .text { + display: block; + width: 300px; +} + +#wikiLoveDialog #wlAddDetails .wlOmitSig { + font-weight: light; + font-size: 0.9em; + float: right; + color: #999; +} + +#wikiLoveDialog #wlAddDetails input, +#wikiLoveDialog #wlAddDetails textarea, +#wikiLoveDialog #wlAddDetails select { + margin-top: 5px; + margin-left: 15px; + max-width: 460px; + display: block; +} + +#wikiLoveDialog .wlSpinner { + float: right; + margin-top: 6px; + display: none; +} + +/* dialog preview */ +#wikiLoveDialog #wlPreview { + float: right; + width: 480px; +} + +#wikiLoveDialog #wlPreview div { + margin-left: 15px; +} + +#wikiLoveDialog #wlPreview .editsection { + display: none; +} + +/* dialog misc */ +#wikiLoveDialog .submit { + float: right; +} \ No newline at end of file Property changes on: trunk/extensions/WikiLove/wikiLove.css ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/extensions/WikiLove/wikiLove.js =================================================================== --- trunk/extensions/WikiLove/wikiLove.js (rev 0) +++ trunk/extensions/WikiLove/wikiLove.js 2011-05-02 20:34:52 UTC (rev 87294) @@ -0,0 +1,319 @@ +( function( $ ) { $.wikiLove = { + types: { + // example type, could be removed later (also no i18n) + 'barnstar': { + descr: 'Barnstar', // description in the types menu + select: 'Select a barnstar:', // subtype select label + subtypes: { // some different subtypes + // note that when not using subtypes you should use these subtype options + // for the top-level type + 'original': { + title: 'An Original Barnstar for you!', // subject title for the message + descr: 'Original barnstar', // description in the menu + text: '{{subst:The Original Barnstar|$1 ~~~~}}', // message text, $1 is replaced by the user message + template: 'The Original Barnstar' // template that is used, for statistics + }, + 'special': { + title: null, // no predefined title, allows the user to enter a title + descr: 'Special barnstar', + text: '{{subst:The Special Barnstarl|$1 ~~~~}}', + template: 'The Special Barnstar' + } + } + }, + // default type, nice to leave this one in place when adding other types + 'makeyourown': { + title: null, + descr: mw.msg( 'wikilove-type-makeyourown' ), + text: "$1\n\n~~~~", + template: '' + } + }, + $dialog: null, // dialog jQuery object + editToken: '', // edit token used for the final AJAX call + currentTypeId: null, // id of the currently selected type (e.g. 'barnstar' or 'makeyourown') + currentSubtypeId: null, // id of the currently selected subtype (e.g. 'original' or 'special') + currentTypeOrSubtype: null, // content of the current (sub)type (i.e. an object with title, descr, text, etc.) + + /* + * Opens the dialog and builds it if necessary. + */ + openDialog: function() { + if ( $.wikiLove.$dialog === null ) { + // Build a type list like this: + // <ul id="wlTypes"> + // <li tabindex="0"><span>Barnstar</span></li> + // <li tabindex="0"><span>Make your own</span></li> + // </ul> + var $typeList = $( '<ul id="wlTypes"></ul>' ); + for( var typeId in $.wikiLove.types ) { + $typeList.append( + $( '<li tabindex="0"><span>' + $.wikiLove.types[typeId].descr + '</span></li>' ) + .data( 'typeId', typeId ) + ); + } + + // Build the left menu for selecting a type: + // <div id="wlSelectType"> + // <h3>Select Type:</h3> + // <ul id="wlTypes">...</ul> + // </div> + var $selectType = $( '<div id="wlSelectType"></div>' ) + .append( '<h3>' + mw.msg( 'wikilove-select-type' ) + '</h3>' ) + .append( $typeList ); + + // Build the right top section for selecting a subtype and entering a title (optional) and message + // <div id="wlAddDetails"> + // <h3>Add Details:</h3> + // + // <label for="wlSubtype" id="wlSubtypeLabel">...</label> (label depends on type) + // <select id="wlSubtype">...</select> (also depends on type) + // + // <label for="wlTitle" id="wlTitleLabel">Title:</label> (hidden for some (sub)types) + // <input type="text" class="text" id="wlTitle"/> + // + // <label for="wlMessage" id="wlMessageLabel">Enter a message:</label> + // <span class="wlOmitSig">(without a signature)</span> (this span floats right) + // <textarea id="wlMessage"></textarea> (textarea grows automatically with content) + // + // <input id="wlButtonPreview" class="submit" type="submit" value="Preview"/> + // <img class="wlSpinner" src="..."/> (spinner for the preview button) + // </div> + var $addDetails = $( '<div id="wlAddDetails"></div>' ) + .append( '<h3>' + mw.msg( 'wikilove-add-details' ) + '</h3>' ) + .append( '<label for="wlSubtype" id="wlSubtypeLabel"></label>' ) + .append( '<select id="wlSubtype"></select>' ) + .append( '<label for="wlTitle" id="wlTitleLabel">' + mw.msg( 'wikilove-title' ) + '</label>' ) + .append( '<input type="text" class="text" id="wlTitle"/>' ) + .append( '<label for="wlMessage" id="wlMessageLabel">' + mw.msg( 'wikilove-enter-message' ) + '</label>' ) + .append( '<span class="wlOmitSig">' + mw.msg( 'wikilove-omit-sig' ) + '</span>' ) + .append( '<textarea id="wlMessage"></textarea>' ) + .append( '<input id="wlButtonPreview" class="submit" type="submit" value="' + + mw.msg( 'wikilove-button-preview' ) + '"/>' ) + .append( '<img class="wlSpinner" src="' + mw.config.get( 'wgServer' ) + mw.config.get( 'wgScriptPath' ) + + '/extensions/WikiLove/images/spinner.gif"/>' ) + .hide(); + + // Build the right bottom preview section + // <div id="wlPreview"> + // <h3>Preview:</h3> + // <div id="wlPreviewArea">...</div> (preview gets loaded here) + // <input id="wlButtonSend" class="submit" type="submit" value="Send WikiLove"/> + // <img class="wlSpinner" src="..."/> (another spinner for the send button) + // </div> + var $preview = $( '<div id="wlPreview"></div>' ) + .append( '<h3>' + mw.msg( 'wikilove-preview' ) + '</h3>' ) + .append( '<div id="wlPreviewArea"></div>' ) + .append( '<input id="wlButtonSend" class="submit" type="submit" value="' + + mw.msg( 'wikilove-button-send' ) + '"/>' ) + .append( '<img class="wlSpinner" src="' + mw.config.get( 'wgServer' ) + mw.config.get( 'wgScriptPath' ) + + '/extensions/WikiLove/images/spinner.gif"/>' ) + .hide(); + + // Build a modal, hidden dialog with the 3 different sections + $.wikiLove.$dialog = $( '<div id="wikiLoveDialog"></div>' ) + .append( $selectType ) + .append( $addDetails ) + .append( $preview ) + .dialog({ + width: 800, + autoOpen: false, + title: mw.msg( 'wikilove-dialog-title' ), + modal: true, + resizable: false + }); + + $( '#wlMessage' ).elastic(); // have the message textarea grow automatically + $( '#wlTypes li' ).click( $.wikiLove.clickType ); + $( '#wlSubtype' ).change( $.wikiLove.changeSubtype ); + $( '#wlButtonPreview' ).click( $.wikiLove.clickPreview ); + $( '#wlButtonSend' ).click( $.wikiLove.clickSend ); + } + + $.wikiLove.$dialog.dialog( 'open' ); + }, + + /* + * Handler for the left menu. Selects a new type and initialises next section + * depending on whether or not to show subtypes. + */ + clickType: function( e ) { + e.preventDefault(); + + var newTypeId = $( this ).data( 'typeId' ); + if( $.wikiLove.currentTypeId != newTypeId ) { // only do stuff when a different type is selected + $.wikiLove.currentTypeId = newTypeId; + $.wikiLove.currentSubtypeId = null; // reset the subtype id + + $( '#wlTypes li' ).removeClass( 'selected' ); + $( this ).addClass( 'selected' ); // highlight the new type in the menu + + if( typeof $.wikiLove.types[$.wikiLove.currentTypeId].subtypes == 'object' ) { + // we're dealing with subtypes here + $.wikiLove.currentTypeOrSubtype = null; // reset the (sub)type object until a subtype is selected + $( '#wlSubtype' ).html( '' ); // clear the subtype menu + + for( var subtypeId in $.wikiLove.types[$.wikiLove.currentTypeId].subtypes ) { + // add all the subtypes to the menu while setting their subtype ids in jQuery data + var subtype = $.wikiLove.types[$.wikiLove.currentTypeId].subtypes[subtypeId]; + $( '#wlSubtype' ).append( + $( '<option>' + subtype.descr + '</option>' ).data( 'subtypeId', subtypeId ) + ); + } + $( '#wlSubtype' ).show(); + + // change and show the subtype label depending on the type + $( '#wlSubtypeLabel' ).text( $.wikiLove.types[$.wikiLove.currentTypeId].select ); + $( '#wlSubtypeLabel' ).show(); + $.wikiLove.changeSubtype(); // update controls depending on the currently selected (i.e. first) subtype + } + else { + // there are no subtypes, just use this type for the current (sub)type + $.wikiLove.currentTypeOrSubtype = $.wikiLove.types[$.wikiLove.currentTypeId]; + $( '#wlSubtype' ).hide(); + $( '#wlSubtypeLabel' ).hide(); + $.wikiLove.updateAllDetails(); // update controls depending on this type + } + + $( '#wlAddDetails' ).show(); + $( '#wlPreview' ).hide(); + } + return false; + }, + + /* + * Handler for changing the subtype. + */ + changeSubtype: function() { + // find out which subtype is selected + var newSubtypeId = $( '#wlSubtype option:selected' ).first().data( 'subtypeId' ); + if( $.wikiLove.currentSubtypeId != newSubtypeId ) { // only change stuff when a different subtype is selected + $.wikiLove.currentSubtypeId = newSubtypeId; + $.wikiLove.currentTypeOrSubtype = $.wikiLove.types[$.wikiLove.currentTypeId] + .subtypes[$.wikiLove.currentSubtypeId]; + $.wikiLove.updateAllDetails(); + $( '#wlPreview' ).hide(); + } + }, + + /* + * Called when type or subtype changes, updates controls. Currently only updates title label and textbox. + */ + updateAllDetails: function() { + // show or hide title label and textbox depending on whether a predefined title exists + if( typeof $.wikiLove.currentTypeOrSubtype.title == 'string' ) { + $( '#wlTitleLabel').hide(); + $( '#wlTitle' ).hide(); + $( '#wlTitle' ).val( $.wikiLove.currentTypeOrSubtype.title ); + } + else { + $( '#wlTitleLabel').show(); + $( '#wlTitle' ).show(); + $( '#wlTitle' ).val( '' ); + } + }, + + /* + * Handler for clicking the preview button. Builds data for AJAX request. + */ + clickPreview: function() { + var title = '==' + $( '#wlTitle' ).val() + "==\n"; + var msg = $.wikiLove.currentTypeOrSubtype.text.replace( '$1', $( '#wlMessage' ).val() ); + $.wikiLove.doPreview( title + msg ); + }, + + /* + * Fires AJAX request for previewing wikitext. + */ + doPreview: function( wikitext ) { + $( '#wlAddDetails .wlSpinner' ).fadeIn( 200 ); + $.ajax({ + url: mw.config.get( 'wgServer' ) + mw.config.get( 'wgScriptPath' ) + '/api.php?', + data: { + 'action': 'parse', + 'format': 'json', + 'text': wikitext, + 'prop': 'text', + 'pst': true + }, + dataType: 'json', + type: 'POST', + success: function( data ) { + $.wikiLove.showPreview( data.parse.text['*'] ); + $( '#wlAddDetails .wlSpinner' ).fadeOut( 200 ); + } + }); + }, + + /* + * Callback for the preview function. Sets the preview area with the HTML and fades it in. + */ + showPreview: function( html ) { + $( '#wlPreviewArea' ).html( html ); + $( '#wlPreview' ).fadeIn( 200 ); + }, + + /* + * Handler for the send (final submit) button. Builds data for AJAX request. + * The type sent for statistics is 'typeId-subtypeId' when using subtypes, + * or simply 'typeId' otherwise. + */ + clickSend: function() { + var title = $( '#wlTitle' ).val(); + var msg = $.wikiLove.currentTypeOrSubtype.text.replace( '$1', $( '#wlMessage' ).val() ); + $.wikiLove.doSend( title, msg, $.wikiLove.currentTypeId + + ($.wikiLove.currentSubtypeId == null ? '-' + $.wikiLove.currentSubtypeId : ''), + $.wikiLove.currentTypeOrSubtype.template); + }, + + /* + * Fires the final AJAX request and then redirects to the talk page where the content is added. + */ + doSend: function( subject, wikitext, type, template ) { + $( '#wlPreview .wlSpinner' ).fadeIn( 200 ); + $.ajax({ + url: mw.config.get( 'wgServer' ) + mw.config.get( 'wgScriptPath' ) + '/api.php?', + data: { + 'action': 'wikiLove', + 'format': 'json', + 'title': mw.config.get( 'wgPageName' ), + 'template': template, + 'type': type, + 'text': wikitext, + 'subject': subject, + 'token': $.wikiLove.editToken + }, + dataType: 'json', + type: 'POST', + success: function( data ) { + mw.log( data ); + mw.log( mw.config.get( 'wgPageName' ) ); + $( '#wlPreview .wlSpinner' ).fadeOut( 200 ); + + if ( data.redirect.pageName == mw.config.get( 'wgPageName' ) ) { + // unfortunately, when on the talk page we cannot reload and then + // jump to the correct section, because when we set the hash (#...) + // the page won't reload... + window.location.reload(); + } + else { + window.location = mw.config.get( 'wgArticlePath' ).replace('$1', data.redirect.pageName) + + data.redirect.fragment; + } + } + }); + }, + + /* + * Init function which is called upon page load. Binds the WikiLove icon to opening the dialog. + */ + init: function() { + $( '#ca-wikilove a' ).click( function( e ) { + $.wikiLove.openDialog(); + e.preventDefault(); + return false; + }); + } +}; +$.wikiLove.init(); +} ) ( jQuery ); \ No newline at end of file Property changes on: trunk/extensions/WikiLove/wikiLove.js ___________________________________________________________________ Added: svn:eol-style + native _______________________________________________ MediaWiki-CVS mailing list MediaWiki-CVS@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs