Robert Vogel has submitted this change and it was merged.
Change subject: [WIP] Notifications
......................................................................
[WIP] Notifications
* Implemented localStorage based storage mechanism for notifications
* Improved init mechanism
* Added notification badge
* Added notification flyout based on OO.ui.PopupWidget
* Added assets for button template and OPT-IN gadget
* Now using marker for base page insertion
* Built own message dialog to have anchor tags displayed
TODO
* Find reason for strange DB errors in API calls with "\n" in parameter
* Fix popup displacement
Change-Id: I525195eac98ff7addd6f47546f40d50daf51d707
---
A assets/MediaWiki_Gadget-teahouse-opt-in.wiki
A assets/MediaWiki_Gadget-teahouse-opt-in_main.js.wiki
M assets/MediaWiki_Gadget-teahouse.wiki
A assets/MediaWiki_Gadgets-definition.partial.wiki
A assets/Template_Answered_question.wiki
A assets/Template_Ask_your_question.wiki
A assets/Template_Pending_question.wiki
A assets/Template_Question.wiki
M gadgetize.json
M i18n/de.json
M i18n/en.json
M i18n/qqq.json
A resources/mediawiki.teahouse.board.js
M resources/mediawiki.teahouse.css
M resources/mediawiki.teahouse.dialog.js
M resources/mediawiki.teahouse.gadget.development.js
M resources/mediawiki.teahouse.js
A resources/mediawiki.teahouse.notifications.js
A resources/ui/dialogs/th.ui.MessageDialog.js
M resources/ui/dialogs/th.ui.QuestionDialog.js
20 files changed, 663 insertions(+), 128 deletions(-)
Approvals:
Robert Vogel: Verified; Looks good to me, approved
diff --git a/assets/MediaWiki_Gadget-teahouse-opt-in.wiki
b/assets/MediaWiki_Gadget-teahouse-opt-in.wiki
new file mode 100644
index 0000000..24bc039
--- /dev/null
+++ b/assets/MediaWiki_Gadget-teahouse-opt-in.wiki
@@ -0,0 +1 @@
+[[Wikipedia:Teahouse|Wikipedia Teahouse]] OPT IN - Always display "Ask a
question" link in personal tools
\ No newline at end of file
diff --git a/assets/MediaWiki_Gadget-teahouse-opt-in_main.js.wiki
b/assets/MediaWiki_Gadget-teahouse-opt-in_main.js.wiki
new file mode 100644
index 0000000..d8a96fd
--- /dev/null
+++ b/assets/MediaWiki_Gadget-teahouse-opt-in_main.js.wiki
@@ -0,0 +1 @@
+/* This is just a dummy script that allows to have the "Teahouse-opt-in"
gadget in the preferences */
\ No newline at end of file
diff --git a/assets/MediaWiki_Gadget-teahouse.wiki
b/assets/MediaWiki_Gadget-teahouse.wiki
index 7391e1e..911539a 100644
--- a/assets/MediaWiki_Gadget-teahouse.wiki
+++ b/assets/MediaWiki_Gadget-teahouse.wiki
@@ -1 +1 @@
-Wikipedia Teahouse - Wikimedia Deutschland e.V., Hallo Welt! - Medienwerkstatt
GmbH, [[mw:User:Osnard|Robert Vogel]]
\ No newline at end of file
+[[Wikipedia:Teahouse|Wikipedia Teahouse]] BASE - Wikimedia Deutschland e.V.,
Hallo Welt! - Medienwerkstatt GmbH, Author: [[mw:User:Osnard|Robert Vogel]]
\ No newline at end of file
diff --git a/assets/MediaWiki_Gadgets-definition.partial.wiki
b/assets/MediaWiki_Gadgets-definition.partial.wiki
new file mode 100644
index 0000000..5f7b5f8
--- /dev/null
+++ b/assets/MediaWiki_Gadgets-definition.partial.wiki
@@ -0,0 +1,4 @@
+<!-- The following lines have to be added to "MediaWiki:Gadgets-definition" -->
+
+* teahouse[ResourceLoader|default]|teahouse/main.js|teahouse/teahouse.css
+* teahouse-opt-in[ResourceLoader]|teahouse/main.js
\ No newline at end of file
diff --git a/assets/Template_Answered_question.wiki
b/assets/Template_Answered_question.wiki
new file mode 100644
index 0000000..817429f
--- /dev/null
+++ b/assets/Template_Answered_question.wiki
@@ -0,0 +1,3 @@
+<includeonly>
+[[Category:Teahouse/Answered_Question]]
+</includeonly>
\ No newline at end of file
diff --git a/assets/Template_Ask_your_question.wiki
b/assets/Template_Ask_your_question.wiki
new file mode 100644
index 0000000..7528ba6
--- /dev/null
+++ b/assets/Template_Ask_your_question.wiki
@@ -0,0 +1 @@
+<span class="th-ask" style="display: inline-block; background-color: #27AA65;
padding: 0.5em; color:#F0F0F0; cursor:pointer">Ask your question</span>
\ No newline at end of file
diff --git a/assets/Template_Pending_question.wiki
b/assets/Template_Pending_question.wiki
new file mode 100644
index 0000000..9394d60
--- /dev/null
+++ b/assets/Template_Pending_question.wiki
@@ -0,0 +1,3 @@
+<includeonly>
+[[Category:Teahouse/Pending_Question]]
+</includeonly>
\ No newline at end of file
diff --git a/assets/Template_Question.wiki b/assets/Template_Question.wiki
new file mode 100644
index 0000000..0bbf46c
--- /dev/null
+++ b/assets/Template_Question.wiki
@@ -0,0 +1,5 @@
+<noinclude>
+This is the wrapper template that embeds a question into the question list
+</noinclude><includeonly><h3>[[Project:Teahouse/Questions/{{{title}}}|{{{title}}}]]</h3>
+{{Project:Teahouse/Questions/{{{title}}}}}
+</includeonly>
\ No newline at end of file
diff --git a/gadgetize.json b/gadgetize.json
index 08b15a9..dbebc85 100644
--- a/gadgetize.json
+++ b/gadgetize.json
@@ -1,5 +1,7 @@
{
"MediaWiki:Gadget-teahouse": ["assets/MediaWiki_Gadget-teahouse.wiki"],
+ "MediaWiki:Gadget-teahouse-opt-in":
["assets/MediaWiki_Gadget-teahouse-opt-in.wiki"],
+ "MediaWiki:Gadget-teahouse-opt-in/main.js":
["assets/MediaWiki_Gadget-teahouse-opt-in_main.js.wiki"],
"MediaWiki:Gadget-teahouse/main.js": [
"resources/mediawiki.teahouse.js",
@@ -12,14 +14,17 @@
"MediaWiki:Gadget-teahouse/ui/dialogs/th.ui.QuestionDialog.js": [
"resources/ui/dialogs/th.ui.QuestionDialog.js"
],
+ "MediaWiki:Gadget-teahouse/ui/dialogs/th.ui.MessageDialog.js": [
+ "resources/ui/dialogs/th.ui.MessageDialog.js"
+ ],
"MediaWiki:Gadget-teahouse/teahouse.css": [
"resources/mediawiki.teahouse.css"
],
- "Template:Teahouse/Pending_Question": [
"assets/Template_Pending_Questions.wiki" ],
- "Template:Teahouse/Answered_Question": [
"assets/Template_Answered_Questions.wiki" ],
- "Template:Teahouse/Question_ListItem": [
"assets/Template_Answered_Questions.wiki" ],
- "Module:Teahouse_Questions": [ "assets/Module_Teahouse_Questions.lua"
],
+ "Template:Teahouse/Ask_your_question": [
"assets/Template_Ask_your_question.wiki" ],
+ "Template:Teahouse/Pending_question": [
"assets/Template_Pending_question.wiki" ],
+ "Template:Teahouse/Answered_question": [
"assets/Template_Answered_question.wiki" ],
+ "Template:Teahouse/Question": [ "assets/Template_Question.wiki" ],
"Help:Teahouse": [ "assets/Help_Teahouse.wiki" ]
}
\ No newline at end of file
diff --git a/i18n/de.json b/i18n/de.json
index 3c1edf0..73aeb75 100644
--- a/i18n/de.json
+++ b/i18n/de.json
@@ -6,10 +6,12 @@
},
"th-desc": "Entwicklungserweiterung für das 'Teahouse' Gadget",
"th-button-text": "Stelle deine Frage",
+ "th-button-title": "Lerne die Community kennen",
"th-dialog-title": "Stelle deine Frage",
"th-dialog-description-top": "Stelle deine Frage an die Wikipedia
Community! Auf $1 kannst du dir die Fragen anderer Benutzer ansehen.",
"th-dialog-label-summary": "Deine Frage",
"th-dialog-label-text": "Genauere Beschreibung (Du kannst WikiText
verwenden)",
+ "th-dialog-label-similar": "Ähnliche, bereits gestellte Fragen",
"th-dialog-btn-ok": "Veröffentlichen",
"th-dialog-btn-cancel": "Abbrechen",
"th-dialog-disclaimer": "Veröffentlichung unter den Nutzungsbedingungen
der Wikipedia",
@@ -17,5 +19,7 @@
"th-dialog-msg-title-save": "Frage veröffentlicht",
"th-dialog-msg-text-save": "Deine Frage wurde unter $1 veröffentlicht.
Möchtest du die komplette Liste der Fragen sehen?",
"th-dialog-msg-btn-yes": "Ja",
- "th-dialog-msg-btn-no": "Nein"
+ "th-dialog-msg-btn-no": "Nein",
+ "th-notifications-badge-title": "Es gibt Reaktionen auf deine Fragen",
+ "th-notifications-popup-title": "Reaktionen auf deine Fragen"
}
diff --git a/i18n/en.json b/i18n/en.json
index 7faee83..3c796cd 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -6,10 +6,12 @@
},
"th-desc": "Development extension for \"Teahouse\" gadget",
"th-button-text": "Ask your question",
+ "th-button-title": "Get in touch with the community",
"th-dialog-title": "Ask your question",
"th-dialog-description-top": "Ask your question to the Wikipedia
Community! You can see the questions of other users on $1",
"th-dialog-label-summary": "Your question",
"th-dialog-label-text": "Further description (You may use wikitext)",
+ "th-dialog-label-similar": "Similar questions",
"th-dialog-btn-ok": "Publish",
"th-dialog-btn-cancel": "Cancel",
"th-dialog-disclaimer": "Published under Wikipedia's Terms of Use",
@@ -17,5 +19,7 @@
"th-dialog-msg-title-save": "Question published",
"th-dialog-msg-text-save": "Your question has been published on $1. Do
you wand to see the complete list of questions?",
"th-dialog-msg-btn-yes": "Yes",
- "th-dialog-msg-btn-no": "No"
+ "th-dialog-msg-btn-no": "No",
+ "th-notifications-badge-title": "There are reactions to your questions",
+ "th-notifications-popup-title": "Reactions to your questions"
}
diff --git a/i18n/qqq.json b/i18n/qqq.json
index 2030733..36c825e 100644
--- a/i18n/qqq.json
+++ b/i18n/qqq.json
@@ -6,12 +6,20 @@
},
"th-desc": "Used in [[Special:Version]], description teahouse
extension",
"th-button-text": "Text of a button/link on a page and within the user
menu",
+ "th-button-title": "Title attribute of button anchor",
"th-dialog-title": "Title of the dialog box",
"th-dialog-description-top": "A short description of how to use teh
dialog at the beginning",
"th-dialog-label-summary": "Label of a text field that contains the
question itself",
"th-dialog-label-text": "Label of a textarea that allows an additional
information for the question",
+ "th-dialog-label-similar": "Label of a list with links to similar
questions",
"th-dialog-btn-ok": "Text of the button that sends the question. It
should be clear that the action makes the question available to the public.",
"th-dialog-btn-cancel": "Text of the button that resets the form and
closes the dialog",
"th-dialog-disclaimer": "Hint to the Terms of Use. Links to
https://wikimediafoundation.org/wiki/Terms_of_Use",
- "th-dialog-anon-ip-hint": "Hint that the IP adress of an anonymous user
will be saved"
+ "th-dialog-anon-ip-hint": "Hint that the IP adress of an anonymous user
will be saved",
+ "th-dialog-msg-title-save": "",
+ "th-dialog-msg-text-save": "",
+ "th-dialog-msg-btn-yes": "",
+ "th-dialog-msg-btn-no": "",
+ "th-notifications-badge-title": "",
+ "th-notifications-popup-title": ""
}
diff --git a/resources/mediawiki.teahouse.board.js
b/resources/mediawiki.teahouse.board.js
new file mode 100644
index 0000000..a3ce14f
--- /dev/null
+++ b/resources/mediawiki.teahouse.board.js
@@ -0,0 +1,162 @@
+(function( mw, $, d, undefined ){
+
+ function _publishQuestion( question ) {
+ //We append a signature WikiText fragment by default
+ var signature = ['-', '-~~', '~~'].join(''); //This is just to
prevent the wikitext parser from parsing it on deployment
+ if( question.text.indexOf( signature ) === -1 ) {
+ question.text += "\n\n" + signature;
+ }
+
+ question.text += "\n\n{{" + _config.templates.pendingQuestion +
'}}';
+
+ var dfd = $.Deferred();
+
+ //Step 1: Create the question subpage
+ var createQuestionAPI = new mw.Api();
+ createQuestionAPI.postWithToken( 'edit', {
+ action: 'edit',
+ watchlist: 'watch',
+ title: _config.basePage + "/" + question.title,
+ summary: "Added by Teahouse gadget",
+ text: question.text
+ })
+ .fail(function( resp1, jqXHR ){
+ console.log( resp1 );
+ dfd.reject();
+ })
+ .done(function( resp1, jqXHR ){
+ //Step 2: From the response build the template text for
the board
+ var title = resp1.edit.title + '';
+ var timestamp = resp1.edit.newtimestamp;
+ var wrapper = _config.templates.questionWrapper + '';
+ mw.teahouse.notifications.registerTitle( title,
timestamp );
+ var titleParts = title.split( '/' );
+ var basename = titleParts[titleParts.length-1];
+
+ var template = "\n" + '{{' + wrapper + '|title=' +
basename + '}}'+"\n";
+
+ //Step 3: Query the WikiText content of the board
+ var getBasePageWikiTextAPI = new mw.Api();
+ getBasePageWikiTextAPI.get({
+ action: 'query',
+ titles: _config.basePage,
+ prop: 'revisions',
+ rvprop: 'content',
+ indexpageids : ''
+ })
+ .fail(function( resp2, jqXHR ){
+ console.log( resp2 );
+ dfd.reject();
+ })
+ .done(function( resp2, jqXHR ){
+ //Step 4: Add the new template at the desired
position within
+ //the board page
+ var pageId = resp2.query.pageids[0];
+ var content =
resp2.query.pages[pageId].revisions[0]['*'];
+ var contentParts = content.split(
_config.insertMarker, 2 );
+ if( contentParts.length === 1 ) { //No
_config.insertMarker found
+ contentParts[0] += "\n";
+ contentParts.push(''); //We append an
empty string
+ }
+
+ contentParts[1] = template + contentParts[1];
+ content = contentParts.join(
_config.insertMarker );
+
+ //Step 5: Write the new content to the board
page
+ var addToListAPI = new mw.Api();
+ addToListAPI.postWithToken( 'edit', {
+ action: 'edit',
+ title: _config.basePage,
+ text: content,
+ summary: "Added by Teahouse gadget"
+ })
+ .fail(function( resp3, jqXHR ){
+ console.log( resp3 );
+ dfd.reject();
+ })
+ .done(function( resp3, jqXHR ){
+ dfd.resolve( { questionpage: resp1.edit
} );
+ });
+ });
+ });
+
+ return dfd.promise();
+ }
+
+ var _sqCache = {};
+ var _lastPromise = undefined;
+ function _getSimilarQuestions( value, $target, callback ) {
+ mw.loader.using( 'mediawiki.Title', function() {
+ var cacheKey = value.toLowerCase();
+ var baseTitle = new mw.Title( _config.basePage );
+ var prefix = baseTitle.getPrefixedText() + '/';
+
+ if( cacheKey in _sqCache ) {
+ _renderSimilarQuestionsList(
_sqCache[cacheKey], prefix, $target );
+ callback();
+ return;
+ }
+
+ if( _lastPromise ) {
+ _lastPromise.abort();
+ }
+
+ var _api = new mw.Api();
+ _lastPromise = _api.get({
+ action: 'query',
+ list: 'search',
+ srsearch: value,
+ srlimit: 50,
+ srnamespace: baseTitle.getNamespaceId()
+ })
+ .done(function( response, jqXHR ){
+ _sqCache[cacheKey] = response.query.search;
+ _renderSimilarQuestionsList(
response.query.search, prefix, $target );
+ callback();
+ _lastPromise = undefined;
+ });
+
+ });
+ }
+
+ function _renderSimilarQuestionsList( items, prefix, $target ) {
+ var $list = $('<ul>').addClass('th-similar-questions-list');
+ var count = 0;
+
+ for( var i = 0; i < items.length && count < 5; i++ ) {
+ var title = items[i].title;
+ if( title.indexOf( prefix ) !== 0 ) {
+ continue;
+ }
+
+ var displayTitle = title.replace( prefix, '' );
+ var anchor = mw.html.element( 'a', {
+ href: mw.util.getUrl( title ),
+ title: title,
+ target: '_blank'
+ }, displayTitle );
+ $list.append( '<li>'+anchor+'</li>' );
+ count++;
+ }
+ $target.empty();
+ if( count > 0 ) {
+ var $label = $('<span>').addClass(
'oo-ui-labelElement-label' )
+ .append(
mw.message('th-dialog-label-similar').plain() );
+ $target.append( $label );
+ $target.append( $list );
+ }
+ }
+
+ var _config = {};
+ function _init( config ) {
+ delete( mw.teahouse.board.init );
+ _config = config;
+ }
+ //TODO: Make mw.teahouse.board a OOJS object of class "th.Board"
+ mw.teahouse.board = {
+ init: _init,
+ publishQuestion: _publishQuestion,
+ getSimilarQuestions: _getSimilarQuestions
+ };
+
+})( mediaWiki, jQuery, document );
\ No newline at end of file
diff --git a/resources/mediawiki.teahouse.css b/resources/mediawiki.teahouse.css
index 8434c3c..15ac186 100644
--- a/resources/mediawiki.teahouse.css
+++ b/resources/mediawiki.teahouse.css
@@ -3,6 +3,11 @@
color: #F0F0F0;
background-color: #27AA65; /*
https://www.mediawiki.org/wiki/Wikimedia_Foundation_Design/Color_usage */
padding: 0 0.5em;
+ text-decoration: none;
+}
+
+.th-question-dialog-body .oo-ui-labelElement-label {
+ font-weight: bold;
}
.th-question-dialog-body .th-inputWidget {
@@ -17,6 +22,35 @@
cursor: initial;
}
-.th-question-dialog-body .th-similar-questions-list {
- list-style-type: none;
+.th-question-dialog-body .th-similar-questions-list,
+.th-notif-popup-content .th-notif-popup-list {
+ list-style: none;
+ margin: 0;
+}
+
+#p-personal .th-notif-popup-content .th-notif-popup-list li {
+ float: none;
+ font-size: 100%;
+}
+
+#p-personal .th-notif-popup-content .th-notif-popup-list {
+ padding: 0.5em;
+}
+
+/* Borrowed from Echo extension */
+a.th-notifications-badge {
+ min-width: 7px;
+ border-radius: 2px;
+ padding: 0.25em 0.45em 0.2em 0.45em;
+ margin-left: -4px;
+ text-align: center;
+ /*background-color: #d2d2d2;*/
+ font-weight: bold;
+ color: white;
+ cursor: pointer;
+ text-decoration: none;
+}
+
+a.th-unread-notifications {
+ background-color: #27AA65;
}
\ No newline at end of file
diff --git a/resources/mediawiki.teahouse.dialog.js
b/resources/mediawiki.teahouse.dialog.js
index 9c3497d..6f4934a 100644
--- a/resources/mediawiki.teahouse.dialog.js
+++ b/resources/mediawiki.teahouse.dialog.js
@@ -1,51 +1,89 @@
(function( mw, $, d, undefined ){
+ function _getComponentUrl( path ) {
+ var url = new mw.Uri( _config.resourcesPath + path );
+ url.query['action'] = 'raw';
+ url.query['ctype'] = 'text/javascript';
- function _setupDialog( config ) {
+ return url.toString();
+ }
+ function _getWindowManager() {
+ if( !_windowManager ) {
_windowManager = new OO.ui.WindowManager({
modal: true,
isolate: true
});
$( 'body' ).append( _windowManager.$element );
+ }
+ return _windowManager;
+ }
- _window = new th.ui.QuestionDialog( config );
- _messageDialog = new OO.ui.MessageDialog();
- _windowManager.addWindows( [ _window, _messageDialog ]
);
+ function _setupQuestionDialog() {
+ _questionDialog = new th.ui.QuestionDialog( {}, _config );
+ _getWindowManager().addWindows( [ _questionDialog ] );
+ }
- return;
+ function _setupMessageDialog() {
+ _messageDialog = new th.ui.MessageDialog( {}, _config );
+ _getWindowManager().addWindows( [ _messageDialog ] );
}
var _windowManager = undefined;
- var _window = undefined;
+ var _questionDialog = undefined;
var _messageDialog = undefined;
- mw.teahouse.openQuestionDialog = function( data ) {
+
+ function _openQuestionDialog( data ) {
mw.loader.using( ['oojs-ui', 'mediawiki.Uri'], function(){
- var config = mw.teahouse.getConfig();
+ if( !_questionDialog ) {
+ $.getScript( _getComponentUrl(
"/ui/dialogs/th.ui.QuestionDialog.js" ), function(){
+ _setupQuestionDialog();
+ mw.teahouse.dialog.openQuestionDialog(
data ); //re-call after dependency is loaded
+ });
+ return false;
+ }
- if( !_windowManager ) {
- var url = new mw.Uri( config.resourcesPath +
"/ui/dialogs/th.ui.QuestionDialog.js" );
- url.query['action'] = 'raw';
- url.query['ctype'] = 'text/javascript';
+ data = $.extend( data, {
+ config: _config
+ });
+ _windowManager.openWindow( _questionDialog, data );
+ });
- $.getScript( url.toString(), function(){
- _setupDialog();
- mw.teahouse.openQuestionDialog( data );
//re-call after dependency is loaded
+ return false;
+ }
+
+ function _openMessageDialog( data, then ) {
+
+ mw.loader.using( ['oojs-ui', 'mediawiki.Uri'], function(){
+ if( !_messageDialog ) {
+ $.getScript( _getComponentUrl(
"/ui/dialogs/th.ui.MessageDialog.js" ), function(){
+ _setupMessageDialog();
+ mw.teahouse.dialog.openMessageDialog(
data, then ); //re-call after dependency is loaded
});
return;
}
- data = $.extend( data, {
- config: config,
- board: mw.teahouse.board
- });
- _windowManager.openWindow( _window, data );
+ _windowManager
+ .openWindow( _messageDialog, data )
+ .then(then);
});
+ }
+
+ var _config = {};
+ function _init( config ) {
+ delete(mw.teahouse.dialog.init);
+ _config = config;
+
+ //Register event handler for click on ...
+ $(d).on( 'click', '#p-teahouse',
mw.teahouse.dialog.openQuestionDialog ); //... menu link
+ $(d).on( 'click', '.th-ask',
mw.teahouse.dialog.openQuestionDialog ); //... custom element
+ }
+
+ mw.teahouse.dialog = {
+ init: _init,
+ openQuestionDialog: _openQuestionDialog,
+ openMessageDialog: _openMessageDialog
};
- mw.teahouse.openMessageDialog = function( data ) {
- //TODO: make sure _windowManager is available...
- return _windowManager.openWindow( _messageDialog, data );
- };
})( mediaWiki, jQuery, document );
diff --git a/resources/mediawiki.teahouse.gadget.development.js
b/resources/mediawiki.teahouse.gadget.development.js
index 251129a..22a75fc 100644
--- a/resources/mediawiki.teahouse.gadget.development.js
+++ b/resources/mediawiki.teahouse.gadget.development.js
@@ -2,10 +2,12 @@
basePage: 'Projekt:Teehaus/Fragen',
i18n: {
"th-button-text": "Stelle deine Frage",
+ "th-button-title": "Lerne die Community kennen",
"th-dialog-title": "Stelle deine Frage",
"th-dialog-description-top": "Stelle deine Frage an die
Wikipedia Community! Auf $1 kannst du dir die Fragen anderer Benutzer ansehen.",
"th-dialog-label-summary": "Deine Frage",
"th-dialog-label-text": "Genauere Beschreibung (Du kannst
WikiText verwenden)",
+ "th-dialog-label-similar": "Ähnliche, bereits gestellte Fragen",
"th-dialog-btn-ok": "Veröffentlichen",
"th-dialog-btn-cancel": "Abbrechen",
"th-dialog-disclaimer": "Veröffentlichung unter den
Nutzungsbedingungen der Wikipedia",
@@ -13,7 +15,9 @@
"th-dialog-msg-title-save": "Frage veröffentlicht",
"th-dialog-msg-text-save": "Deine Frage wurde unter $1
veröffentlicht. Möchtest du die komplette Liste der Fragen sehen?",
"th-dialog-msg-btn-yes": "Ja",
- "th-dialog-msg-btn-no": "Nein"
+ "th-dialog-msg-btn-no": "Nein",
+ "th-notifications-badge-title": "Es gibt Reaktionen auf deine
Fragen",
+ "th-notifications-popup-title": "Reaktionen auf deine Fragen"
},
resourcesPath: mw.config.get('wgScriptPath') +
'/extensions/Teahouse/resources'
});
\ No newline at end of file
diff --git a/resources/mediawiki.teahouse.js b/resources/mediawiki.teahouse.js
index 22d8097..d2ed025 100644
--- a/resources/mediawiki.teahouse.js
+++ b/resources/mediawiki.teahouse.js
@@ -5,19 +5,23 @@
* single wikipedias
*/
var _config = {
- basePage: 'Wikipedia:Teahouse',
+ basePage: 'Wikipedia:Teahouse/Questions',
+ insertMarker: '<!-- INSERTMARKER -->',
resourcesPath: mw.config.get('wgScript') +
"?title=MediaWiki:Gadget-teahouse",
templates: {
- pendingQuestion: 'Pending_question',
- answeredQuestion: 'Answered_question',
- questionWrapper: 'Wikipedia/Teahouse/Question'
+ pendingQuestion: 'Teahouse/Pending_question',
+ answeredQuestion: 'Teahouse/Answered_question',
+ questionWrapper: 'Teahouse/Question',
+ insertionMarker: 'Teahouse/Insertion_marker'
},
i18n: {
"th-button-text": "Ask your question",
+ "th-button-title": "Get in touch with the community",
"th-dialog-title": "Ask your question",
"th-dialog-description-top": "Ask your question to the
Wikipedia Community! You can see the questions of other users on $1",
"th-dialog-label-summary": "Your question",
"th-dialog-label-text": "Further description (You may
use wikitext)",
+ "th-dialog-label-similar": "Similar questions",
"th-dialog-btn-ok": "Publish",
"th-dialog-btn-cancel": "Cancel",
"th-dialog-disclaimer": "Published under Wikipedia's
Terms of Use",
@@ -25,20 +29,22 @@
"th-dialog-msg-title-save": "Question published",
"th-dialog-msg-text-save": "Your question has been
published on $1. Do you wand to see the complete list of questions?",
"th-dialog-msg-btn-yes": "Yes",
- "th-dialog-msg-btn-no": "No"
+ "th-dialog-msg-btn-no": "No",
+ "th-notifications-badge-title": "There are reactions to
your questions",
+ "th-notifications-popup-title": "Reactions to your
questions"
}
};
/**
* Initializes the Teahouse gadget components.
- * @param object config
* @returns void
*/
- function _init( config ){
-
- //Register event handler for click on ...
- $(d).on( 'click', '#p-teahouse', mw.teahouse.openQuestionDialog
); //... menu link
- $(d).on( 'click', '.th-ask', mw.teahouse.openQuestionDialog );
//... custom element
+ function _init(){
+ delete(mw.teahouse.init); //Can be calles only once
+ //Init sub-components and share current config with them
+ mw.teahouse.board.init( _config );
+ mw.teahouse.notifications.init( _config );
+ mw.teahouse.dialog.init( _config );
//Set cookie when anon clicks edit link
$(d).on( 'click', '#ca-ve-edit, #ca-edit', function(){
@@ -53,22 +59,27 @@
* @returns void
*/
function _checkUserIsEligible() {
+ //If the Teahouse-opt-in gadget is enabled we don't do any
further checks
+ if( mw.user.options.get( 'gadget-teahouse-opt-in', 0 ) === '1'
) {
+ _showDialogLink();
+ return;
+ }
//If not logged in we just check if the user has already made
an edit
if( mw.user.isAnon() === true &&
mw.cookie.get('mediaWiki.teahouse.anonEdit') === '1' ) {
- _showLink();
+ _showDialogLink();
}
//In case of a registered user we need to check for
"autoconfirmed" group.
else if( mw.user.isAnon() === false ) {
mw.user.getGroups()
.done(function(result){
if( result[0] && $.inArray(
'autoconfirmed', result[0] ) ) {
- _showLink();
+ _showDialogLink();
}
});
}
}
- function _showLink() {
+ function _showDialogLink() {
var linkMarkup =
'<li id="p-teahouse">'
+ '<a title="' + mw.message('th-button-title').plain()
+ '" href="#">'
@@ -76,7 +87,7 @@
+ '</a>'
+ '</li>';
- $(linkMarkup).prependTo( $('#p-personal > ul').first() );
+ _config._$dlgLink = $(linkMarkup).prependTo( $('#p-personal >
ul').first() );
}
mw.teahouse = {
@@ -85,14 +96,15 @@
mw.messages.set( _config.i18n );
_config = $.extend( _config, config );
mw.messages.set( _config.i18n );
- mw.loader.using( 'mediawiki.user', _init );
- },
- getConfig: function() {
- return _config;
+ //TDOD: Maybe use these as Gadget dependencies within
MediaWiki:Gadgets-definition?
+ mw.loader.using( ['mediawiki.user',
'mediawiki.cookie'], function() {
+ _init.call(mw.teahouse);
+ });
}
};
+ //register "th.ui" namespace for OOJS UI components
window.th = {
ui: {}
};
diff --git a/resources/mediawiki.teahouse.notifications.js
b/resources/mediawiki.teahouse.notifications.js
new file mode 100644
index 0000000..75c30c3
--- /dev/null
+++ b/resources/mediawiki.teahouse.notifications.js
@@ -0,0 +1,233 @@
+(function( mw, $, d, undefined ){
+ /**
+ * Just a little helper to geht the numer of properties in an object
+ * @param obj {Object} the JavaScript object
+ * @returns {Number}
+ */
+ function _objLength( obj ) {
+ var count = 0;
+ for( var key in obj ) {
+ count++;
+ }
+ return count;
+ }
+
+ /**
+ * Saves current data for localStore
+ * @param {object} data
+ * @returns {undefined}
+ */
+ function _persistData( data ) {
+ var storageData = JSON.stringify( data || _data );
+
+ if( window.localStorage ) {
+ window.localStorage.setItem( _storageKey, storageData );
+ }
+ else {
+ mw.cookie.set( _storageKey, storageData );
+ }
+
+ }
+
+ /**
+ * Calls MW API, updates internal data and repeats this periodically
+ * @returns {undefined}
+ */
+ function _checkForNotifications() {
+ mw.teahouse.notifications.getCurrentNotifications()
+ .done(function( titles ) {
+ if( _objLength(titles) > 0 ) {
+ _showNoficationsLink(
_objLength(titles) );
+ }
+ window.setTimeout( _checkForNotifications, 5 *
60 * 1000 );
+ });
+ }
+
+ var _$notif = null;
+ function _showNoficationsLink( count ) {
+ if ( !_$notif ) {
+ var notifMarkup =
+ '<li id="p-teahouse-notif">'
+ + '<a class="th-notifications-badge
th-unread-notifications" title="' +
mw.message('th-notifications-badge-title').plain() + '" href="#">'
+ + count
+ + '</a>'
+ + '</li>';
+ _$notif = $(notifMarkup);
+
+ //As showing the "Ask your question" link depends on an
AJAX call
+ //there stands the chance that this call is before or
after the
+ //link has been added to the DOM. We have to make sure
the order of
+ //elements is always the same
+ if( _config._$dlgLink ) {
+ _config._$dlgLink.after( _$notif );
+ }
+ else {
+ _$notif.prependTo( $('#p-personal >
ul').first() );
+ }
+ }
+
+ _$notif.find( 'a.th-notifications-badge' ).html( count );
+ }
+
+ /**
+ * We don't use a OO.ui.PopupElement here to prevent a dependency to
+ * 'oojs-ui' module during normal page load
+ * @type OO.ui.PopupWidget
+ */
+ var _popUpWidget = null;
+ var _$popUpContent = null;
+ function _toggleNotifPopup( event ) {
+ if( _popUpWidget ) {
+ var list = $('<ul>').addClass('th-notif-popup-list');
+ for( var title in _data.notifications ) {
+ var titleParts = title.split('/');
+
+ //basename of subpage
+ var displayTitle =
titleParts[titleParts.length-1];
+
+ var anchor = mw.html.element('a', {
+ href: mw.util.getUrl( title ),
+ title: title,
+ target: '_blank'
+ }, displayTitle );
+ list.append('<li>'+anchor+'</li>');
+ }
+
+ _$popUpContent.empty().append( list );
+ _popUpWidget.toggle();
+ }
+ else {
+ //Just create elements ...
+ mw.loader.using( ['oojs-ui'], function() {
+ _$popUpContent = $('<div>')
+ .addClass( 'th-notif-popup-content' );
+
+ _popUpWidget = new OO.ui.PopupWidget({
+ autoClose: true,
+ head: true,
+ label:
mw.message('th-notifications-popup-title').plain(),
+ $content: _$popUpContent
+ });
+ _$notif.append( _popUpWidget.$element );
+
+ //... and call yourself again to populate list
+ _toggleNotifPopup();
+ });
+ }
+
+ return false;
+ }
+
+ var _config = {};
+ var _data = {
+ watchlist: {},
+ notifications: {},
+ lastCheck: 0
+ };
+ var _storageKey = 'mediaWiki.teahouse.notifications.data';
+ function _init( config ) {
+ delete(mw.teahouse.notifications.init);
+
+ _config = config;
+
+ var storageData = '{}';
+ if( window.localStorage ) {
+ storageData = window.localStorage.getItem( _storageKey
);
+ }
+ else {
+ storageData = mw.cookie.get( _storageKey, undefined,
'{}' );
+ }
+
+ var data = JSON.parse( storageData );
+
+ _data = $.extend( _data , data ); //Set internal data
+
+ //mark current title as read
+ mw.teahouse.notifications.markTitleAsRead(
mw.config.get('wgPageName') );
+
+ _checkForNotifications();
+
+ $(d).on( 'click', '#p-teahouse-notif', _toggleNotifPopup );
+ }
+
+ mw.teahouse.notifications = {
+ init: _init,
+ registerTitle: function( title, timestamp ) {
+ _data.watchlist[title] = timestamp;
+ _persistData();
+ },
+
+ removeTitle: function( title ) {
+ if( title in _data.watchlist ) {
+ delete( _data.watchlist[title] );
+ }
+ _persistData();
+ },
+
+ markTitleAsRead: function( title ) {
+ var t = title.replace( /_/g, ' ' );
+ if( t in _data.watchlist ) {
+ _data.watchlist[t] = (new Date()).toISOString();
+
+ //Do a API call for changes
+ _data.lastCheck = (new Date( 0 )).toISOString();
+ }
+
+ _persistData();
+ },
+
+ getCurrentNotifications: function() {
+ var dfd = $.Deferred();
+ var titles = [];
+ for( var title in _data.watchlist ) {
+ titles.push( title );
+ }
+ if( titles.length === 0 ) {
+ dfd.reject();
+ return dfd.promise();
+ }
+
+ //If the last call is less than five minutes old, we do
not call
+ //again, even on new page load
+ var lastCheckPlusWait = new Date( _data.lastCheck );
+
lastCheckPlusWait.setMinutes(lastCheckPlusWait.getMinutes() + 5);
+
+ if( lastCheckPlusWait > new Date() ) {
+ dfd.resolve( _data.notifications );
+ return dfd.promise();
+ }
+
+ var currentUsername = mw.user.getName();
+
+ var api = new mw.Api();
+ api.get({
+ action: 'query',
+ prop: 'revisions',
+ titles: titles.join( '|' ),
+ rvprop: 'timestamp|user'
+ })
+ .done(function( response, jqXHR ) {
+ _data.notifications = {}; //Reset
+
+ for( var pageId in response.query.pages ) {
+ var title =
response.query.pages[pageId].title;
+ var revisions =
response.query.pages[pageId].revisions;
+ if( !revisions ) {
+ continue;
+ }
+ var myDate = new Date(
_data.watchlist[title] );
+ var theirDate = new Date(
revisions[0].timestamp );
+
+ if( theirDate > myDate &&
currentUsername !== revisions[0].user ) {
+ _data.notifications[title] =
revisions[0];
+ }
+ }
+ _data.lastCheck = (new Date()).toISOString();
+ _persistData();
+ dfd.resolve( _data.notifications );
+ });
+
+ return dfd.promise();
+ }
+ };
+})( mediaWiki, jQuery, document );
\ No newline at end of file
diff --git a/resources/ui/dialogs/th.ui.MessageDialog.js
b/resources/ui/dialogs/th.ui.MessageDialog.js
new file mode 100644
index 0000000..229cb05
--- /dev/null
+++ b/resources/ui/dialogs/th.ui.MessageDialog.js
@@ -0,0 +1,53 @@
+/*!
+ * Teahouse user interface MessageDialog class.
+ */
+
+/**
+ * Dialog displaying a message to a user
+ *
+ * @class
+ * @extends OO.ui.MessageDialog
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+
+th.ui.MessageDialog = function ThUiMessageDialog( config ) {
+ th.ui.MessageDialog.super.call( this, config );
+};
+OO.inheritClass( th.ui.MessageDialog, OO.ui.MessageDialog );
+
+th.ui.MessageDialog.static.actions = [
+ { action: 'yes', label: mw.message('th-dialog-msg-btn-yes').plain() },
+ { action: 'no', label: mw.message('th-dialog-msg-btn-no').plain() }
+];
+/**
+ * @inheritdoc
+ */
+th.ui.MessageDialog.prototype.initialize = function () {
+ // Parent method
+ th.ui.MessageDialog.super.prototype.initialize.call( this );
+
+ //Create new panel
+ this.$message = $('<div>').addClass( 'oo-ui-messageDialog-message' );
+
+ this.text.$element.append( this.$message );
+};
+
+/**
+ * @inheritdoc
+ */
+th.ui.MessageDialog.prototype.getSetupProcess = function ( data ) {
+ data = data || {};
+
+ // Parent method
+ return th.ui.MessageDialog.super.prototype.getSetupProcess.call( this,
data )
+ .next( function () {
+
+ //We hide the base class' OO.ui.LabelWidget
+ this.message.$element.hide();
+
+ this.$message.empty();
+ this.$message.append(data.message);
+ }, this );
+};
\ No newline at end of file
diff --git a/resources/ui/dialogs/th.ui.QuestionDialog.js
b/resources/ui/dialogs/th.ui.QuestionDialog.js
index e1c355c..73dac5d 100644
--- a/resources/ui/dialogs/th.ui.QuestionDialog.js
+++ b/resources/ui/dialogs/th.ui.QuestionDialog.js
@@ -12,7 +12,8 @@
* @param {Object} [config] Configuration options
*/
-th.ui.QuestionDialog = function ThUiQuestionDialog( config ) {
+th.ui.QuestionDialog = function ThUiQuestionDialog( config, teahousecfg ) {
+ this._config = teahousecfg;
th.ui.QuestionDialog.super.call( this, config );
};
OO.inheritClass( th.ui.QuestionDialog, OO.ui.ProcessDialog );
@@ -27,7 +28,6 @@
disabled: true
},
{
- //action: 'cancel',
label: mw.message('th-dialog-btn-cancel').plain(),
flags: 'safe'
}
@@ -36,8 +36,6 @@
th.ui.QuestionDialog.prototype.initialize = function () {
th.ui.QuestionDialog.super.prototype.initialize.apply( this, arguments
);
- var config = mw.teahouse.getConfig();
-
this.content = new OO.ui.PanelLayout( {
$: this.$,
padded: true,
@@ -45,7 +43,6 @@
expanded: false
} );
- //this.tiQuestion = new OO.ui.SearchWidget();
this.tiQuestion = new OO.ui.TextInputWidget({
classes: [ 'th-inputWidget', 'th-tiQuestion' ]
});
@@ -89,10 +86,10 @@
this.pnlSimilarQuestions = $('<div>');
var anchor = mw.html.element( 'a', {
- href: mw.util.getUrl( config.basePage ),
- title: config.basePage,
+ href: mw.util.getUrl( this._config.basePage ),
+ title: this._config.basePage,
target: '_blank'
- }, config.basePage );
+ }, this._config.basePage );
this.content.$element.append(
$('<p>').append( mw.message('th-dialog-description-top',
anchor).text() )
@@ -110,46 +107,21 @@
th.ui.QuestionDialog.prototype.onTiQuestionChange = function( value ) {
var me = this;
- var config = mw.teahouse.getConfig();
var actions = this.getActions();
- this.pnlSimilarQuestions.empty();
+
//actions.list[0] is the save/publish action
//If there is no question entered or it is way too short to be a
question
//we disable the "save" action
if(value !== '' && value.length > 3) {
- var api = new mw.Api();
- api.get({
- action: 'query',
- list: 'search',
- srsearch: value,
- srlimit: 50,
- srnamespace: 4 //NS_PROJECT --> todo: make configurable
- })
- .done(function( response, jqXHR ){
- var list =
$('<ul>').addClass('th-similar-questions-list');
- var count = 0;
- for( var i = 0; i < response.query.search.length &&
count < 5; i++ ) {
- var title = response.query.search[i].title;
- if( title.indexOf( config.basePage ) !== 0 ) {
- continue;
- }
-
- var displayTitle = title.replace(
config.basePage, '' );
- var anchor = mw.html.element( 'a', {
- href: mw.util.getUrl( title ),
- title: title,
- target: '_blank'
- }, title );
- list.append( '<li>'+anchor+'</li>' );
- count++;
- }
- me.pnlSimilarQuestions.append( list );
+ mw.teahouse.board.getSimilarQuestions( value,
this.pnlSimilarQuestions, function() {
me.getManager().updateWindowSize( me );
});
actions.list[0].setDisabled(false);
return;
}
+
+ this.pnlSimilarQuestions.empty();
this.getManager().updateWindowSize( this );
actions.list[0].setDisabled(true);
};
@@ -173,16 +145,10 @@
th.ui.QuestionDialog.prototype.getActionProcess = function ( action ) {
var me = this;
- var config = mw.teahouse.getConfig();
var text = this.tiDesc.getValue() + ''; //Implicit conversion to string.
//Don't know why it is necessary but otherwise adding a "\n" will break
//the string
-
- //We append a signature WikiText fragment by default
- if( text.indexOf( '--~~~~' ) === false ) {
- text += "\n--~~~~";
- }
var question = {
title: this.tiQuestion.getValue(),
@@ -190,42 +156,36 @@
};
if ( action === 'save' ) {
- return new OO.ui.Process( function () {
- //TODO: Move out of dialog implementation and into "The
Board"
- var api = new mw.Api();
- return api.postWithToken( 'edit', {
- action: 'edit',
- title: config.basePage + "/" + question.title,
- summary: "Added by Teahouse gadget",
- text: question.text
- })
- .fail(function( response, jqXHR ){
- console.log(arguments);
- })
- .done(function( response, jqXHR ){
+ return new OO.ui.Process( function() {
+ return mw.teahouse.board.publishQuestion( question )
+ .done(function( editdata ) {
+
//TODO: add a parameter whether to reset fields
or not
//This parameter will be passed to
'getTeardownProcess'
- me.close( { action: action } );
- })
- .then(function( response, jqXHR ){
- var msg = mw.message('th-dialog-msg-text-save',
response.edit.title ).text();
+ me.close( { action: action } )
+ .done(function( data ){
- mw.teahouse.openMessageDialog({
- title:
mw.message('th-dialog-msg-title-save').plain(),
- message: msg,
- actions: [
- { label:
mw.message('th-dialog-msg-btn-yes').plain(), action: 'yes' },
- { label:
mw.message('th-dialog-msg-btn-no').plain(), action: 'no' }
- ]
- }).then( function ( opening ) {
- opening.then( function ( opened ) {
- opened.then( function ( data ) {
- if( data.action ===
'yes' ) {
-
window.location.href = mw.util.getUrl( config.basePage );
- }
+ var anchor =
mw.html.element('a', {
+ href: mw.util.getUrl(
editdata.questionpage.title ),
+ title:
editdata.questionpage.title,
+ target: '_blank'
+ }, editdata.questionpage.title
);
+
+ var msg =
mw.message('th-dialog-msg-text-save', anchor ).text();
+
+
mw.teahouse.dialog.openMessageDialog({
+ title:
mw.message('th-dialog-msg-title-save').plain(),
+ message: msg
+ }, function ( opening ) {
+ opening.then( function
( opened ) {
+ opened.then(
function ( data ) {
+ if(
data.action === 'yes' ) {
+
window.location.href = mw.util.getUrl( me._config.basePage );
+ }
+ });
+ });
});
});
- });
});
});
}
--
To view, visit https://gerrit.wikimedia.org/r/166749
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I525195eac98ff7addd6f47546f40d50daf51d707
Gerrit-PatchSet: 13
Gerrit-Project: mediawiki/extensions/Teahouse
Gerrit-Branch: master
Gerrit-Owner: Robert Vogel <[email protected]>
Gerrit-Reviewer: Robert Vogel <[email protected]>
Gerrit-Reviewer: Smuggli <[email protected]>
Gerrit-Reviewer: Tweichart <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits