01132: More flexible script & cookbook initialization
Description: There's a better way of doing script and cookbook initialisations than the current dependency on including files in the correct order.
Proof of concept: setup.php
For example, I'm putting together a new PmWiki blog package, but I'm running into some rather thorny issues manipulating the $EditFunctions
array in a sane manner.
More specifically, at the moment I'm looking to overload the EditDraft function, without having to duplicate the contents of scripts/draft.php and other script files. The simple thing to do would be something like this:
if (IsEnabled($EnableDrafts, 0)) { include_once("my_draft.php"); $EnableDrafts = 0; }
But that won't play nice, since drafts should come after simuledit + prefs, and hence those too would have to be included before my_draft.php
, and those in turn might be influenced by stuff in per-page customisations, etc. So the chance of screwing up and/or interfering with other recipes gets pretty high.
The reason it's so difficult is because much of the initialisation code for scripts as well as cookbook recipes happens right when they're included, which means that the order in which you put things in your config file(s) is important. I think this isn't the best way of doing things.
For a fix, I suggest a new table of initialisation functions that could get called from pmwiki.php at the very end of initialisation, just before HandleDispatch. The format for this table should be something similar to the MarkupTable, which handles the exact same problem, just for a different context (the order in which markup rules get implemented).
Regarding my original issue, this table would let me eg. add a function near its end that would safely replace the appropriate $EditFunctions
entry with my own function.
A proof of concept implementation of this SetupTable is attached. To see it in action, include it in a config file and use ?action=setuprules
on a wiki page.
I once talked with Pm about such ordering of functions and PageStore objects, he found it far too complex to do it again. Wouldn't it be simpler but also efficient to do something like:
$EditFunctions
= array(
'EditTemplate' => 100,
'RestorePage' => 200,
'ReplaceOnSave' => 300,
# etc.
);
This would allow us to insert/disable/replace/reorder edit functions, and later PmWiki will asort() the aray and process it. This way will be implemented in FPLTemplate(), I'd like to do it for the upload functions as well, which would add more flexibility than now. --Petko August 06, 2009, at 04:03 AM
That would indeed help with $EditFunctions
, but it's not the only thing I'm looking for with this fix. I would very much like a way to have much more fine-grained control of the order in which stuff happens (or doesn't!) during PmWiki execution. However, my suggested Markup-style table is perhaps overkill, so could we instead have something like this (roughly corresponding to lines 323-344 of pmwiki.php):
$Setup = array( 'SetCurrentTime' => 100, 'ResolvePageName' => 200, "$FarmD/scripts/stdconfig.php" => 300, 'ReadInterMapFiles' => 400 );
Which would get parsed roughly as follows:
asort($Setup); foreach($Setup as $k => $v) { if (!$k || !$v) continue; if (substr($k,-4) == '.php') include_once($k); else $k(); }
Also, regarding your proposed change to $EditFunctions
, wouldn't it be better to use array( 100 => 'EditTemplate', ... )
as then it should be relatively well backwards-compatible with the current form, instead of breaking most recipes that do anything with $EditFunctions
? —Eemeli Aro August 06, 2009, at 06:58 AM
I would love to see greater control over when config code gets executed. In addition to being able to specify an order for initialization with normal config.php I think there should be some capability for executing code after stdconfig, at the very end of producing a page, etc. --Peter Bowers August 06, 2009, at 03:15 PM
Here are a couple of actual use cases where I'd find it very useful to be able to get a function to run near the end of the setup phase. —Eemeli Aro August 17, 2009, at 05:06 AM
- EditAttributes adds two entries to
$EditFunctions
, one of which is meant to run as early as possible as it'll modify the page text and attributes, which other$EditFunctions
may depend on (such as the Bloge new post naming). At the moment, Bloge needs to specifically look forEditAttrBeforePost
and add itself after it, if it exists.
$EditFunctions
or by being able to set a function to run using $Setup
as above.
- A second difficulty exists with EditAttributes, as it needs to set
$EditFields
beforeHandleEdit
is run: the added text fields need to be set before the$EditFunctions
are executed. This means that easy configuration of the recipe requires the$EditAttrFields
to be defined before includingeditattr.php
, which isn't possible is you want to have eg. per-group customizations to edit fields. To fix this, currently you need to execute the following after you've finally set$EditAttrFields
:$EditFields = array_merge($EditFields, array_keys($EditAttrFields));
$Setup
array where a function could execute the above line of code.
draft.php
renames a page when a draft version is published, by removing the-Draft
suffix from the name. This is fine, provided that you're not creating a new page or that you know when starting the draft what the page's eventual name will be. This is not necessarily the case with Bloge when creating a new blog post, which has a name that includes the post's publication date. The difficulty here comes from the rather precise positioning ofEditDraft
in$EditFunctions
in relation to at leastMergeSimulEdits
. Hence overloadingEditDraft
is very difficult at the moment.
$EditFunctions
would need to be changed along with how functions are added to it (to something like SDV($EditFunctions[300],'EditDraft');
in draft.php
) or a $Setup
array could help replace EditDraft
with its own function.$Setup['PageStore'] = array('wiki.d'=>100, 'wikilib.d' => 200, /*etc*/); $Setup['EditFunctions'] = array( 'EditTemplate' => 100, 'RestorePage' => 200, /*etc*/); $Setup['UploadFunctions'] = array( 'UploadVerify'=>100, /*etc*/); $Setup['FmtTemplateFunctions'] = array( /*etc*/); $Setup['init'] = array( 'SetCurrentTime' => 100, 'ResolvePageName' => 200, "$FarmD/scripts/stdconfig.php" => 300, /*etc*/);
The $EditFunctions
will be merged with the ones in $Setup so this should be backwords compatible.
The stuff in the $Setup['init'] array is launched after all group/page customization is read, so a page could more easily disable or reconfigure a recipe. The snippet should also somehow respect $EnableStdConfig
and other similar variables. --Petko August 17, 2009, at 06:28 AM
I think you're talking about a far more in-depth re-working of PmWiki internals than what I was looking for. Not that I'm at all against it, I just think we're having two separate conversations here. So, to address both of them:
I don't think we should restructure $WikiLibDirs
or $EditFunctions
. Too many recipes and custom configurations use them and they fundamentally are not broken. Yes, I'll agree that using them might be easier if you could numerically determine the order of things, but I think the cost would be too great. Also I'm sceptical of backwards-compatibility between your $Setup['EditFunctions']
and the current
: how would you determine where to add entries from one to the other?
$EditFunctions
$UploadFunctions
(from PITS.00785, yes?) and $FmtTemplateFunctions
I'm not familiar enough with to really say anything. However, if/when they are implemented, I think I'd prefer having them as separate global variables like the rest of PmWiki config. If PmWiki is at some point moving to a single $Setup
array type of configuration, I think as an intermediate step we'd need to have the members of that array be references to the pre-existing global variables.
But $Setup['init']
or something like it I do want. The way I see it, it could even initially be an empty array that's run through after stdconfig.php
—in other words, it should not introduce any regressions with cookbook recipes or configurations. It should also be a powerful enough tool to help fix the shortcomings of eg. $EditFunctions
. —Eemeli Aro August 17, 2009, at 07:26 PM
A simple $PostConfig array processed after stdconfig.php was added to subversion, for 2.2.17, unless we discover some problems (speak up). --Petko June 05, 2010, at 07:18 PM
Any possibility it could be moved down a few more lines? If it occurred immediately after this line:
SDV($LinkPageCreateSpaceFmt,$LinkPageCreateFmt);
or even just before this line:
if (IsEnabled($EnableActions, 1)) HandleDispatch($pagename, $action);
then functions can call MarkupToHTML() successfully... Also the later it is the less likely we will run into caching issues...
If there's an advantage to having it in its current location then perhaps there could be some mechanism where functions with sort values less than 100 go in the current location but functions with sort values greater than 100 go in the later location?
Or maybe ...
$PostConfig['A']['myfunc'] = 100; // this will run right after stdconfig is loaded $PostConfig['B']['mylatefunc'] = 200; // this will run after links are defined
with certain predefined values for the index. ( --Peter Bowers June 21, 2010, at 04:20 AM
I'm not sure that functions should call MarkupToHTML() directly from the config files: at the moment, I can't decide if we should try to support this and to keep fixing it every time someone's code breaks it. :-) If someone requires link variables to be defined, they could define them in the script, however direct calling of MarkupToHTML() might work but is still utterly unsupported. PITS:01214. --Petko September 04, 2010, at 11:50 AM
I don't know if I understood the problem completely, but about this:
if (IsEnabled($EnableDrafts
, 0)) {
include_once("my_draft.php");
$EnableDrafts
= 0;
}
I believe this could be solved by setting a variable called $DraftsScript, that when changed will run the Draft Script you want instead of PmWiki's Draft Script.
Also, to change, remove or substitute an entry in EditFunctions more easily we could create a recipe that does that.
I did a really tiny recipe that does just that as it was a MarkupFrame manipulator, it just doesn't replace or remove, it just ads in front or after an already placed EditFunction and also at the beginig or at the end.
I will change the recipe to do replaces and removes as well.
CarlosAB March 17, 2018, at 01:34 AM