MarkAHershberger has uploaded a new change for review. https://gerrit.wikimedia.org/r/210831
Change subject: WIP Show recent uploads in MWMediaSearchWidget ...................................................................... WIP Show recent uploads in MWMediaSearchWidget Initially show the user's recent uploads. Using the search makes them in-accessible. This should really be done with a toggle. A kind of, sort of update of I79d283318585fb677423e607e0851157459b1108 Bug: 60398 Change-Id: I0a1a88933efa9003a75373c61f194f56bb083219 --- M VisualEditor.php M extension.json M modules/ve-mw/dm/models/ve.dm.MWMediaResourceProvider.js M modules/ve-mw/dm/models/ve.dm.MWMediaResourceQueue.js A modules/ve-mw/dm/models/ve.dm.MWRecentUploadsProvider.js M modules/ve-mw/i18n/en.json M modules/ve-mw/i18n/qqq.json M modules/ve-mw/ui/widgets/ve.ui.MWMediaSearchWidget.js 8 files changed, 368 insertions(+), 35 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/VisualEditor refs/changes/31/210831/1 diff --git a/VisualEditor.php b/VisualEditor.php index efd4a3b..01f3e96 100644 --- a/VisualEditor.php +++ b/VisualEditor.php @@ -1037,6 +1037,7 @@ 'scripts' => array( 'modules/ve-mw/dm/models/ve.dm.MWImageModel.js', 'modules/ve-mw/dm/models/ve.dm.MWMediaResourceProvider.js', + 'modules/ve-mw/dm/models/ve.dm.MWRecentUploadsProvider.js', 'modules/ve-mw/dm/models/ve.dm.MWMediaResourceQueue.js', 'modules/ve-mw/ui/widgets/ve.ui.MWMediaSearchWidget.js', diff --git a/extension.json b/extension.json index 7b9623e..3180458 100644 --- a/extension.json +++ b/extension.json @@ -1070,6 +1070,7 @@ "scripts": [ "modules/ve-mw/dm/models/ve.dm.MWImageModel.js", "modules/ve-mw/dm/models/ve.dm.MWMediaResourceProvider.js", + "modules/ve-mw/dm/models/ve.dm.MWRecentUploadsProvider.js", "modules/ve-mw/dm/models/ve.dm.MWMediaResourceQueue.js", "modules/ve-mw/ui/widgets/ve.ui.MWMediaSearchWidget.js", "modules/ve-mw/ui/widgets/ve.ui.MWMediaResultWidget.js", diff --git a/modules/ve-mw/dm/models/ve.dm.MWMediaResourceProvider.js b/modules/ve-mw/dm/models/ve.dm.MWMediaResourceProvider.js index bef33e3..a40067a 100644 --- a/modules/ve-mw/dm/models/ve.dm.MWMediaResourceProvider.js +++ b/modules/ve-mw/dm/models/ve.dm.MWMediaResourceProvider.js @@ -98,7 +98,7 @@ provider = this; return this.loadSiteInfo() - .then( function () { + .then( function () { if ( aborted ) { return $.Deferred().reject(); } @@ -106,7 +106,7 @@ return xhr; } ) .then( - function ( results ) { + function ( results ) { if ( !results || results.length === 0 ) { provider.toggleDepleted( true ); return []; diff --git a/modules/ve-mw/dm/models/ve.dm.MWMediaResourceQueue.js b/modules/ve-mw/dm/models/ve.dm.MWMediaResourceQueue.js index 7c073ff..4bfe282 100644 --- a/modules/ve-mw/dm/models/ve.dm.MWMediaResourceQueue.js +++ b/modules/ve-mw/dm/models/ve.dm.MWMediaResourceQueue.js @@ -17,12 +17,12 @@ * search call to the API. */ ve.dm.MWMediaResourceQueue = function VeDmMWMediaResourceQueue( config ) { - config = config || {}; + config = config || {}; - // Parent constructor - ve.dm.MWMediaResourceQueue.super.call( this, config ); - this.searchQuery = ''; - this.maxHeight = config.maxHeight || 200; + // Parent constructor + ve.dm.MWMediaResourceQueue.super.call( this, config ); + this.searchQuery = ''; + this.maxHeight = config.maxHeight || 200; }; /* Inheritance */ @@ -35,36 +35,57 @@ * @return {jQuery.Promise} Promise that resolves when the resources are set up */ ve.dm.MWMediaResourceQueue.prototype.setup = function () { - var i, len, - queue = this; + var i, len, + queue = this; - return this.getFileRepos().then( function ( sources ) { - if ( queue.providers.length === 0 ) { - // Set up the providers - for ( i = 0, len = sources.length; i < len; i++ ) { - queue.providers.push( new ve.dm.MWMediaResourceProvider( - sources[i].apiurl, - { - name: sources[i].name, - local: sources[i].local, - scriptDirUrl: sources[i].scriptDirUrl, - userParams: { - gsrsearch: queue.getSearchQuery() - }, - staticParams: { - action: 'query', - iiurlheight: queue.getMaxHeight(), - generator: 'search', - gsrnamespace: 6, - continue: '', - iiprop: 'dimensions|url|mediatype|extmetadata|timestamp', - prop: 'imageinfo' - } - } ) - ); + return this.getFileRepos().then( function ( sources ) { + if ( queue.providers.length === 0 ) { + // Set up the providers + for ( i = 0, len = sources.length; i < len; i++ ) { + if ( !mw.user.isAnon() ) { + // This appears to result in a second call to MWMRP.loadSiteInfo() + queue.providers.push( new ve.dm.MWRecentUploadsProvider( + sources[i].apiurl, + { + name: sources[i].name, + local: sources[i].local, + scriptDirUrl: sources[i].scriptDirUrl, + userParams: { + grcuser: mw.user.getName() + }, + staticParams: { + action: 'query', + generator: 'recentchanges', + grcnamespace: 6, + continue: '', + prop: 'imageinfo' + } } + ) ); } - } ); + queue.providers.push( new ve.dm.MWMediaResourceProvider( + sources[i].apiurl, + { + name: sources[i].name, + local: sources[i].local, + scriptDirUrl: sources[i].scriptDirUrl, + userParams: { + gsrsearch: queue.getSearchQuery() + }, + staticParams: { + action: 'query', + iiurlheight: queue.getMaxHeight(), + generator: 'search', + gsrnamespace: 6, + continue: '', + iiprop: 'dimensions|url|mediatype|extmetadata|timestamp', + prop: 'imageinfo' + } + } ) + ); + } + } + } ); }; /** diff --git a/modules/ve-mw/dm/models/ve.dm.MWRecentUploadsProvider.js b/modules/ve-mw/dm/models/ve.dm.MWRecentUploadsProvider.js new file mode 100644 index 0000000..dcb343e --- /dev/null +++ b/modules/ve-mw/dm/models/ve.dm.MWRecentUploadsProvider.js @@ -0,0 +1,306 @@ +/*! + * VisualEditor DataModel MWRecentUploadsProvider class. + * + * @copyright 2011-2015 VisualEditor Team and others; see AUTHORS.txt + * @license The MIT License (MIT); see LICENSE.txt + */ + +/** + * MediaWiki media resource provider. + * + * @class + * @extends ve.dm.APIResultsProvider + * + * @constructor + * @param {string} apiurl The API url + * @param {Object} [config] Configuration options + * @cfg {string} [scriptDirUrl] The url of the API script + */ +ve.dm.MWRecentUploadsProvider = function VeDmMWRecentUploadsProvider( apiurl, config ) { + config = config || {}; + + // Parent constructor + ve.dm.MWRecentUploadsProvider.super.call( this, apiurl, config ); + + // Fetching configuration + this.scriptDirUrl = config.scriptDirUrl; + this.isLocal = config.local; + + if ( config.local ) { + this.setAjaxSettings( { + url: mw.util.wikiScript( 'api' ), + // If the url is local use json + dataType: 'json' + } ); + } else { + this.setAjaxSettings( { + // If 'apiurl' is set, use that. Otherwise, build the url + // from scriptDirUrl and /api.php suffix + url: this.getAPIurl() || ( this.scriptDirUrl + '/api.php' ), + // If the url is not the same origin use jsonp + dataType: 'jsonp', + // JSON-P requests are not cached by default and get a &_=random trail. + // While setting cache=true will still bypass cache in most case due to the + // callback parameter, at least drop the &_=random trail which triggers + // an API warning (invalid parameter). + cache: true + } ); + } + this.siteInfoPromise = null; + this.thumbSizes = []; + this.imageSizes = []; +}; + +/* Inheritance */ +OO.inheritClass( ve.dm.MWRecentUploadsProvider, ve.dm.APIResultsProvider ); + +/* Methods */ + +/** + * Initialize the source and get the site info. + * + * Connect to the api url and retrieve the siteinfo parameters + * that are required for fetching results. + * + * @return {jQuery.Promise} Promise that resolves when the class + * properties are set. + */ +ve.dm.MWRecentUploadsProvider.prototype.loadSiteInfo = function () { + var provider = this; + + if ( !this.siteInfoPromise ) { + this.siteInfoPromise = new mw.Api().get( { + action: 'query', + meta: 'siteinfo' + } ) + .then( function ( data ) { + provider.setImageSizes( data.query.general.imagelimits || [] ); + provider.setThumbSizes( data.query.general.thumblimits || [] ); + provider.setUserParams( { + // Standard width per resource + iiurlwidth: provider.getStandardWidth() + } ); + } ); + } + return this.siteInfoPromise; +}; + +/** + * Override parent method and get results from the source + * + * @param {number} [howMany] The number of items to pull from the API + * @returns {jQuery.Promise} Promise that is resolved into an array + * of available results, or is rejected if no results are available. + */ +ve.dm.MWRecentUploadsProvider.prototype.getResults = function ( howMany ) { + var xhr, + aborted = false, + provider = this; + + return this.loadSiteInfo() + .then( function () { + console.log("loadSiteInfo"); + if ( aborted ) { + console.log("loadSiteInfo reject"); + return $.Deferred().reject(); + } + console.log("fetching " + howMany + " api results"); + xhr = provider.fetchAPIresults( howMany ); + return xhr; + } ) + .then( + function ( results ) { + if ( !results || results.length === 0 ) { + provider.toggleDepleted( true ); + return []; + } + return results; + }, + // Process failed, return an empty promise + function () { + provider.toggleDepleted( true ); + return $.Deferred().resolve( [] ); + } + ) + .promise( { abort: function () { + aborted = true; + if ( xhr ) { + xhr.abort(); + } + } } ); +}; + +/** + * Call the API for search results. + * + * @param {number} howMany The number of results to retrieve + * @return {jQuery.Promise} Promise that resolves with an array of objects that contain + * the fetched data. + */ +ve.dm.MWRecentUploadsProvider.prototype.fetchAPIresults = function ( howMany ) { + console.log( "fetching..." ); + var xhr, + ajaxOptions = {}, + provider = this, + apiCallConfig = $.extend( + {}, + this.getUserParams(), + { + grcstart: this.getOffset(), + iiprop: 'size|url|extmetadata' +// iiextmetadatalanguage: provider.getLang() + } ); + + // Number of images + apiCallConfig.grclimit = howMany || this.getDefaultFetchLimit(); + + if ( !this.isValid() ) { + console.log( "aborting, not valid" ); + return $.Deferred().reject().promise( { abort: $.noop } ); + } + + ajaxOptions = this.getAjaxSettings(); + + console.log( "Actually getting." ); + xhr = new mw.Api().get( $.extend( this.getStaticParams(), apiCallConfig ), ajaxOptions ); + return xhr + .then( function ( data ) { + var page, newObj, raw, + results = []; + + if ( data.error ) { + provider.toggleDepleted( true ); + return []; + } + + // Do we ever want to continue? probably not. + if ( data[ 'continue' ][ 'continue'] !== "||") { + console.debug(data['continue']); + // Update the offset for next time + provider.setOffset( data[ 'continue' ].grcstart ); + } else { + // This is the last available set of results. Mark as depleted! + provider.toggleDepleted( true ); + } + + // If the source returned no results, it will not have a + // query property + if ( data.query ) { + raw = data.query.pages; + if ( raw ) { + // Strip away the page ids + for ( page in raw ) { + console.debug("yah!"); + console.debug(raw[page]); + console.debug(raw[page]); + newObj = raw[page].imageinfo[0]; + newObj.title = raw[page].title; + results.push( newObj ); + } + } + } + return results; + } ) + .promise( { abort: xhr.abort } ); +}; + +/** + * Set name + * + * @param {string} name + */ +ve.dm.MWRecentUploadsProvider.prototype.setName = function ( name ) { + this.name = name; +}; + +/** + * Get name + * + * @returns {string} name + */ +ve.dm.MWRecentUploadsProvider.prototype.getName = function () { + return this.name; +}; + +/** + * Get standard width, based on the provider source's thumb sizes. + * + * @return {number|undefined} fetchWidth + */ +ve.dm.MWRecentUploadsProvider.prototype.getStandardWidth = function () { + return ( this.thumbSizes && this.thumbSizes[ this.thumbSizes.length - 1 ] ) || + ( this.imageSizes && this.imageSizes[ 0 ] ) || + // Fall back on a number + 300; +}; + +/** + * Get prop + * + * @return {string} prop + */ +ve.dm.MWRecentUploadsProvider.prototype.getFetchProp = function () { + return this.fetchProp; +}; + +/** + * Set prop + * + * @param {string} prop + */ +ve.dm.MWRecentUploadsProvider.prototype.setFetchProp = function ( prop ) { + this.fetchProp = prop; +}; + +/** + * Set thumb sizes + * + * @param {number[]} sizes Available thumbnail sizes + */ +ve.dm.MWRecentUploadsProvider.prototype.setThumbSizes = function ( sizes ) { + this.thumbSizes = sizes; +}; + +/** + * Set image sizes + * + * @param {number[]} sizes Available image sizes + */ +ve.dm.MWRecentUploadsProvider.prototype.setImageSizes = function ( sizes ) { + this.imageSizes = sizes; +}; + +/** + * Get thumb sizes + * + * @returns {number[]} sizes Available thumbnail sizes + */ +ve.dm.MWRecentUploadsProvider.prototype.getThumbSizes = function () { + return this.thumbSizes; +}; + +/** + * Get image sizes + * + * @returns {number[]} sizes Available image sizes + */ +ve.dm.MWRecentUploadsProvider.prototype.getImageSizes = function () { + return this.imageSizes; +}; + +/** + * Check if this source is valid and ready for search. + * @return {boolean} Source is valid + */ +ve.dm.MWRecentUploadsProvider.prototype.isValid = function () { + var params = this.getUserParams(); + console.log("Valid?"); + return params.grcuser && + ( + this.isLocal || + // If we don't have either 'apiurl' or 'scriptDirUrl' + // the source is invalid, and we will skip it + this.apiurl !== undefined || + this.scriptDirUrl !== undefined + ); +}; diff --git a/modules/ve-mw/i18n/en.json b/modules/ve-mw/i18n/en.json index 6a60d38..1b37118 100644 --- a/modules/ve-mw/i18n/en.json +++ b/modules/ve-mw/i18n/en.json @@ -250,6 +250,7 @@ "visualeditor-loadwarning-token": "Error loading edit token from server: $1. Would you like to retry?", "visualeditor-mainnamespacepagelink": "Project:Main namespace", "visualeditor-media-input-placeholder": "Search for media", + "visualeditor-media-input-recent-uploads": "Your recent uploads", "visualeditor-meta-tool": "Options", "visualeditor-mweditmodesource-title": "Switch to source editing?", "visualeditor-mweditmodesource-tool": "Switch to source editing", diff --git a/modules/ve-mw/i18n/qqq.json b/modules/ve-mw/i18n/qqq.json index 96001a6..2604c52 100644 --- a/modules/ve-mw/i18n/qqq.json +++ b/modules/ve-mw/i18n/qqq.json @@ -259,6 +259,7 @@ "visualeditor-loadwarning-token": "Text (JavaScript confirm()) shown when the editor fails to load properly.\n\nParameters:\n* $1 - the error message from the server.", "visualeditor-mainnamespacepagelink": "Name of a page describing the main namespace (NS0) in this project.\n{{doc-important|Do not translate \"Project\"; it is automatically converted to the wiki's project namespace.}}", "visualeditor-media-input-placeholder": "Place holder text for media search input", + "visualeditor-media-input-recent-uploads": "Heading for section showing a user's recent uploads", "visualeditor-meta-tool": "Text of tool in the toolbar the lets users set categories, language links and other page settings.\n{{Identical|Options}}", "visualeditor-mweditmodesource-title": "Title of dialog to confirm switching to source mode.", "visualeditor-mweditmodesource-tool": "Label for tool that changes edit mode to source editing.", diff --git a/modules/ve-mw/ui/widgets/ve.ui.MWMediaSearchWidget.js b/modules/ve-mw/ui/widgets/ve.ui.MWMediaSearchWidget.js index d497a7c..f2a135c 100644 --- a/modules/ve-mw/ui/widgets/ve.ui.MWMediaSearchWidget.js +++ b/modules/ve-mw/ui/widgets/ve.ui.MWMediaSearchWidget.js @@ -128,7 +128,9 @@ var search = this, value = this.query.getValue(); - if ( value === '' ) { + // It would be nice to be able to use something from the + // ResourceQueue object here. + if ( value === '' && mw.user.isAnon() ) { return; } -- To view, visit https://gerrit.wikimedia.org/r/210831 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I0a1a88933efa9003a75373c61f194f56bb083219 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/VisualEditor Gerrit-Branch: master Gerrit-Owner: MarkAHershberger <m...@nichework.com> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits