Contents
A more current discussion is there: http://etherpad.osuosl.org/moin2-PluginSystem
Proposal
The current (i.e. "traditional") plugin concept in MoinMoin implies early distinction of plugin types. That is, plugins are organized by type (or call it category) first, and by application focus second. This is reflected in the plugin directory structure:
wiki data plugin action ActionA ActionB parser ParserA ParserB ...
If every plugin would be naturally restricted to one and only one of the predefined types, the given organization would not impose a real problem.
However, I often find myself in a situation where an extension I write for MoinMoin is a mini-application, consisting of two or more technical plugin types. From the viewpoint of a plugin author, having to bounce up and down the above deeply nested directory structure is a maintenance nightmare.
This made me think about the plugin concept in general.
I think there is a misconception in that concept. It enforces the view that a plugin is a unit of a certain type. A much more appropriate view would be that a plugin is a unit of functionality, and parsers, actions and other "types" are the mechanisms through which the plugin "hooks into" moin. This is true at the level of deployment as well as the level of implementation (thereby virtually removing the difference in packaging between the two).
In fact, most other established plugin-enabled applications (like WordPress or Drupal) follow this pattern, rather than imposing a "type-centered" approach.
I propose to gradually shift the plugin organization to one that distinguishes plugin packages first, and by type second:
wiki data plugin A (<= all stuff of plugin "A" goes here) action (<= it's action(s)) ActionA AnotherActionOfA parser (<= it's parser(s)) ParserA ... B (<= all stuff of plugin "B" goes here) action (<= it's action(s)) ActionB parser (<= it's parser(s)) ParserB AnotherParserOfB ...
Contrast to the current organization, the proposal is in alignment with a natural deployment and development schemes, supporting local focus for both.
Discussion
Personally I would prefer to put everything from a related set of plugins into one file (module) or directory (package) and just have some way of telling Moin which classes re actions, macros, parsers, etc. This could be done either with new-style clasess and their __subclasses__ attribute, or more explicitly with some dict or lists. -- RadomirDopieralski
- Radomir, basically, that's my "ideal" state of affairs, too. A plugin, as a package of functionality, would have full autonomy on how it will organize its code (and data, too, like CSS and images, but I think that's also the subject of Reimar's remark below) - except for some basic convention to identify an "entry point" for the plugin loader. I just thought that it might be easier to just switch nesting levels first, so that existing code for folder-based rules can be reused.
Also, it may be worthwhile to think about allowing for "registration" based plugin binding, like with WordPress, as an alternative to the declarative, convention based strategy as it is now. With the first, all plugin types have a unified entry point only (which may even be module-level code executed at import time, or some function with a predefined name and signature), and would actively "register" their "stuff" (macros, parsers, actions etc.) by means of an API. With the second, a plugin only needs to declare stuff, which is "auto-magically" recognized by the core. Naturally, much more detailed conventions are required for the latter case. Personally I do prefer the declarative approach, but the other one may be simpler and more flexible. As I said, this is just a thought that might be worthwhile considering. -- -- ZoranIsailovski 2009-03-19 02:26:17
I think about to move the instance plugins in different dirs instead of adding plugins several times to wiki instances. Since 1.8 we can have plugins in different dirs. e.g. plugin_dirs = ['/some/where/extensions/data/plugin']. That can also be plugin_dirs = ['/srv/share/moin/extensions/arnica', '/srv/share/moin/extensions/mathtran', '/srv/share/moin/extensions/slideshow']. That means you have only to configure the plugin_dirs var to get acces to the plugins you want. The plugins are separated from each other which makes it much easier to handle the extension repo plugins and may be other 3rd party plugins. The arnica css can be added for example to /srv/share/moin/extensions/arnica/htdocs. We have to add an arnica Alias to the server configurations pointing to that dir. I think that makes it much easier for me to make a release of a plugin of that repo. And it looks to me that this becomes easy to describe. For arnica it is something similiar to: unpack arnica on /srv/share/moin/extensions/ set an Alias add to wikiconfig.py the plugin_dirs var and the usual html_head line. -- ReimarBauer 2009-03-18 22:52:15
Reimar, I definitely support the idea to have the option to separate plugins from wiki instances. ( I've been missing such a feature painfully when I was maintaining a not-so-small number of intranet wikis. ) But plugins specifc to a wiki instance need to be still supported (and should take precedence over global user plugins, which should take precedence over core plugins, I'd say). I do not quite grasp what exactly you mean by "The plugins are separated from each other".
Since a year we have an open development repository for 3rd party plugins http://hg.moinmo.in/moin/extensions/. If we can convince more authors to use that repo for their third party plugins we all benefits for the developing part. Currently only moin devs have their stuff at that place and we use the same rules as for moin itselfs. Currently you can clone that dir and with one var in wikiconfig.py every plugin is enabled. That can be a problem if the plugin is not refactored or carefully reviewed or compatible to the moin version you use. While we use actions_excluded to disable an action plugin I think we also should have the possibility to allow_extensions from that new path by giving a list. Of course they can be overwritten in the instance path. While I also can write a setup.py for a plugin of that extension repo I do want also multiple ways to use that extensions repo.
- I've been to the repo. Though it's hard to tell as there are only a couple of extensions there (or am I missing on something badly?), it seems to me it's the same old folder structure.
Example: There is data/plugin/macro/arnica.py, and data/plugin/parser/text_x_arnica.py, which both seem to belong to the same functional unit, "arnica". But then there's also data/plugin/parser/inline_latex.py, which seemingly does not belong to "arnica". As it is now, they are not separated. My proposal was to do something like data/plugin/arnica that may contain the subfolders parser, macro, action, but also htdocs, and whatever else is needed for the plugin to operate in the context of moin. One step further may be to remove the requirement of having special subfolders for each plugin type, but let the plugin "main" file (according to, say, a file naming convention) announce it's parsers, macros and other components using an API. Are you d'accord?Do you agree? -- -- ZoranIsailovski 2009-03-19 18:21:13
I was telling what I want to do to discuss it. Principle I am fine with that for the developing part in the repo but Radomir is right about to much alias definitions. I am not sure if you know that we have moved htdocs in 1.9 to MoinMoin/web/static/htdocs because of the wsgi refactoring. So there must be no easy access given for a wiki admin to that dir. Not every plugin will need files in htdocs but some do. I don't feel that we are close to a good solution by now from the perspective of a wiki admin without root rights. While wsgi makes it such easy to have wiki admins which aren't root users. (MoinMoinBugs/1.9PackageInstaller)
I disagree, though, with a strategy that forces one to modify non-plugin files in order to make the plugin available. Plugins need to be auto-detected, and any configuration tweaks and hooks needed by the plugin, need to be performed from within that plugin. Any central "authority" whatsoever would contradict the plugin "philosophy" as such. A plugin should become immediately available after unpacking it to a predefined location (like /srv/share/moin/extensions/). Even a server restart would be overkill. -- ZoranIsailovski 2009-03-19 02:12:33
Of course, having the data files and the code of plugins both reside together and be automatically detected is the ideal way in the perfect world. Unfortunately, that's not possible with the current state of affairs and the way web servers work. The plugin code has to be accessible for the moinmoin python process running on the server, and it is indeed relatively easy to write code that would scan the directories provided and import apropriate code as needed. The data files, on the other hand, are needed by the html and javascript running in the user's browser. To be available to the browser, they need to be served by the web server running MoinMoin (or any other, in fact). We can scan the plugin directories and serve the needed data files when MoinMoin is being run standalone, with its own built-in web server, but that's really only suitable for personal wikis. Public wikis will usually need some setup to tell the web server where the static data files are and how to serve them. Obviously, we can't configure the web server for the user. It seems wise to keep the required setup of web server at minimum, though, and this is achieved by only having one htdocs directory with all the static files, so that the user doesn't have to configure his web server for each single plugin he adds separately. We can't keep the plugins code in that directory, because they may contain passwords and other sensitive data, and really shouldn't be accessible to the world. -- RadomirDopieralski
Radomir, I see you point, but you are making it sound as if it was a technological restriction outside the scope of moin, as if they were related to web server technology only ("the way web servers work"). If that was true, the same restriction would apply to any other web application. Hence there should be no such plugin solution at all out there. But there is. I mentioned WordPress and Drupal before, and there are more (Habari and Nucleus are 2 that come to mind). They all work the desired way, allowing for plugins to have all their data and code deployed together.
Of course, there are differences: My "examples" are all PHP apps, and most of them require Apache as server (since they are building heavily on .htaccess). I can't tell how much of a facilitation this means, but I know that their plugin concept, and the easiness with which you can deploy and install a plugin, is a major factor to both, the many plugin developers, and the many users they have. Most WordPress users, and plugin developers, do not have root access. If they were excluded from installing/developing plugins, WordPress would not be where it is now - the most used blogging web app.
But easy, self-enclosed deployment enables other attractive features, like automatic plugin updates and installs (something that WordPress has introduced with the newest version, for example, but which was provided by a plugin long before). This is boosting their user and developer base even further.
Easy, self-enclosed plugin deployment is not only technological sugar, it is a "marketing" factor to attract users and developers. Whichever way you look at it, it's the right way to go. While I'm not sure how a good solution for moin may look like, I do think it deserves to go the right way. -- ZoranIsailovski 2009-03-29 12:57:30
There is no question that we want to make it easy, otherwise we wouldn't even bother thinking about it. There are generally two ways in which PHP apps do it:
Serve the static files by itself, by reading them from disk and streaming to the web server. This is slow and wastes CPU, but gives you full control and flexibility. Apache and some other web servers even have mechanisms to speed it up, by just telling the web server which file to use, and leave the reading and serving up to it. We are planning to do this in MoinMoin as a fallback, for when the URL prefix is not configured, so that you can get //something// working fast, and maybe improve it later if it's necessary.
- Mix the data files with the code, by putting both together in the same directory subtree. This may work because .php files are by default executable by the web server, so even if you attempt to access them directly, you get the execution results, and not the actual content with passwords and private user data. It still poses a security threat in situations when PHP execution may get temporarily disabled on your web server. It also complicates the setups that require URL rewriting or don't execute PHP files directly.
All in all, 1. is already being worked on, but can't replace serving static files, 2. is out of question for Python. We've been looking around for other options (symlinks, redirects) but none are flexible enough and cross-platform at the same time to implement them and not break existing MoinMoin instances. We would very much like to hear any (even crazy) new ideas.
By the way, MoinMoin can be installed without root permissions pretty easily as cgi, or even as a wsgi app, if the host has it enabled. All you have to do is copying the htdocs directory somewhere, and setting the URL prefix to that location.
Maybe it's good to list the possible methods of making plugins be able to add their own static files:
Serve the static files by MoinMoin. Advantage: you can do anything with them, put them anywhere you want, even in a database backend. Disadvantage: slow, high server load, non-portable speedup hacks.
- Put the files together with their plugins in subdirectories of one big directory that is accessible through http. Advantages: fast access, easy install. Disadvantages: the directory must be set up to be world-accessible, security concerns -- especially with auth plugins.
Let MoinMoin make symlinks to from the files in plugin directories to the world-accessible directory. Advantages: fast, no security risk. Disadvantages: web server must follow symlinks, directory needs to be set up to be world-accessible, Microsoft Windows doesn't support symlinks, htdocs must be writable by moin.
Let MoinMoin copy the files to the htdocs directory. Advantages: works on windows. Disadvantages: duplicated size, changes don't appear immediately when upgrading.
- Radomir, thanks for your thorough response. I don't quite agree with some of the conclusions though, especially "2. is out of question for Python". Why? From what I know about web servers, there's no magic behind PHP being "by default executable". It's only server configuration. It just happens to be turned on by default on Apache. I'd say there's no real difference between python and PHP regarding the web server, neither by execution, nor by security matters (cgi and mod_python would be possible examples).
Also, it is a misconception that Windows does not support symlinks. It does, at least for directories. They call them "reparse points", and I happen to use them excessively.
-- ZoranIsailovski 2009-03-31 00:17:46
I'm not an expert on this, but what exactly do you mean by "slow"? You're fine with MoinMoin delivering the wiki pages, aren't you? I think the advantages of MoinMoin serving the static files outweigh the disadvantages. Besides: What are "the static files"? Mainly style sheets and pictures, right? That's what browser caches are for afaik. What you're doing smells like premature optimization and that's not pythonic, is it?
^ I did not write this, but I agree on the cache/performance thing. -- ZoranIsailovski
So is there any light at the end of the tunnel? Right now, I don't see any decision, just opinions. Perhaps it helps if I summarized them:
- Currently, deployment of all files needed by a plugin to a single folder (or subtree), regardless of their type, is limited.
- A way to overcome these limits is to make moin serve these files.
- There are performance concerns to do so. Some think browser-based caching will compensate for this. There is no consensus on it.
Here is a question that comes to my mind:
Is it possible to have both mechanisms in place, and serve most static files from "htdocs", and some from the plugin folder (the latter being served by moin)? Is there a way to easily switch between the two? That way, a plugin can be easily installed into one folder, and the wikiadmin can decide to move some of the files to "htdocs" if he detects performance issues.
-- ZoranIsailovski 2009-04-17 14:04:18
in many case you use the "main" apache process(es) to serve the static files (or even running this on a separated system, for kind of "poor man's load balancing"). and some wsgi processes/threads are executing the moinmoin code with different user rights (for security reason). With your static files together with the program code you have in my eyes drawbacks like:
Security
I do not want that my main apache process has read right to moinmoin program codePerformance (see here)
I don't like rewrites, aliases, symlinks (not to mention .htaccess stuff)Scaling
Want to serve my static files from another server or just use some transparent proxy stuff, etc.Farmwiki
If you're running a farmwiki then you want to use the plugins from a single place, but use a single instance of this for many wikis with different themes and layouts (having css spread across many plugin directories/files would be horrible in my eyes)
-- MarcelHäfner 2009-04-17 20:07:22
I would also like to see a new, generalized plugin system, with just one MoinMoin.plugins package (instead of actions, macros, parsers, ...) and additionally, per-wiki configurable plugin directories. Moin could load all plugin modules on startup, and each plugin module registers itself with all handlers and ressources it offers (including static ressources). For the static stuff, it would be great to handle them by moin by default, but make handling by external webserver easily possible, too. So, who writes the code for that? moin 2.0 would be a good time to implement that. -- ThomasWaldmann 2009-05-10 17:40:20
I see Marcel's points, but not necessarily their justification: Security: Things should be secure enough, not necessarily as secure as possible. Why would giving read right to the main apache process reduce security to an unacceptable level (while it is "good enough" for PHP code)? If the main apache process is "insecure", wouldn't fixing that be the right solution? Performance: As with security, I only need just enough performance. I have no evidence that MoinMoin is, in terms of performance, light years ahead of PHP based CMS'es (which do allow to mix static and code files). Scaling: In the general case, the plugin system should allow for local static files, not enforce them. Hence, there should be no problem moving them to en external location. Farms: Essentially it is desirable to have a singe code base (contrast to replicating sets of plugins). Instead, instance-wise configurations should decide what plugins are active in the individual wiki instance. In my view, farms and scaling are additional arguments PRO overhauling the plugin system. I suggest we start gathering the NewPluginSystemRequirements (some of them are here already, just scattered throughout the discussiuon). -- ZoranIsailovski 2009-05-20 19:24:28
May be we could also find a way to overwrite default values defined for the argumentparser by a wikiconfig setting.
Currently, MoinMoin already can serve the static files through it, and will do that if you don't setup the Alias directive. I wonder if we could do something similar for plugin's static files. Several variations are possible:
You can make MoinMoin serve all plugin files, and require the admin to copy them to the htdocs directory if he wants to use the Alias directive. The moin's commandline script could have an option for doing it. It could even have a command for installing a plugin from archive!
As before, but have separate directory for htdocs, and separate for plugin files -- and two Alias directives. This way you can serve through alias most static files without too much headaches, and also have an option to serve them all with some extra work.
Separate Alias for every plugin... somehow I don't like it.
-- RadomirDopieralski 2010-02-14 22:16:53
About overhaul of the whole plugin system, I had recently an opportunity to work with Trac, and I think its plugin system is something we could learn a lot from. They use the hook mechanisms provided by setuptools. The plugins are standard python packages, in form of eggs -- the archive may actually also contain data files. You can install the plugins easily with easy_install, and just enable in the configuration file. -- RadomirDopieralski 2010-02-14 22:26:19
See also FeatureRequests/EggFilePluginSystem