It's important to maintain a WW that works without JavaScript, which will require attention as I bring more features into Ajax actions. (How important is it?)
The non-javascript, existing WW features work by calling mostly ManageProject with special ww-action= values in the URL. That triggers a call to my WWAction classes, which do the actions and produce output to be included in the HTML page.
Ajax versions of the same actions are implemented by calling api.php, which triggers calls to WW-specific subclasses of MediaWiki's ApiBase class.
As I add more ApiBase subclasses, I'm in danger of duplicating all the existing WWAction classes, which would be bad. It's a waste of effort, and the code duplication would lead to fixes in the API code not getting implemented in the WWAction code, and the non-JavaScript implementation of WW would crumble and stop working with nobody noticing.
Much better to develop a framework allowing a single piece of code to be called for both kinds of actions - shouldn't be a big deal, really.
Or at least make sure they both contain nothing but the simplest calls to core functions that are the same in both cases. Currently this is not so - for instance, the import-project-file API action does a significant amount of work distinguishing different cases, source files vs. project files for instance, that is different from any other WW interface.
Anonymous
PS. DRY = https://en.wikipedia.org/wiki/Don't_repeat_yourself
There is also a whole system of PEOperation classes in ProjectEngine, implementing the sync, make, delete, retrieve and such actions that we ask PE to do. Many of them are the implementation layer of corresponding WWAction classes. But those are separate by design, because WW is the interface between MW and PE, and PE is the interface between WW and the filesystem.
So what does a WWAction operation currently need to do, and what does an API action need to do?
WWAction:
API actions:
So my first impression is that the API class interface encompasses just about everything that WWAction needs, so I could reimplement all the actions as API classes and provide a WWAction interface that calls those and transduces their input and output. They might need to do a little extra to handle the confirmation questions: I think I could do that by defining an extra method they can implement to explain how to prompt for missing parameters.
In other words: instead of writing a bunch of new API classes to do all the different operations that I've already implemented as WWAction classes, I'm going to restructure the WWAction classes into API class form, and they will also provide the WWAction operations that they already do. Sweet!
I think I'll do that by leaving the WWActions.php file as is, taking a copy of it, and making the copy into a bunch of API classes, then flip the switch later to start using the new classes for WWAction.
Last edit: Lee Worden 2013-11-09
Argh, but what about the idea of using Comet? If I convert all those classes to API classes, and then find that I have to convert them all to something else so that they can give real-time updates to the caller, it'll be a real pain. I'd better get further into the Comet planning before I do this conversion.
OK so some questions, now that I've done some Comet work and I'm back here.
the Api classes get their parameter values by calling $this->extractRequestParams(). When using WWAction, can I have it put the values from URL parameters into a place where that function will retrieve them?
I'll need to pass in a parameter saying whether to use Comet or just do the work and return something at the end. Should I extend MediaWiki's
formatparameter to include SSE (Comet) output, or add a custom parameter, something like useComet=yes?How to handle dieUsage() (and are there other, similar exit routes?)?
extractRequestParams() calls things like $this->getMain()->getCheck('...').
Which might actually retrieve WWAction's GET parameters, if I use them just right. I think I'll play with that.
doesn't look like I can add SSE to the list of output formats, so I'll just use an extra argument telling me to ignore the requested format and do it my way.
Or, I might be able to shoehorn it in using the ApiCheckCanExecute hook??
If that does work, I could use it while I request a more sane hook get added to the core code. It would allow me to provide a custom subclass of ApiFormatBase to format the output.
It does seem that this object is created before the execute() function is called, so I could have the action classes call on it to output data and headers. I was skeptical, but this does have some appeal. Maybe I'll try it.
Uh oh, ApiCheckCanExecute hook is called too late. Will see if there's something else I can hook into instead.
Regarding adding SSE to the supported output formats, see https://bugzilla.wikimedia.org/show_bug.cgi?id=57637, thank you very much. It'll be possible in MW 1.23.
As for dieUsage(), the ApiBase::die* functions throw exceptions, and ApiMain catches them to do error reporting, so if I'm calling from WWAction I can catch them there and do different reporting. So it seems like I can go ahead and use those for error handling, which is good in the Api framework.
I'm rewriting the merge-background-job action as an API action, with shim code to call that action from the WWAction framework. As part of that process, I've got merge-background-job running as an Ajax action from the browser, and I can switch back and forth between calling it via ww-action=merge-from-background in the URL params and calling it as 'merge-background-job' via api.php. Weirdly, it works via ww-action, but via Ajax it reports that it succeeded, displays an error message: "ProjectEngine: Project pe-ww:http://localhost/wiki:Sandbox not locked." and fails to merge the job.
Last edit: Lee Worden 2013-12-06
This is weird. I need to untangle this odd mergery. One question is why the error isn't reported as an error in the UI. The other one is why the merge doesn't succeed when called by api.php, but the same code works when called by Special:ManageProject.
Both need to be fixed, and I think I'll take them in that order, arbitrarily.
The error reporting is fixed. But the WW messages need to be output differently when there's an error. I'll update the standard now.
And on why the same operation works when it's a 'ww-action' URL option and fails when it's an API operation. I think I see why now.
There's code in the PERequest_hook() function in the background jobs code, and in a bunch of other hook functions in there, that checks whether there's a 'jobid' in the URL parameters, and modifies the meaning of other operations. This allows us to use the "list working directory" page and so on to operate in a background job rather than the persistent directory by adding jobid=XXXX to the URL.
What it does when it detects the jobid is two things: it adds the jobid to the request data sent to ProjectEngine, and if there's no project name given, it adds a 'pe-session-dir' value to the request data to tell ProjectEngine to do the operation in the background session directory. This allows URLs like ...Special:GetProjectFile?filename=.status&jobid=2345 to get the .status file in the base directory of background job 2345 - outside all the project directories in that session.
So that's the context. Actually 'jobid' is not in the URL when we use the URL to merge a background job: it's &ww-action=merge-from-background&action-jobid=2345, to give the ID of the job for the merge operation, not for the rest of the page's operations.
So it doesn't add jobid and pe-session-dir to the PE request, which is right. The jobid is put there explicitly by the merge code, and pe-session-dir needs to not be there, because it breaks the merge operation.
In the API call, on the other hand, we have a URL like api.php?action=ww-merge-background-job&jobid=2345. This triggers the hook's code to set the jobid (which is redundant and harmless) and 'pe-session-dir', which breaks the operation.
I guess one solution is to have it not do that when using api.php rather than index.php... well, no, I need to have it not do that when making an API call, even if it were within an index.php process... I guess this is what RequestContexts are for (introduced in about MW 1.16). If I were passing one of those around, I could just use it and not get confused.
... I don't actually know if RequestContext is quite the right thing for this, actually. Anyway I can do something like it, which is probably a lot better than what I've got now, API aside: I can have the background code evaluate whether the page's operations should all be within a background job at one time, early, and then not check anymore in all the different hooks. I can have it do that near the beginning of S:MP and S:GPF and not anywhere else.
Yes, that was easy, and the API action works now. The old action also works, and it browses files in background jobs correctly. That's better!
I think I have a working merge API action now. I'll check it and deploy it to lalashan.
Keeping in mind that (while useful) it's an Ajax action, and I'm not done until it's got Comet output for full responsiveness.
Should I just develop Comet support for 1.23+, since that's when I'll be able to use format=sse? I kind of burned myself when I developed ImportProjectFiles out of Special:Upload rather than ImportWizard because ImportWizard didn't support 1.19, but then it took so long to develop that we don't care about 1.19 anyway. https://www.mediawiki.org/wiki/Version_lifecycle says that 1.23 will replace 1.19 as the LTS release in May 2014. (Why did I think 1.22 was going to be the new LTS?)
For now, why don't I work on this API/WWAction integration, and come back to the Comet implementation afterward. I have ww-list-background-jobs, ww-create-background-job, ww-merge-background-job, and ww-destroy-background-job in the API framework now, and I think I'll take ww-destroy-background-job as my test case for this integration because it's earlier in the file, and because it includes a confirmation step.
The integrated version of the destroy-background-job action works great - that was easy! It does 3 things along with calling the ww-destroy-background-job api, and other actions will have to do them in various combinations as well:
It'd be great to just have a single WWAction class and an array like
and nothing else on the Action side.
That example definitely suggests that it might be possible to deduce the API action names, also. So actually maybe I can have a single class that deduces API action names and calls them, doing certain standard transformations on the parameters ('action-jobid' to 'jobid', 'action-filename' to 'filename'), using the i18n message data to find out whether to ask for confirmation, and using the i18n message data and/or api's return data to produce the output message...
I'll do a survey of all the existing WWAction classes to see if that would cover what they do...
As promised, survey of WWAction classes to see what kinds of exceptional treatment they need. I'm considering a structure like:
Now I look over the existing WWAction stuff for anything that doesn't fit that pattern.
To summarize:
How many actions are there that will work the default way? Is it worth having a default way?
So yes, it does seem worth it.
With the default, I'll either need to provide a whitelist of API actions that can be called in the default way as WWActions, or there'll be a risk of it calling API actions that aren't intended to work that way. Given that the API actions can be trusted to do permissions checking, though, it doesn't seem like much of a problem. It would be a problem if an API action produces weird output, like the raw .tar.gz output of export-wd or if one produces SSE output by default... But even so, it'd only be a problem if someone hand-constructs an index.php URL with the unsupported action name in it, and they can be responsible for the consequences...
Steps:
Have created the default framework and am using it for destroy-background-job.
The newly invented array $wwConfirmationsForApiActions controls which actions come with a confirmation step, and the text that's used for the confirmation. (though not in more complex confirmations, like the 3-way questions that remove-source-file uses.)
I think the next step is to pass that array to the JS side and use it there as well.
the JS now has access to $wwConfirmationsForApiActions and code to use it, and a bit more infrastructure for general API usage. It's not in use yet - ww-destroy-background-job is all ready to use it, but I want to have more time on my hands for debugging when I switch it over.
even though I said I was going to work on it offline :(