UploadWizard/Software design/front-end
Developers' guide to UploadWizard
editThe main overview: a simple PHP script is invoked via Special:UploadWizard.
For older/incompatible browsers, this simply shows the basic upload form. For most / modern browsers, this loads a funky Javascript application which under normal circumstances does not reload the page, but shows a multi-step wizard with Javascript techniques. It manages most interactions via the API and dynamic HTML construction.
We have also written a lot of stuff related to stashed uploads that lives in includes/filerepo and includes/upload .
The general strategy
editWe add uploads to the page. In some browsers we can extract some local metadata, even thumbnails.
When the user is ready, we upload each file via the API to a temporary area, called the Upload Stash.
Then we poll the server to get even more metadata about the file we just uploaded, using the libraries in MediaWiki to analyze the file.
Then we gather data from the user in little form controls to describe the file. We move them through various steps and try to ensure they enter correct and useful data.
Then, when the user is finally ready to commit, we poll those little form controls and ask them to render the data they hold as wikitext. We combine all the wikitext into one string for each upload.
Then we send another API call to tell the server to move the file(s) out of the temporary area, and to create a page for it with the wikitext we generated.
The usual boilerplate
edit- UploadWizard.alias.php -- the Special page name in various languages
- UploadWizard.config.php -- a configuration file in PHP. Can be overridden by similar config in LocalSettings, in $wgUploadWizardConfig.
- UploadWizard.i18n.php -- message strings
- UploadWizardHooks.php -- We don't use any hooks, so this is just for ResourceLoader package definition, strings, etc. If you add a new JS file or a new string, you must modify this file too.
The PHP part
editThe PHP part is fairly minimal.
- UploadWizard.php -- the usual boilerplate, simply includes all the files needed, and registers Special:UploadWizard
- SpecialUploadWizard.php -- where the special page starts! Basic HTML is laid down here, which is enhanced by UploadWizard.js.
- UploadWizardTutorial.php -- the tutorial step of the wizard, which may use InstantCommons to fetch the tutorial, in the proper language version. Broken out into its own file just for simplicity.
- UploadWizardPage.js -- kicks off the javascript on the page, creates an mw.UploadWizard object and populates it.
Javascript and resources
editWe are using ResourceLoader to load resources such as JS and many image files, compressed and optimized. Our package is defined in UploadWizardHooks.php; if you add a new string, JS file or image file, then you have to update that file to get it loaded.
If you would like to develop and debug JavaScript code you almost certainly want to set in LocalSettings.php:
$wgResourceLoaderDebug = true;
which will cause the JS files to be loaded separately, uncompressed. (ResourceLoader is still running, but it's just not optimizing, compressing or combining the files.) Then it's much easier to use a debugger like Firefox's Firebug or Chrome's Inspector.
All of the main meat of this extension is in the JavaScript, and ancillary files, which are located in UploadWizard/resources.
Objects are usually placed into the mediaWiki namespace, which is usually aliased to mw. Some jQuery extensions go into the jQuery namespace, obviouly. Typically $j is used for Javascript, rather than $.
Each file is usually wrapped with the formula:
( function( mw, $j ) { ... code here ... } ( mediaWiki, jQuery ) );
which aliases the global mediaWiki and jQuery objects into some local abbreviations.
mw.UploadWizard* libraries
edit- mw.UploadWizard.js -- contains two main objects.
- mw.UploadWizard -- the object that represents the entire UploadWizard. It is responsible for:
- setting up the different "steps" -- which are just divs that are hidden and shown -- and the buttons to to the next step (or retry the current step)
- transitioning between "steps"
- keeping track of all the uploads, in an uploads property
- handling simultaneous uploads, and having the interface respond instantly to changes in the state of each upload, and detecting when all the uploads are in a state where it's good to procceed.
- running the progress bar
- mw.UploadWizardUpload -- object that represents an Upload
- contains all data extracted locally about the file, or from API calls -- includes thumbnails, imageinfo, filename, title, etc.
- handles the actual upload (via other libraries; it is possible to swap its upload method with a mock uploader, for instance, when testing.)
- has a ui property which points to an "UploadWizardUploadInterface" object.
- mw.UploadWizard -- the object that represents the entire UploadWizard. It is responsible for:
- mw.UploadWizardUploadInterface.js
- mw.UploadWizardUploadInterface -- represents the interface of an Upload (only on page 1, where we are actually uploading the data).
- each upload has a ui property, this is an mw.UploadWizardUploadInterface.
- responsible for showing appropriate interface as the upload proceeds, showing errors, etc.
- there are sometimes complex dances going back and forth between UploadWizardUpload and UploadWizardUploadInterface. The basic rule is that the upload object contains logic, the uploadInterface object is interface. But only while uploading.
- mw.UploadWizardUploadInterface -- represents the interface of an Upload (only on page 1, where we are actually uploading the data).
- mw.UploadWizardDeed.js
- Represents a 'deed' interface and object which is the way for the user to dedicate the work to Commons. A deed is relatively abstract and comes in two flavors, right now : the "ownwork" deed, if it's the uploader's own work, and the "3rd party" deed, if it's not. The UploadWizardDeed object will present a certain interface to the user, and is then responsible for generating the correct wikitext.
- mw.UploadWizardLicenseInput.js
- Represents a license, in interface and object. Like UploadWizardDeed.js its responsibility is to show an appropriate interface to the user to pick a license, and at the right time, to generate wikitext that represents that choice. In practice, it is incorporated into the form created by UploadWizardDeed.
- mw.UploadWizardDescription.js
- Represents a "description" block, combining the software object and the interface, as shown on the "Describe" step of the uploadwizard. Responsible for showing the form, and also generating the wikitext when asked.
- mw.UploadWizardUtil.js
- Miscellaneous utility functions
Other libraries
editOther things to know
editUpload.state and makeTransitioner
editThe wizard keeps track of a number of uploads. Each upload has a 'state'. Based on user interaction, the wizard will transition each upload from state to state.
upload.state is just a string property. States that the upload can be in, include:
Normal states: new transporting (uploading data) transported (uploaded data) metadata (getting metadata about the file) stashed license (on the license page) details (on the details page) submitting-details complete thanks (on the thanks page) Error states: error aborted
Sometimes we manage upload.state fairly simply, by just assigning it. Other times we want to fire off simultaneous processes, and block until all the uploads are in a certain state (or have reached an error state), all the while updating the interface as to our progress. To do this we use an function called 'makeTransitioner' which returns a closure, which, when executed, handles the job. The number of simultaneous processes is controlled by the maxSimultaneousConnections configuration item.
We use makeTransitioner when uploading the files to the stash, and when submitting details.
Message strings
editUploadWizard is fully internationalized. If you want to add a new message,
- create it in the English ('en') section of UploadWizard.i18n.php, using similar conventions to the other messages (use the same prefix).
- add it to UploadWizardHooks.php's message section (sadly we have to tell ResourceLoader to load each message individually)