Add preferences interface!
Lots going on here, I contemplated summarizing it, but… To be continued. This implements the interface and adds a couple of prefs, more still need to be decided and added.
This commit is contained in:
parent
083985e592
commit
29f68565a1
@ -70,3 +70,65 @@ div.gradient-button, .gradient-button {
|
||||
}
|
||||
}
|
||||
|
||||
/* Form styling */
|
||||
.afch-form {
|
||||
.afch-option {
|
||||
display: block;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.afch-label {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
select .afch-input {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
.afch-textfield {
|
||||
-webkit-appearance: none;
|
||||
border: 1px solid @lightgray;
|
||||
border-collapse: collapse;
|
||||
box-sizing: border-box;
|
||||
cursor: auto;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
height: 30px;
|
||||
margin: 0px;
|
||||
padding-left: 5px;
|
||||
vertical-align: middle;
|
||||
width: 25%;
|
||||
min-width: 150px;
|
||||
|
||||
&:focus {
|
||||
border: 1px solid @gray;
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.afch-textarea {
|
||||
height: 150px;
|
||||
width: 50%;
|
||||
margin-top: 5px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
label, input {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
label {
|
||||
text-align: left;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
label + input[type=text] {
|
||||
width: 30%;
|
||||
min-width: 250px;
|
||||
}
|
||||
|
||||
input + input {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
@ -94,51 +94,6 @@
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* Label/input styling */
|
||||
|
||||
.afch-option {
|
||||
display: block;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.afch-label {
|
||||
font-size: 0.75em;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
select .afch-input {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
.afch-textfield {
|
||||
-webkit-appearance: none;
|
||||
border: 1px solid @lightgray;
|
||||
border-collapse: collapse;
|
||||
box-sizing: border-box;
|
||||
cursor: auto;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
height: 30px;
|
||||
margin: 0px;
|
||||
padding-left: 5px;
|
||||
vertical-align: middle;
|
||||
width: 25%;
|
||||
min-width: 150px;
|
||||
|
||||
&:focus {
|
||||
border: 1px solid @gray;
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.afch-textarea {
|
||||
height: 150px;
|
||||
width: 50%;
|
||||
margin-top: 5px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
/* MAIN (+SPLASH) */
|
||||
|
||||
.initial,
|
||||
@ -158,10 +113,6 @@
|
||||
font-size: 15px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.feedback-link {
|
||||
padding-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.warnings {
|
||||
@ -281,23 +232,10 @@
|
||||
.accept {
|
||||
.ThemedPanel(@green);
|
||||
|
||||
label, input {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
label {
|
||||
.afch-label {
|
||||
font-size: 0.75em;
|
||||
margin-top: 12px;
|
||||
width: 15%;
|
||||
text-align: left;
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
label + input[type=text] {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
input + input {
|
||||
float: right;
|
||||
}
|
||||
|
||||
select#lifeStatus.afch-input {
|
||||
|
@ -60,10 +60,9 @@
|
||||
|
||||
AFCH.api = new mw.Api();
|
||||
|
||||
// FIXME: Add real preferences code!
|
||||
AFCH.prefs = {
|
||||
summaryAd: ' ([[WP:AFCHRW|afch-rewrite]] ' + AFCH.consts.version + ')'
|
||||
};
|
||||
// Set up the preferences interface
|
||||
AFCH.preferences = new AFCH.Preferences();
|
||||
AFCH.prefs = AFCH.preferences.prefStore;
|
||||
|
||||
// Add more constants -- don't overwrite those already set, though
|
||||
AFCH.consts = $.extend( {}, {
|
||||
@ -78,8 +77,8 @@
|
||||
nullstatus: { update: function () { return; } },
|
||||
// Current user
|
||||
user: mw.user.getName(),
|
||||
// Wiki id/database name, e.g. "enwiki"
|
||||
wikiId: mw.config.get( 'wgDBname' ),
|
||||
// Edit summary ad
|
||||
summaryAd: ' ([[WP:AFCHRW|afch-rewrite]] ' + AFCH.consts.version + ')',
|
||||
// Require users to be on whitelist to use the script
|
||||
whitelistRequired: true,
|
||||
// Name of the whitelist page for reviewers
|
||||
@ -143,6 +142,7 @@
|
||||
// jquery resources
|
||||
'jquery.chosen',
|
||||
'jquery.spinner',
|
||||
'jquery.ui.dialog',
|
||||
|
||||
// mediawiki.api
|
||||
'mediawiki.api',
|
||||
@ -169,8 +169,8 @@
|
||||
initFeedback: function ( $element, type, linkText ) {
|
||||
var feedback = new mw.Feedback( {
|
||||
title: new mw.Title( 'Wikipedia talk:WikiProject Articles for creation/Helper script/Rewrite' ),
|
||||
bugsLink: 'https://github.com/WPAFC/afch-rewrite/issues/new',
|
||||
bugsListLink: 'https://github.com/WPAFC/afch-rewrite/issues?state=open'
|
||||
bugsLink: 'https://en.wikipedia.org/w/index.php?title=Wikipedia_talk:WikiProject_Articles_for_creation/Helper_script/Rewrite&action=edit§ion=new',
|
||||
bugsListLink: 'https://en.wikipedia.org/w/index.php?title=Wikipedia_talk:WikiProject_Articles_for_creation/Helper_script/Rewrite'
|
||||
} );
|
||||
$( '<span>' )
|
||||
.text( linkText || 'Give feedback!' )
|
||||
@ -553,7 +553,7 @@
|
||||
action: 'edit',
|
||||
text: options.contents,
|
||||
title: pagename,
|
||||
summary: options.summary + AFCH.prefs.summaryAd
|
||||
summary: options.summary + AFCH.consts.summaryAd
|
||||
};
|
||||
|
||||
// Depending on mode, set appendtext=text or prependtext=text,
|
||||
@ -636,7 +636,7 @@
|
||||
action: 'move',
|
||||
from: oldTitle,
|
||||
to: newTitle,
|
||||
reason: reason + AFCH.prefs.summaryAd
|
||||
reason: reason + AFCH.consts.summaryAd
|
||||
}, additionalParameters );
|
||||
|
||||
if ( AFCH.consts.mockItUp ) {
|
||||
@ -709,6 +709,11 @@
|
||||
logPage = new AFCH.Page( 'User:' + mw.config.get( 'wgUserName' ) + '/' +
|
||||
( window.Twinkle && window.Twinkle.getPref( 'speedyLogPageName' ) || 'CSD log' ) );
|
||||
|
||||
// Abort if user disabled in preferences
|
||||
if ( !AFCH.prefs.logCsd ) {
|
||||
return;
|
||||
}
|
||||
|
||||
logPage.getText().done( function ( logText ) {
|
||||
var status,
|
||||
date = new Date(),
|
||||
@ -984,11 +989,160 @@
|
||||
return JSON.parse( cached );
|
||||
}
|
||||
|
||||
// Prevent JSON.parse from exploding on the `undefined`
|
||||
if ( !fallback ) {
|
||||
fallback = false;
|
||||
}
|
||||
|
||||
// Otherwise just use mw.user.options
|
||||
return JSON.parse( mw.user.options.get( fullKey, JSON.stringify( fallback ) ) );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* AFCH.Preferences is a mechanism for accessing and altering user
|
||||
* preferences in regards to the script.
|
||||
*
|
||||
* Preferences are edited by the user via a jquery.ui.dialog and are
|
||||
* saved and persist for the user using AFCH.userData.
|
||||
*
|
||||
* Typical usage:
|
||||
* AFCH.preferences = new AFCH.Preferences();
|
||||
* AFCH.preferences.initLink( $( '.put-prefs-link-here' ) );
|
||||
*
|
||||
* @type {object}
|
||||
*/
|
||||
Preferences: function () {
|
||||
var prefs = this;
|
||||
|
||||
/**
|
||||
* Default values for user preferences; details for each preference can be
|
||||
* found inline in `templates/tpl-preferences.html`.
|
||||
* @type {object}
|
||||
*/
|
||||
this.prefDefaults = {
|
||||
autoOpen: false,
|
||||
logCsd: true
|
||||
};
|
||||
|
||||
/**
|
||||
* Current user's preferences
|
||||
* @type {object}
|
||||
*/
|
||||
this.prefStore = AFCH.userData.get( 'preferences', this.prefDefaults );
|
||||
|
||||
/**
|
||||
* Initializes the preferences modification dialog
|
||||
*/
|
||||
this.initDialog = function () {
|
||||
var $spinner = $.createSpinner( {
|
||||
size: 'large',
|
||||
type: 'block'
|
||||
} ).css( 'padding', '20px' );
|
||||
|
||||
if ( !this.$dialog ) {
|
||||
// Initialize the $dialog div
|
||||
this.$dialog = $( '<div>' );
|
||||
}
|
||||
|
||||
// Until we finish lazy-loading the prefs interface,
|
||||
// show a spinner in its place.
|
||||
this.$dialog.empty().append( $spinner );
|
||||
|
||||
this.$dialog.dialog( {
|
||||
width: 500,
|
||||
autoOpen: false,
|
||||
title: 'AFCH Preferences',
|
||||
modal: true,
|
||||
buttons: [
|
||||
{
|
||||
text: 'Cancel',
|
||||
click: function () {
|
||||
prefs.$dialog.dialog( 'close' );
|
||||
}
|
||||
},
|
||||
{
|
||||
text: 'Save preferences',
|
||||
click: function () {
|
||||
prefs.save();
|
||||
prefs.$dialog.empty().append( $spinner );
|
||||
}
|
||||
}
|
||||
]
|
||||
} );
|
||||
|
||||
// If we've already fetched the template, render immediately
|
||||
if ( this.views ) {
|
||||
this.renderMain();
|
||||
} else {
|
||||
// Otherwise, load the template file and *then* render
|
||||
$.ajax( {
|
||||
type: 'GET',
|
||||
url: AFCH.consts.baseurl + '/tpl-preferences.js',
|
||||
dataType: 'text'
|
||||
} ).done( function ( data ) {
|
||||
prefs.views = new AFCH.Views( data );
|
||||
prefs.renderMain();
|
||||
} );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders the main preferences menu in the $dialog
|
||||
*/
|
||||
this.renderMain = function () {
|
||||
if ( !( this.views && this.$dialog ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Empty the dialog and render the preferences view. Provides the values of all
|
||||
// of the preferences as variables, as well as an additional few used in other locations.
|
||||
this.$dialog.empty().append(
|
||||
this.views.renderView( 'preferences', $.extend( {}, this.prefStore, {
|
||||
version: AFCH.consts.version,
|
||||
versionName: AFCH.consts.versionName
|
||||
} ) )
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates prefs based on data in the dialog which
|
||||
* is created in AFCH.preferences.init().
|
||||
*/
|
||||
this.save = function () {
|
||||
// First, hide the buttons so the user won't start multiple actions
|
||||
this.$dialog.dialog( { buttons: [] } );
|
||||
|
||||
// Now update the prefStore
|
||||
$.extend( this.prefStore, AFCH.getFormValues( this.$dialog.find( '.afch-input' ) ) );
|
||||
|
||||
// Set the new userData value
|
||||
AFCH.userData.set( 'preferences', this.prefStore ).done( function () {
|
||||
// When we're done, close the dialog and notify the user
|
||||
prefs.$dialog.dialog( 'close' );
|
||||
mw.notify( 'AFCH: Preferences saved successfully! They will take effect when the current page is ' +
|
||||
'reloaded or when you browse to another page.' );
|
||||
} );
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a link to launch the preferences modification dialog
|
||||
*
|
||||
* @param {jQuery} $element element to append the link to
|
||||
* @param {string} linkText text to display in the link
|
||||
*/
|
||||
this.initLink = function ( $element, linkText ) {
|
||||
$( '<span>' )
|
||||
.text( linkText || 'Update preferences' )
|
||||
.addClass( 'preferences-link link' )
|
||||
.appendTo( $element )
|
||||
.click( function () {
|
||||
prefs.initDialog();
|
||||
prefs.$dialog.dialog( 'open' );
|
||||
} );
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Represents a series of "views", aka templateable thingamajigs.
|
||||
* When creating a set of views, they are loaded from a given piece of
|
||||
@ -1082,6 +1236,56 @@
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the values of all elements matched by a selector, including
|
||||
* converting checkboxes to bools, providing textual values of select
|
||||
* elements, ignoring placeholder elements, and more.
|
||||
*
|
||||
* @param {jQuery} $selector elements to get values from
|
||||
* @return {object} object of values, with the ids as keys
|
||||
*/
|
||||
getFormValues: function ( $selector ) {
|
||||
var data = {};
|
||||
|
||||
$selector.each( function ( _, element ) {
|
||||
var value, allTexts,
|
||||
$element = $( element );
|
||||
|
||||
if ( element.type === 'checkbox' ) {
|
||||
value = element.checked;
|
||||
} else {
|
||||
value = $element.val();
|
||||
|
||||
// Ignore placeholder text
|
||||
if ( value === $element.attr( 'placeholder' ) ) {
|
||||
value = '';
|
||||
}
|
||||
|
||||
// For <select multiple> with nothing selected, jQuery returns null...
|
||||
// convert that to an empty array so that $.each() won't explode later
|
||||
if ( value === null ) {
|
||||
value = [];
|
||||
}
|
||||
|
||||
// Also provide the full text of the selected options in <select>.
|
||||
// Primary use for this is the edit summary in handleDecline().
|
||||
if ( element.nodeName.toLowerCase() === 'select' ) {
|
||||
allTexts = [];
|
||||
|
||||
$element.find( 'option:selected' ).each( function () {
|
||||
allTexts.push( $( this ).text() );
|
||||
} );
|
||||
|
||||
data[element.id + 'Texts'] = allTexts;
|
||||
}
|
||||
}
|
||||
|
||||
data[element.id] = value;
|
||||
} );
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates an <a> element that links to a given page
|
||||
* @param {string} pagename
|
||||
|
@ -662,9 +662,13 @@
|
||||
$afchLaunchLink = $( mw.util.addPortletLink( 'p-cactions', '#', 'Review (AFCH)',
|
||||
'ca-afch', 'Review submission using afch-rewrite', '1' ) );
|
||||
|
||||
// When clicked, launch AFCH (only once to prevent serious
|
||||
// befuddlement, both for the user and for the script...)
|
||||
$afchLaunchLink.one( 'click', createAFCHInstance );
|
||||
if ( AFCH.prefs.autoOpen ) {
|
||||
// Launch the script immediately
|
||||
createAFCHInstance();
|
||||
} else {
|
||||
// Otherwise, wait for a click (`one` to prevent spawning multiple panels)
|
||||
$afchLaunchLink.one( 'click', createAFCHInstance );
|
||||
}
|
||||
|
||||
// Mark launch link for the old helper script as "old" if present on page
|
||||
$( '#p-cactions #ca-afcHelper > a' ).append( ' (old)' );
|
||||
@ -805,8 +809,9 @@
|
||||
}
|
||||
} );
|
||||
|
||||
// Add the feedback link to the header
|
||||
AFCH.initFeedback( $afch.find( '.initial-header > span' ), '[your topic here]', '(Give feedback!)' );
|
||||
// Add feedback and preferences links
|
||||
AFCH.initFeedback( $afch.find( 'span.feedback-wrapper' ), '[your topic here]', 'give feedback' );
|
||||
AFCH.preferences.initLink( $afch.find( 'span.preferences-wrapper' ), 'preferences' );
|
||||
|
||||
// Set up click handlers
|
||||
$afch.find( '#afchAccept' ).click( function () { spinnerAndRun( showAcceptOptions ); } );
|
||||
@ -1196,41 +1201,7 @@
|
||||
data.afchText = new AFCH.Text( text );
|
||||
|
||||
// Also provide the values for each afch-input element
|
||||
$afch.find( '.afch-input' ).each( function ( _, element ) {
|
||||
var value, allTexts,
|
||||
$element = $( element );
|
||||
|
||||
if ( element.type === 'checkbox' ) {
|
||||
value = element.checked;
|
||||
} else {
|
||||
value = $element.val();
|
||||
|
||||
// Ignore placeholder text
|
||||
if ( value === $element.attr( 'placeholder' ) ) {
|
||||
value = '';
|
||||
}
|
||||
|
||||
// For <select multiple> with nothing selected, jQuery returns null...
|
||||
// convert that to an empty array so that $.each() won't explode later
|
||||
if ( value === null ) {
|
||||
value = [];
|
||||
}
|
||||
|
||||
// Also provide the full text of the selected options in <select>.
|
||||
// Primary use for this is the edit summary in handleDecline().
|
||||
if ( element.nodeName.toLowerCase() === 'select' ) {
|
||||
allTexts = [];
|
||||
|
||||
$element.find( 'option:selected' ).each( function () {
|
||||
allTexts.push( $( this ).text() );
|
||||
} );
|
||||
|
||||
data[element.id + 'Texts'] = allTexts;
|
||||
}
|
||||
}
|
||||
|
||||
data[element.id] = value;
|
||||
} );
|
||||
$.extend( data, AFCH.getFormValues( $afch.find( '.afch-input' ) ) );
|
||||
|
||||
prepareForProcessing();
|
||||
|
||||
|
20
src/templates/tpl-preferences.html
Normal file
20
src/templates/tpl-preferences.html
Normal file
@ -0,0 +1,20 @@
|
||||
//<nowiki>
|
||||
|
||||
<!-- preferences -->
|
||||
<div class="afch afch-form afch-preferences">
|
||||
<div class="details centered">
|
||||
<strong>AFCH v{{version}} / {{versionName}}</strong>
|
||||
</div>
|
||||
<hr>
|
||||
<div id="autoOpenWrapper">
|
||||
<label for="autoOpen" class="afch-label">Automatically open the review panel on AfC submissions</label>
|
||||
<input type="checkbox" id="autoOpen" class="afch-input" {{#autoOpen}}checked{{/autoOpen}} />
|
||||
</div>
|
||||
<div id="logCsdWrapper">
|
||||
<label for="logCsd" class="afch-label">Log speedy deletion nominations</label>
|
||||
<input type="checkbox" id="logCsd" class="afch-input" {{#logCsd}}checked{{/logCsd}} />
|
||||
</div>
|
||||
</div>
|
||||
<!-- /preferences -->
|
||||
|
||||
//</nowiki>
|
@ -3,7 +3,8 @@
|
||||
<!-- main -->
|
||||
<div class="review-panel initial">
|
||||
<div class="initial-header">
|
||||
<span>AFCH v<span title="{{versionName}}">{{version}}</span></span>
|
||||
<span>AFCH v<span title="{{versionName}}">{{version}}</span>
|
||||
(<span class="feedback-wrapper"></span> | <span class="preferences-wrapper"></span>)</span>
|
||||
Reviewing "{{title}}"
|
||||
</div>
|
||||
<div class="warnings hidden"></div>
|
||||
@ -57,7 +58,7 @@
|
||||
<!-- accept -->
|
||||
<div class="review-panel accept">
|
||||
<div class="header">Accepting...</div>
|
||||
<div id="afchContent" class="content">
|
||||
<div id="afchContent" class="content afch-form">
|
||||
|
||||
<div id="newTitleWrapper">
|
||||
<label for="newTitle" class="afch-label">New title</label>
|
||||
|
Loading…
x
Reference in New Issue
Block a user